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; } }