remove unnecessary existence checks.
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / FileLock.java
1 package uk.ac.vamsas.client.simpleclient;
2
3 import java.io.BufferedOutputStream;
4 import java.io.File;
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;
12 /**
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.
15  * Method:
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.
18  * @author JimP
19  *
20  */
21 public class FileLock extends Lock {
22   private File _lock = null;
23   protected static String _LockSuffix="lck";
24   private NativeLock advisory=null;
25   /**
26    * ensure that the _lock file exists
27    * and create a lock
28    */
29   private boolean ensureLockFile(boolean block) {
30     if (_lock==null)
31       return false;
32     if (advisory!=null && advisory.isLocked())
33       return true;
34     try { 
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);
41       }
42     }
43     return advisory.isLocked();
44   }
45   /**
46    * call to clear up a filelock file after its been made
47    *
48    */
49   private void tidy() {
50     if (_lock!=null) { 
51       if ( advisory!=null) 
52         advisory.release(true);
53       advisory.target.deleteOnExit();
54       advisory=null;
55       _lock=null;
56     }
57   }
58   /**
59    * @param lockfile
60    * @param block true means thread blocks until FileLock is obtained.
61    */
62   public FileLock(File lockfile, boolean block) {
63     super(lockfile);
64     // try and get a lock.
65     try {
66       _lock = new File(lockfile.getParentFile(), lockfile.getName()+"."+_LockSuffix);
67       if (!ensureLockFile(block)) {
68         log.debug("Couldn't get lock on "+_lock);
69         tidy();
70         return;
71       }
72       // create target file ready to be written to if necessary.
73       if (!lockfile.exists())
74         if (!lockfile.createNewFile()) {
75           log.warn("Failed to create locked file "+lockfile);
76           return;
77         }
78       //openRaFile();
79     } catch (FileNotFoundException e) {
80       //
81       log.debug("FileLock failed with target="+lockfile+" and lockfile suffix of "+_LockSuffix);
82       //log.error("Error! Couldn't create a lockfile at "
83       //  + lockfile.getAbsolutePath(), e);
84     } catch (IOException e) {
85       log.error("Error! Problems with IO when creating a lock on "
86           + lockfile.getAbsolutePath(),e);
87     }
88   }
89   
90   private boolean openRaFile() throws IOException {
91     if (target==null)
92       return false;
93     if (advisory==null || !advisory.isLocked())
94       return false;
95     if (rafile==null || rafile.getFD()==null || !rafile.getFD().valid()) {
96       rafile=new RandomAccessFile(target,"rw");
97     } else {
98       if (log.isDebugEnabled())
99         log.debug("Reusing existing RandomAccessFile on "+target);
100     }
101     return (rafile.getChannel()!=null) && rafile.getChannel().isOpen();
102   }
103   
104   public boolean isLocked() {
105     if (advisory != null) {
106       if (advisory.isLocked())
107         return true;
108       advisory=null;
109       if (log.isDebugEnabled())
110         log.debug("Lockfile "+_lock+" unexpectedly deleted ?");
111     }
112     return false;
113   }
114   
115   public void release() {
116     release(true);
117   }
118   
119   public void release(boolean closeChannel) {
120     if (!isLocked())
121       return;
122     if (rafile!=null) {
123       if (closeChannel) {
124         try {
125           rafile.close();
126         } catch (Exception e) {
127           log.debug("Unexpected exception whilst closing RandomAccessFile on "+target, e);
128         }
129         rafile=null; // do not hold reference to rafile anymore either
130       }
131       if (log.isDebugEnabled())
132         log.debug("Releasing advisory lock on "+target);
133       // TODO: LATER: verify this change in closeChannel semantics really is correct - ArchiveClient works correctly
134     }
135     tidy();
136   }
137   
138   public FileInputStream getFileInputStream(boolean atStart) throws IOException {
139     if (!isLocked()) {
140       log.debug("Don't hold lock on "+target+" to get locked FileInputStream.");
141       return null;
142     }
143     openRaFile();
144     if (atStart)
145       rafile.seek(0);
146     return new FileInputStream(rafile.getFD());
147   }
148   
149   
150   public FileOutputStream getFileOutputStream(boolean clear) throws IOException {
151     if (!isLocked()) {
152       log.debug("Don't hold lock on "+target+" to get locked FileOutputStream.");
153       return null;
154     }
155     openRaFile();
156     if (clear) {
157       rafile.seek(0);
158       rafile.setLength(0);
159     } else
160       rafile.seek(rafile.length());
161     return new LockedFileOutputStream(rafile.getFD());
162   }
163   
164   
165   public BufferedOutputStream getBufferedOutputStream(boolean clear) throws IOException {
166     log.debug("Getting BufferedOutputStream (clear="+clear+")");
167     FileOutputStream fos = getFileOutputStream(clear);
168     if (fos!=null)
169       return new BufferedOutputStream(fos);
170     return null;
171   }
172   
173   /* (non-Javadoc)
174    * @see uk.ac.vamsas.client.simpleclient.Lock#getLength()
175    */
176   public long length() {
177     if (isLocked()) {
178       if (!target.exists()) {
179         try {
180           target.createNewFile();
181         } catch (Exception e) {
182           log.error("Invalid lock:Failed to create target file "+target);
183           tidy();
184           return -1;
185         }
186         return 0;
187       }
188       return target.length();
189     }
190     return -1;
191   }
192   protected void finalize() throws Throwable {
193     release(true); // we explicitly lose the lock here.
194     super.finalize();
195   }
196   public RandomAccessFile getRaFile() throws IOException {
197     if (isLocked() && openRaFile()) {
198       return rafile;
199     }
200     log.debug("Failed to getRaFile on target "+target);
201     return null;
202   }
203   public FileChannel getRaChannel() throws IOException {
204     if (isLocked() && openRaFile()) {
205       return rafile.getChannel();
206     }
207     log.debug("Failed to getRaChannel on target "+target);
208     return null;
209   }
210 }