1 package uk.ac.vamsas.client.simpleclient;
3 import java.io.BufferedOutputStream;
5 import java.io.FileInputStream;
6 import java.io.FileNotFoundException;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.io.RandomAccessFile;
10 import java.nio.channels.FileChannel;
11 import java.nio.channels.ReadableByteChannel;
13 * File based Locking mechanism to get around some bizarre limitations of JarEntry seeking.
14 * Abstract locks have a target file, to which access is controlled when a lock is held. Native locks on WindowsXP seem to conflict with Jar seek operations, so a file lock creates an advisory lock.
16 * A lock file is created, if it doesn't already exist - the naming convention is TargetFile+suffixSeparator+_LockSuffix.
17 * A lock is obtained by locking the lock file with a native lock. The NativeLock is used for this.
21 public class FileLock extends Lock {
22 private File _lock = null;
23 protected static String _LockSuffix="lck";
24 private NativeLock advisory=null;
26 * ensure that the _lock file exists
29 private boolean ensureLockFile(boolean block) {
32 if (advisory!=null && advisory.isLocked())
35 advisory=new NativeLock(_lock, block);
36 } catch (Exception e) {
37 if (!_lock.exists()) {
38 // advisory cannot be created. this is serious.
39 log.fatal("Failed to create advisory lock file "+_lock,e);
40 throw new Error("Failed to create advisory lock file "+_lock);
43 return (advisory!=null) && advisory.isLocked();
46 * call to clear up a filelock file after its been made
51 if ( advisory!=null) {
52 // TODO: fix occasional exceptions raised here (usually on JVM shutdown)
53 if (advisory.target!=null) {
54 advisory.target.deleteOnExit(); // release will null the target
56 advisory.release(true);
64 * @param block true means thread blocks until FileLock is obtained.
66 public FileLock(File lockfile, boolean block) {
68 // try and get a lock.
70 _lock = make_lockForTarget(lockfile);
71 if (!ensureLockFile(block)) {
72 log.debug("Couldn't get lock on "+_lock);
76 // create target file ready to be written to if necessary.
77 if (!lockfile.exists())
78 if (!lockfile.createNewFile()) {
79 log.warn("Failed to create locked file "+lockfile);
83 } catch (FileNotFoundException e) {
85 log.debug("FileLock failed with target="+lockfile+" and lockfile suffix of "+_LockSuffix);
86 //log.error("Error! Couldn't create a lockfile at "
87 // + lockfile.getAbsolutePath(), e);
88 } catch (IOException e) {
89 log.error("Error! Problems with IO when creating a lock on "
90 + lockfile.getAbsolutePath(),e);
95 * @param lockfile target of lock
96 * @return file object that will be locked for lockfile
98 private File make_lockForTarget(File lockfile) {
99 return new File(lockfile.getParentFile(), lockfile.getName()+"."+_LockSuffix);
101 private boolean openRaFile() throws IOException {
104 if (advisory==null || !advisory.isLocked())
106 if (rafile==null || rafile.getFD()==null || !rafile.getFD().valid()) {
107 rafile=new RandomAccessFile(target,"rw");
109 if (log.isDebugEnabled())
110 log.debug("Reusing existing RandomAccessFile on "+target);
112 return (rafile.getChannel()!=null) && rafile.getChannel().isOpen();
115 public boolean isLocked() {
116 if (advisory != null) {
117 if (advisory.isLocked())
120 if (log.isDebugEnabled())
121 log.debug("Lockfile "+_lock+" unexpectedly deleted ?");
126 public void release() {
130 public void release(boolean closeChannel) {
137 } catch (Exception e) {
138 log.debug("Unexpected exception whilst closing RandomAccessFile on "+target, e);
140 rafile=null; // do not hold reference to rafile anymore either
142 if (log.isDebugEnabled())
143 log.debug("Releasing advisory lock on "+target);
144 // TODO: LATER: verify this change in closeChannel semantics really is correct - ArchiveClient works correctly
149 public FileInputStream getFileInputStream(boolean atStart) throws IOException {
151 log.debug("Don't hold lock on "+target+" to get locked FileInputStream.");
157 return new FileInputStream(rafile.getFD());
161 public FileOutputStream getFileOutputStream(boolean clear) throws IOException {
163 log.debug("Don't hold lock on "+target+" to get locked FileOutputStream.");
171 rafile.seek(rafile.length());
172 return new LockedFileOutputStream(rafile.getFD());
176 public BufferedOutputStream getBufferedOutputStream(boolean clear) throws IOException {
177 log.debug("Getting BufferedOutputStream (clear="+clear+")");
178 FileOutputStream fos = getFileOutputStream(clear);
180 return new BufferedOutputStream(fos);
185 * @see uk.ac.vamsas.client.simpleclient.Lock#getLength()
187 public long length() {
189 if (!target.exists()) {
191 target.createNewFile();
192 } catch (Exception e) {
193 log.error("Invalid lock:Failed to create target file "+target);
199 return target.length();
203 protected void finalize() throws Throwable {
204 release(true); // we explicitly lose the lock here.
207 public RandomAccessFile getRaFile() throws IOException {
208 if (isLocked() && openRaFile()) {
211 log.debug("Failed to getRaFile on target "+target);
214 public FileChannel getRaChannel() throws IOException {
215 if (isLocked() && openRaFile()) {
216 return rafile.getChannel();
218 log.debug("Failed to getRaChannel on target "+target);
221 public boolean isTargetLockFile(File afile) {
224 if (target.equals(afile) || make_lockForTarget(target).equals(afile))