tags:

views:

846

answers:

3

This is perhaps similar to previous posts, but I want to be specific about the use of locking on a network, rather than locally. I want to write a file to a shared location, so it may well go on a network (certainly a Windows network, maybe Mac). I want to prevent other people from reading any part of this file whilst it it being written. This will not be a highly concurrent process, and the files will be typically less than 10MB.

I've read the FileLock documentation and File documentation and am left somewhat confused, as to what is safe and what is not. I want to lock the entire file, rather than portions of it.

Can I use FileChannel.tryLock(), and it is safe on a network, or does it depend on the type of network? Will it work on a standard Windows network (if there is such a thing).

If this does not work, is the best thing to create a zero byte file or directory as a lock file, and then write out the main file. Why does that File.createNewFile() documentation say don't use this for file locking? I appreciate this is subject to race conditions, and is not ideal.

+2  A: 

This can't be reliably done on a network file system. As long as your application is the only application that accesses the file, it's best to implement some kind of cooperative locking process (perhaps writing a lock file to the network filesystem when you open the file). The reason that is not recommended, however, is that if your process crashes or the network goes down or any other number of issues happen, your application gets into a nasty, dirty state.

+1  A: 

You can have a empty file which is lying on the server you want to write to.

When you want to write to the server you can catch the token. Only when you have the token you should write to any file which is lying on the server.

When you are ready with you file operations or an exception was thrown you have to release the token.

The helper class can look like

private FileLock lock;

private File tokenFile;

public SLTokenLock(String serverDirectory) {
    String tokenFilePath = serverDirectory + File.separator + TOKEN_FILE;
    tokenFile = new File(tokenFilePath);
}

public void catchCommitToken() throws TokenException {
    RandomAccessFile raf;
    try {
        raf = new RandomAccessFile(tokenFile, "rw"); //$NON-NLS-1$
        FileChannel channel = raf.getChannel();
        lock = channel.tryLock();

        if (lock == null) {
            throw new TokenException(CANT_CATCH_TOKEN);
        }
    } catch (Exception e) {
        throw new TokenException(CANT_CATCH_TOKEN, e);
    }
}

public void releaseCommitToken() throws TokenException {
    try {
        if (lock != null && lock.isValid()) {
            lock.release();
        }
    } catch (Exception e) {
        throw new TokenException(CANT_RELEASE_TOKEN, e);
    }
}

Your operations then should look like

try {
        token.catchCommitToken();

        // WRITE or READ to files inside the directory
    } finally {
        token.releaseCommitToken();
    }
Markus Lausberg
+1  A: 

Rather than implementing a locking strategy which will, in all likelihood, rely on readers to adhere to your convention but will not force them to, perhaps you can write the file out to a hidden or obscurely named file where it will be effectively invisible to readers. When the write operation is complete, rename the file to the expected public name.

The downside is that hiding and/or renaming without additional IO may require you to use native OS commands, but the procedure to do so should be fairly simple and deterministic.

Nicholas