e2d1db777b0b56162acd5efa919ee6dfe1d272e1
[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         if (fileLock.length()>0) {
122           FileOutputStream tos = new FileOutputStream(tempfile);
123           ReadableByteChannel channel;
124           tos.getChannel().transferFrom(channel=fileLock.getRaChannel(), 0,
125               fileLock.length());
126           tos.close();
127           if (!channel.isOpen())
128             throw new Error("LIBRARY PORTABILITY ISSUE: "+tos.getChannel().getClass()+".transferFrom closes source channel!");
129           if (!lockFile(extantLock))
130             throw new Error("LIBRARY PORTABILITY ISSUE: Lost lock for "+sessionFile.getName()+" after backup.");
131           
132         }
133       } catch (FileNotFoundException e1) {
134         log.warn("Can't create temp file for "+sessionFile.getName(),e1);
135         tempfile=null;
136       } catch (IOException e1) {
137         log.warn("Error when copying content to temp file for "+sessionFile.getName(),e1);
138         tempfile=null;
139       }
140     }
141     return tempfile;
142   }
143   /**
144    * Replaces data in sessionFile with data from file handled by another sessionFile
145    * passes up any exceptions.
146    * @param newData source for new data
147    */
148   protected void updateFrom(Lock extantLock, SessionFile newData) throws IOException {
149     log.debug("Updating "+sessionFile.getAbsolutePath()+" from "+newData.sessionFile.getAbsolutePath());
150     if (newData==null)
151       throw new IOException("Null newData object.");
152     if (newData.sessionFile==null)
153       throw new IOException("Null SessionFile in newData.");
154       
155     if (!lockFile(extantLock))
156       throw new IOException("Failed to get write lock for "+sessionFile);
157     if (!newData.lockFile())
158       throw new IOException("Failed to get lock for updateFrom "+newData.sessionFile);
159     RandomAccessFile nrafile = newData.fileLock.getRaFile();
160     nrafile.seek(0);
161     RandomAccessFile trafile = fileLock.getRaFile();
162     /*long tries=5000;
163     while (trafile==null && --tries>0) {
164       log.debug("Lost lock on "+sessionFile+"! Re-trying for a transfer.");
165       lockFile();
166       trafile = fileLock.getRaFile();
167     }*/
168     // TODO JBPNote: attempt to ensure save really saves the VamDoc.jar file
169     trafile.seek(0);
170     trafile.getChannel().transferFrom(nrafile.getChannel(), 0, 
171         nrafile.length());
172     // JBPNote: attempt to close the streams to flush the data out
173     // trafile.close();
174     //nrafile.close();
175   }
176   /**
177    * remove all trace of the sessionFile file
178    *
179    */
180   protected void eraseExistence() {
181     unlockFile();
182     if (sessionFile!=null) {
183       sessionFile.delete();
184       sessionFile = null;
185     }
186   }
187   /* (non-Javadoc)
188    * @see uk.ac.vamsas.client.simpleclient.Lock#getBufferedInputStream(boolean)
189    */
190   public BufferedInputStream getBufferedInputStream(boolean atStart) throws IOException {
191     lockFile();
192     return fileLock.getBufferedInputStream(atStart);
193   }
194
195   /* (non-Javadoc)
196    * @see uk.ac.vamsas.client.simpleclient.Lock#getBufferedOutputStream(boolean)
197    */
198   public BufferedOutputStream getBufferedOutputStream(boolean clear) throws IOException {
199     lockFile();
200     return fileLock.getBufferedOutputStream(clear);
201   }
202
203   /* (non-Javadoc)
204    * @see uk.ac.vamsas.client.simpleclient.Lock#getFileInputStream(boolean)
205    */
206   public FileInputStream getFileInputStream(boolean atStart) throws IOException {
207     lockFile();
208     return fileLock.getFileInputStream(atStart);
209   }
210
211   /* (non-Javadoc)
212    * @see uk.ac.vamsas.client.simpleclient.Lock#getFileOutputStream(boolean)
213    */
214   public FileOutputStream getFileOutputStream(boolean clear) throws IOException {
215     lockFile();
216     return fileLock.getFileOutputStream(clear);
217   }
218   
219 }