simplified and bugfixed occasional lock contention.
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / NativeLock.java
1 package uk.ac.vamsas.client.simpleclient;
2
3 import java.io.BufferedInputStream;
4 import java.io.BufferedOutputStream;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileNotFoundException;
8 import java.io.FileOutputStream;
9 import java.io.IOException;
10 import java.io.OutputStream;
11 import java.io.RandomAccessFile;
12 import java.nio.channels.FileChannel;
13 import java.nio.channels.FileLock;
14 import java.nio.channels.ReadableByteChannel;
15
16 /**
17  * @author JimP
18  *
19  */
20 public class NativeLock extends Lock {
21
22   protected FileLock lock = null;
23
24   /**
25    * @param lockfile
26    * @param block true means thread will block until a lock is obtained.
27    */
28   public NativeLock(File lockfile, boolean block) {
29     super(lockfile);
30     // try and get a lock.
31     lock = null;
32     
33     try {
34       /*if (!lockfile.createNewFile()) {
35           log.warn("Failed to create locked file "+lockfile);
36           return;
37         }
38       */
39       rafile=new RandomAccessFile(lockfile,"rw");
40       if (block)
41         lock = rafile.getChannel().lock();
42       else
43         lock = rafile.getChannel().tryLock();
44       if (lock==null || !lock.isValid()) {
45         // failed to get lock. Close the file channel
46         log.debug("failed to get lock for "+lockfile);
47         rafile.getChannel().close();
48         lock=null;
49       }
50     } catch (FileNotFoundException e) {
51       //
52       log.debug("Lock failed - normal behaviour for windows locking.");
53       //log.error("Error! Couldn't create a lockfile at "
54       //  + lockfile.getAbsolutePath(), e);
55     } catch (IOException e) {
56       log.error("Error! Problems with IO when creating a lock on "
57           + lockfile.getAbsolutePath(),e);
58     } // catch (Exception )
59   }
60
61   public boolean isLocked() {
62     if (lock != null && lock.isValid()) {
63       return true;
64     }
65     return false;
66   }
67
68   public void release() {
69     release(true);
70   }
71
72   public void release(boolean closeChannel) {
73     if (lock==null)
74       return;
75     try {
76       // channel.close should be called before release() for rigourous locking.
77       if (lock.isValid() && rafile!=null && rafile.getFD().valid() && rafile.getChannel()!=null) {
78         if (closeChannel && rafile.getChannel().isOpen()) {
79             rafile.close();
80             rafile=null; 
81         }
82         if (lock!=null && lock.isValid())
83           lock.release();
84         
85       }
86     } catch (IOException e) {
87       log.warn("Whilst releasing lock",e);
88     }
89     lock=null;
90   }
91
92   /**
93    * gets Locked Stream for reading from
94    * @param atStart true to start reading at beginning of file.
95    * @return null if file not locked
96    * @throws IOException
97    */
98   public FileInputStream getFileInputStream(boolean atStart) throws IOException {
99     if (!isLocked())
100       return null;
101     if (atStart)
102       rafile.seek(0);
103     return new FileInputStream(rafile.getFD());
104   }
105
106   /**
107    * gets Locked stream to write to
108    * FileInput always starts at the *end* of the file (after any truncation)
109    * @param clear true means file will be cleared to zero length
110    * @return null if file is not locked
111    * @throws IOException
112    */
113   public FileOutputStream getFileOutputStream(boolean clear) throws IOException {
114     if (!isLocked())
115       return null;
116     if (clear) {
117       rafile.seek(0);
118       rafile.setLength(0);
119     } else
120       rafile.seek(rafile.length());
121     return new LockedFileOutputStream(rafile.getFD());
122   }
123
124   /**
125    * return buffered output stream to locked file.
126    * @param clear - true means file is truncated to 0 length before writing 
127    * @return
128    */
129   public BufferedOutputStream getBufferedOutputStream(boolean clear) throws IOException {
130     OutputStream fos = getFileOutputStream(clear);
131     if (fos!=null)
132       return new BufferedOutputStream(fos);
133     return null;
134   }
135   /**
136    * @see uk.ac.vamsas.client.simpleclient.Lock#finalize()
137    */
138   protected void finalize() throws Throwable {
139     release(true); // we explicitly lose the lock here.
140     // log.debug("lock closing through garbage collection ?");
141     super.finalize();
142   }
143
144   /* (non-Javadoc)
145    * @see uk.ac.vamsas.client.simpleclient.Lock#getLength()
146    */
147   public long length() {
148     if (isLocked()){
149       try {
150         return rafile.length();
151       } catch (Exception e) {
152         log.debug("getLength exception:",e);
153       }
154     }
155     return -1;
156   }
157
158   public RandomAccessFile getRaFile() throws IOException {
159     if (isLocked())
160       return rafile;
161     else
162       log.debug("Failed to getRaFile on "+target);
163     return null;
164   }  
165   
166   public FileChannel getRaChannel() throws IOException {
167     if (isLocked())
168       return rafile.getChannel();
169     else
170       log.debug("Failed to getRaChannel on "+target);
171     return null;
172   }
173
174 }