package org.prevayler.foundation;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
/**
* A utility class for locking files that works for multiple threads. Naive file locking behaves unpredictably if some
* thread has already acquired a lock on a particular file; attempting another lock may succeed, may fail gracefully, or
* may fail with an exception. Therefore this class is entirely static and synchronized to provide coordination between
* all threads in a single JVM.
*/
public class FileLocker {
private File _file;
private RandomAccessFile _stream;
private FileLock _lock;
/**
* Attempt to acquire an exclusive lock on the given file. The file, and any parent directories, are created if they do
* not exist; but the contents of the file are not harmed if it does exist. If the program needs to actually access the
* file, it must use {@link #getStream()} exclusively <b>and not close it</b> since that may release the lock.
* @throws IOException If the lock cannot be acquired.
* @throws java.nio.channels.OverlappingFileLockExceptionIf the JVM detects that another thread already holds a lock on the file. This should never
* happen if this method is used exclusively for file locking.
*/
public FileLocker( File file) throws IOException {
_file=file.getCanonicalFile();
if ("locked".equals(System.setProperty(propertyName(),"locked"))) {
throw new IOException("Already locked internally");
}
_file.getParentFile().mkdirs();
_file.createNewFile();
_stream=new RandomAccessFile(_file,"rw");
try {
_lock=_stream.getChannel().tryLock();
}
catch ( IOException e) {
_stream.close();
throw e;
}
if (_lock == null) {
_stream.close();
System.setProperty(propertyName(),"");
throw new IOException("Already locked externally");
}
}
private String propertyName() throws IOException {
return FileLocker.class.getName() + "-" + _file.getCanonicalPath();
}
/**
* Release the file lock and close the associated stream.
*/
public void release() throws IOException {
try {
try {
_lock.release();
}
finally {
_stream.close();
}
}
finally {
System.setProperty(propertyName(),"");
}
}
public RandomAccessFile getStream(){
return _stream;
}
}