fixed leaky lock (answers todo about verifying that channel.close() should be called...
[vamsas.git] / src / org / vamsas / client / simpleclient / Lock.java
1 package org.vamsas.client.simpleclient;
2
3 import java.io.BufferedInputStream;
4 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.FileLock;
11
12 import org.apache.commons.logging.LogFactory;
13
14 /**
15  * transient object representing a file lock
16  * This lock should hold for all processes interacting in a session.
17  * TODO: currently implemented for local filesystem style locking - need a fallback mechanism for systems without file locks.
18  * @author jimp
19  * 
20  */
21
22 public class Lock {
23   org.apache.commons.logging.Log log = LogFactory.getLog(Lock.class);
24   FileLock lock = null;
25   RandomAccessFile rafile=null;
26   /**
27    * creates a valid Lock (test with <method>isLocked</method>)
28    * if a lock could be obtained for <param>lockfile</param>
29    * @param lockfile
30    */
31   public Lock(java.io.File lockfile) {
32     // try and get a lock.
33     lock = null;
34     
35     try {
36       if (!lockfile.exists())
37         if (!lockfile.createNewFile()) {
38           log.warn("Failed to create locked file "+lockfile);
39           return;
40         }
41       
42       lock = (rafile=new RandomAccessFile(lockfile,"rw")).getChannel().tryLock();
43       if (lock==null || !lock.isValid()) {
44         // failed to get lock. Close the file channel
45         log.debug("failed to get lock for "+lockfile);
46         rafile.getChannel().close();
47         lock=null;
48       }
49     } catch (FileNotFoundException e) {
50       //
51       log.debug("Lock failed - normal behaviour for windows locking.");
52       //log.error("Error! Couldn't create a lockfile at "
53       //  + lockfile.getAbsolutePath(), e);
54     } catch (IOException e) {
55       log.error("Error! Problems with IO when creating a lock on "
56           + lockfile.getAbsolutePath(),e);
57     }
58   }
59   
60   boolean isLocked() {
61     if (lock != null && lock.isValid()) {
62       return true;
63     }
64     return false;
65   }
66   public void release() {
67     release(true);
68   }
69   public void release(boolean closeChannel) {
70     try {
71       // channel.close should be called before release() for rigourous locking.
72       if (rafile!=null && rafile.getChannel()!=null) {
73         if (rafile.getChannel().isOpen()) {
74           if (closeChannel && rafile.getChannel().isOpen())
75             rafile.getChannel().close();
76           if (lock!=null && lock.isValid())
77             lock.release();
78         }
79       }
80     } catch (IOException e) {
81       log.warn("Whilst releasing lock",e);
82     }
83     lock=null;
84     rafile=null; 
85   }
86   
87   /**
88    * gets Locked Stream for reading from
89    * @param atStart true to start reading at beginning of file.
90    * @return null if file not locked
91    * @throws IOException
92    */
93   public FileInputStream getFileInputStream(boolean atStart) throws IOException {
94     if (!isLocked())
95       return null;
96     if (atStart)
97       rafile.seek(0);
98     return new FileInputStream(rafile.getFD());
99   }
100   /**
101    * gets Locked stream to write to
102    * FileInput always starts at the *end* of the file (after any truncation)
103    * @param clear true means file will be cleared to zero length
104    * @return null if file is not locked
105    * @throws IOException
106    */
107   public FileOutputStream getFileOutputStream(boolean clear) throws IOException {
108     if (!isLocked())
109       return null;
110     if (clear)
111       rafile.setLength(0);
112     rafile.seek(rafile.length());
113     return new LockedFileOutputStream(rafile.getFD());
114   }
115   /**
116    * return buffered output stream to locked file.
117    * @param clear - true means file is truncated to 0 length before writing 
118    * @return
119    */
120   public BufferedOutputStream getBufferedOutputStream(boolean clear) throws IOException {
121     FileOutputStream fos = getFileOutputStream(clear);
122     if (fos!=null)
123       return new BufferedOutputStream(fos);
124     return null;
125   }
126
127   /**
128    * return buffered input stream for locked file.
129    * @param atStart - true means read from begining of file
130    * @return null if file is not locked.
131    */
132   public BufferedInputStream getBufferedInputStream(boolean atStart) throws IOException {
133     FileInputStream fis = getFileInputStream(atStart);
134     if (fis!=null)
135       return new BufferedInputStream(fis);
136     return null;
137   }
138   /* Explicitly release lock (probably don't need to do this!)
139    * @see java.lang.Object#finalize()
140    */
141   protected void finalize() throws Throwable {
142     release(true); // we explicitly lose the lock here.
143     // log.debug("lock closing through garbage collection ?");
144     super.finalize();
145   }
146   
147 }