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