Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Enable Cross-platform File Locking with a Lock Server  : Page 3

A custom shared lock server can overcome file-locking differences between operating systems or Java implementations.


WEBINAR: On-Demand

Unleash Your DevOps Strategy by Synchronizing Application and Database Changes REGISTER >

The first thing the client has to do is create a RemoteLockClient, which connects to the server:

public RemoteLockClient( String hostname, int port ) { this.hostname = hostname; this.port = port; try { socket = new Socket( hostname, port ); // ... } catch( IOException ie ) { throw new RuntimeException( "Cannot connect to lock server "+ hostname+":"+port ); } }

The boldfaced line is where the action is—where the RemoteLockClient connects to the lock server.

Now it's time to acquire a lock. You'll notice that java.nio.channels.FileLock has four different locking methods, divided into two pairs of two. In the pair called lock(), one method locks a particular region of a file, and the other one locks the entire file. The second pair, called tryLock(), does the same but in a non-blocking fashion. If the lock cannot be acquired, instead of blocking these two methods return null.

Likewise, RemoteLockClient has four methods. However, each of these in turns calls a single, protected method called doLock(). Figure 4 shows the relationship between these methods.

Figure 4: The 'World of Sound' Sample Store

The doLock() method marshals its arguments into a lock request and sends them down the wire to the lock server:

dout.writeInt( ACQUIRE_LOCK ); dout.writeUTF( filename ); dout.writeLong( position ); dout.writeLong( size ); dout.writeBoolean( shared ); dout.writeBoolean( doTry );

After this information is sent, the server responds with a token that distinguishes this lock from other locks:

int token = din.readInt();

This token is associated with the FileLock object in a Map called tokenToLock. This token is also stored inside the RemoteLock object returned by doLock(). Later, when the RemoteLock's release() method is called, this token is sent back to the server inside a release request:

dout.writeInt( RELEASE_LOCK ); dout.writeInt( token );

The server responds with a code, which is either RELEASE_OK or RELEASE_ERROR:

int response = din.readInt();

If the response is RELEASE_ERROR, the client throws a RemoteLockException.

Meanwhile, on the server side, the server accepts new connections in a background thread. It hands each incoming connection off to a ClientHandler object, which is static to the LockServer class. Each ClientHandler also spawns a background thread, which responds to requests from the client.

First, the ClientHandler finds out whether the incoming request is a lock request or a release request:

int code = din.readInt(); if (code==ACQUIRE_LOCK) { // ... } else { // ... }

If it's a lock request, you read the arguments and attempt to acquire the lock. Note that how you do this depends on the values of two flags: shared and doTry. Shared specifies whether the lock should be shared or not, while doTry specifies whether you should block or return null in the event that you cannot immediately acquire the lock:

RandomAccessFile raf = new RandomAccessFile( filename, shared ? "r" : "rw" ); FileChannel fc = raf.getChannel(); FileLock lock = null; if (doTry) { lock = fc.tryLock( position, size, shared ); } else { lock = fc.lock( position, size, shared ); }

Note that you have to open the file either read-only or read/write, depending on whether the lock is exclusive or shared (respectively). If you do acquire the lock, you must generate a token for it and send that token back to the client:

token = getSerial(); // ... dout.writeInt( token );

If on the other hand the request is a release request, you get the FileLock object from the tokenToLock map and release the lock. You also send back a value indicating whether the release action succeeded or not:

int token = din.readInt(); FileLock lock = (FileLock)tokenToLock.get( new Integer( token ) ); lock.release(); // ok is set to false if an error occurs dout.writeInt( ok ? RELEASE_OK : RELEASE_ERROR );

See the full source listings for more details.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date