aa5e779cd5a5580226e8ab102819251bb1a7e321
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / SessionFile.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.RandomAccessFile;
11 import java.nio.channels.ReadableByteChannel;
12
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15
16 /**
17  * Basic methods for classes handling locked IO on files 
18  * monitored by all (simpleclient) clients in a vamsas session.
19  * @author jimp
20  *TODO: support non nio file locking capable systems
21  */
22 public class SessionFile {
23   private static Log log = LogFactory.getLog(SessionFile.class);
24   protected File sessionFile;
25   protected Lock fileLock = null;
26
27   protected SessionFile(File file) {
28     super();
29     sessionFile = file;
30   }
31
32   protected boolean lockFile(Lock extantlock) {
33     if (fileLock!=null && !fileLock.isLocked()) {
34       fileLock.release();// tidy up invalid lock
35       fileLock=null;
36     }
37     if (extantlock!=null && extantlock.isLocked())
38       fileLock=extantlock; // THIS IS BROKEN - lockFile then nulls the lock.
39     return lockFile();
40   }
41   private boolean ensureSessionFile() {
42     if (sessionFile != null) {
43       if (!sessionFile.exists()) {
44         // create new file
45         try {
46           if (!sessionFile.createNewFile()) {
47             log.error("Failed to create file prior to locking: "+sessionFile);
48             return false;
49           }
50         } catch (IOException e) {
51           log.error("Exception when trying to create file "+sessionFile, e);
52           return false;
53         }
54       }
55       return true;
56     }
57     log.error("ensureSessionFile called for non-initialised SessionFile!");
58     return false;
59   }
60   /**
61    * Get a lock for the SessionFile
62    * 
63    * @return true if lock was made
64    */
65   protected boolean lockFile() {
66     if (fileLock != null) {
67       if (fileLock.isLocked()) {
68         if (!ensureSessionFile())
69           return false;
70         return true;
71       } else {
72         // lock failed for some reason.
73         fileLock.release();
74         log.info("Unexpected session file lock failure. Trying to get it again.");
75         fileLock=null;
76       }
77     }
78     if (!ensureSessionFile())
79       return false;
80     // TODO: see if we need to loop-wait for locks or they just block until
81     // lock is made...
82     long tries=5000;
83     do {
84       tries--;
85       if (fileLock==null || !fileLock.isLocked()) {
86         //try { Thread.sleep(1); } catch (Exception e) {};
87         fileLock = LockFactory.getLock(sessionFile,true); // TODO: wait around if we can't get the lock.
88           }
89       } while (tries>0 && !fileLock.isLocked());
90     if (!fileLock.isLocked())
91       log.error("Failed to get lock for "+sessionFile);
92     // fileLock = new Lock(sessionFile);
93     return fileLock.isLocked();    
94   }
95
96   /**
97    * Explicitly release the SessionFile's lock.
98    * 
99    * @return true if lock was released.
100    */
101   protected void unlockFile() {
102     if (fileLock != null) {
103       fileLock.release();    
104       fileLock = null;
105     }
106   }
107
108   /**
109    * Makes a backup of the sessionFile.
110    * @return Backed up SessionFile or null if failed to make backup.
111    */
112   protected File backupSessionFile() {
113     return backupSessionFile(fileLock, sessionFile.getName(),".old", sessionFile.getParentFile());
114   }
115
116   protected File backupSessionFile(Lock extantLock, String backupPrefix, String backupSuffix, File backupDir) {
117     File tempfile=null;
118     if (lockFile(extantLock)) {
119       try {
120         tempfile = File.createTempFile(backupPrefix, backupSuffix, backupDir);
121         long sourceln = fileLock.length();
122         if (sourceln>0) {
123           FileOutputStream tos = new FileOutputStream(tempfile);
124           ReadableByteChannel channel;
125           channel = fileLock.getRaChannel().position(0);
126           long ntrans = 0;
127           while (ntrans<sourceln)
128           {
129             long tlen;
130             ntrans+= tlen = tos.getChannel().transferFrom(channel, ntrans,
131               sourceln);
132             if (log.isDebugEnabled())
133             {
134               log.debug
135               ("Transferred "+tlen+" out of "+sourceln+" bytes");
136             }
137           }
138           tos.close();
139           if (!channel.isOpen())
140             throw new Error("LIBRARY PORTABILITY ISSUE: "+tos.getChannel().getClass()+".transferFrom closes source channel!");
141           if (!lockFile(extantLock))
142             throw new Error("LIBRARY PORTABILITY ISSUE: Lost lock for "+sessionFile.getName()+" after backup.");
143           
144         }
145       } catch (FileNotFoundException e1) {
146         log.warn("Can't create temp file for "+sessionFile.getName(),e1);
147         tempfile=null;
148       } catch (IOException e1) {
149         log.warn("Error when copying content to temp file for "+sessionFile.getName(),e1);
150         tempfile=null;
151       }
152     }
153     return tempfile;
154   }
155   /**
156    * Replaces data in sessionFile with data from file handled by another sessionFile
157    * passes up any exceptions.
158    * @param newData source for new data
159    */
160   protected void updateFrom(Lock extantLock, SessionFile newData) throws IOException {
161     log.debug("Updating "+sessionFile.getAbsolutePath()+" from "+newData.sessionFile.getAbsolutePath());
162     if (newData==null)
163       throw new IOException("Null newData object.");
164     if (newData.sessionFile==null)
165       throw new IOException("Null SessionFile in newData.");
166       
167     if (!lockFile(extantLock))
168       throw new IOException("Failed to get write lock for "+sessionFile);
169     if (!newData.lockFile())
170       throw new IOException("Failed to get lock for updateFrom "+newData.sessionFile);
171     RandomAccessFile nrafile = newData.fileLock.getRaFile();
172     nrafile.seek(0);
173     RandomAccessFile trafile = fileLock.getRaFile();
174     /*long tries=5000;
175     while (trafile==null && --tries>0) {
176       log.debug("Lost lock on "+sessionFile+"! Re-trying for a transfer.");
177       lockFile();
178       trafile = fileLock.getRaFile();
179     }*/
180     // TODO JBPNote: attempt to ensure save really saves the VamDoc.jar file
181     trafile.seek(0);
182     trafile.getChannel().transferFrom(nrafile.getChannel(), 0, 
183         nrafile.length());
184     // JBPNote: attempt to close the streams to flush the data out
185     // trafile.close();
186     //nrafile.close();
187   }
188   /**
189    * remove all trace of the sessionFile file
190    *
191    */
192   protected void eraseExistence() {
193     unlockFile();
194     if (sessionFile!=null) {
195       sessionFile.delete();
196       sessionFile = null;
197     }
198   }
199   /* (non-Javadoc)
200    * @see uk.ac.vamsas.client.simpleclient.Lock#getBufferedInputStream(boolean)
201    */
202   public BufferedInputStream getBufferedInputStream(boolean atStart) throws IOException {
203     lockFile();
204     return fileLock.getBufferedInputStream(atStart);
205   }
206
207   /* (non-Javadoc)
208    * @see uk.ac.vamsas.client.simpleclient.Lock#getBufferedOutputStream(boolean)
209    */
210   public BufferedOutputStream getBufferedOutputStream(boolean clear) throws IOException {
211     lockFile();
212     return fileLock.getBufferedOutputStream(clear);
213   }
214
215   /* (non-Javadoc)
216    * @see uk.ac.vamsas.client.simpleclient.Lock#getFileInputStream(boolean)
217    */
218   public FileInputStream getFileInputStream(boolean atStart) throws IOException {
219     lockFile();
220     return fileLock.getFileInputStream(atStart);
221   }
222
223   /* (non-Javadoc)
224    * @see uk.ac.vamsas.client.simpleclient.Lock#getFileOutputStream(boolean)
225    */
226   public FileOutputStream getFileOutputStream(boolean clear) throws IOException {
227     lockFile();
228     return fileLock.getFileOutputStream(clear);
229   }
230   
231 }