applied LGPLv3 and source code formatting.
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / SessionFile.java
1 /*\r
2  * This file is part of the Vamsas Client version 0.1. \r
3  * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, \r
4  *  Andrew Waterhouse and Dominik Lindner.\r
5  * \r
6  * Earlier versions have also been incorporated into Jalview version 2.4 \r
7  * since 2008, and TOPALi version 2 since 2007.\r
8  * \r
9  * The Vamsas Client is free software: you can redistribute it and/or modify\r
10  * it under the terms of the GNU Lesser General Public License as published by\r
11  * the Free Software Foundation, either version 3 of the License, or\r
12  * (at your option) any later version.\r
13  *  \r
14  * The Vamsas Client is distributed in the hope that it will be useful,\r
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
17  * GNU Lesser General Public License for more details.\r
18  * \r
19  * You should have received a copy of the GNU Lesser General Public License\r
20  * along with the Vamsas Client.  If not, see <http://www.gnu.org/licenses/>.\r
21  */\r
22 package uk.ac.vamsas.client.simpleclient;\r
23 \r
24 import java.io.BufferedInputStream;\r
25 import java.io.BufferedOutputStream;\r
26 import java.io.File;\r
27 import java.io.FileInputStream;\r
28 import java.io.FileNotFoundException;\r
29 import java.io.FileOutputStream;\r
30 import java.io.IOException;\r
31 import java.io.RandomAccessFile;\r
32 import java.nio.channels.ReadableByteChannel;\r
33 \r
34 import org.apache.commons.logging.Log;\r
35 import org.apache.commons.logging.LogFactory;\r
36 \r
37 /**\r
38  * Basic methods for classes handling locked IO on files monitored by all\r
39  * (simpleclient) clients in a vamsas session.\r
40  * \r
41  * @author jimp TODO: support non nio file locking capable systems\r
42  */\r
43 public class SessionFile {\r
44   private static Log log = LogFactory.getLog(SessionFile.class);\r
45 \r
46   protected File sessionFile;\r
47 \r
48   protected Lock fileLock = null;\r
49 \r
50   protected SessionFile(File file) {\r
51     super();\r
52     sessionFile = file;\r
53   }\r
54 \r
55   protected boolean lockFile(Lock extantlock) {\r
56     if (fileLock != null && !fileLock.isLocked()) {\r
57       fileLock.release();// tidy up invalid lock\r
58       fileLock = null;\r
59     }\r
60     if (extantlock != null && extantlock.isLocked())\r
61       fileLock = extantlock; // THIS IS BROKEN - lockFile then nulls the lock.\r
62     return lockFile();\r
63   }\r
64 \r
65   private boolean ensureSessionFile() {\r
66     if (sessionFile != null) {\r
67       if (!sessionFile.exists()) {\r
68         // create new file\r
69         try {\r
70           if (!sessionFile.createNewFile()) {\r
71             log.error("Failed to create file prior to locking: " + sessionFile);\r
72             return false;\r
73           }\r
74         } catch (IOException e) {\r
75           log.error("Exception when trying to create file " + sessionFile, e);\r
76           return false;\r
77         }\r
78       }\r
79       return true;\r
80     }\r
81     log.error("ensureSessionFile called for non-initialised SessionFile!");\r
82     return false;\r
83   }\r
84 \r
85   /**\r
86    * Get a lock for the SessionFile\r
87    * \r
88    * @return true if lock was made\r
89    */\r
90   protected boolean lockFile() {\r
91     if (fileLock != null) {\r
92       if (fileLock.isLocked()) {\r
93         if (!ensureSessionFile())\r
94           return false;\r
95         return true;\r
96       } else {\r
97         // lock failed for some reason.\r
98         fileLock.release();\r
99         log\r
100             .info("Unexpected session file lock failure. Trying to get it again.");\r
101         fileLock = null;\r
102       }\r
103     }\r
104     if (!ensureSessionFile())\r
105       return false;\r
106     // TODO: see if we need to loop-wait for locks or they just block until\r
107     // lock is made...\r
108     long tries = 5000;\r
109     do {\r
110       tries--;\r
111       if (fileLock == null || !fileLock.isLocked()) {\r
112         // try { Thread.sleep(1); } catch (Exception e) {};\r
113         fileLock = LockFactory.getLock(sessionFile, true); // TODO: wait around\r
114                                                            // if we can't get\r
115                                                            // the lock.\r
116       }\r
117     } while (tries > 0 && !fileLock.isLocked());\r
118     if (!fileLock.isLocked())\r
119       log.error("Failed to get lock for " + sessionFile);\r
120     // fileLock = new Lock(sessionFile);\r
121     return fileLock.isLocked();\r
122   }\r
123 \r
124   /**\r
125    * Explicitly release the SessionFile's lock.\r
126    * \r
127    * @return true if lock was released.\r
128    */\r
129   protected void unlockFile() {\r
130     if (fileLock != null) {\r
131       fileLock.release();\r
132       fileLock = null;\r
133     }\r
134   }\r
135 \r
136   /**\r
137    * Makes a backup of the sessionFile.\r
138    * \r
139    * @return Backed up SessionFile or null if failed to make backup.\r
140    */\r
141   protected File backupSessionFile() {\r
142     return backupSessionFile(fileLock, sessionFile.getName(), ".old",\r
143         sessionFile.getParentFile());\r
144   }\r
145 \r
146   protected File backupSessionFile(Lock extantLock, String backupPrefix,\r
147       String backupSuffix, File backupDir) {\r
148     File tempfile = null;\r
149     if (lockFile(extantLock)) {\r
150       try {\r
151         tempfile = File.createTempFile(backupPrefix, backupSuffix, backupDir);\r
152         long sourceln = fileLock.length();\r
153         if (sourceln > 0) {\r
154           FileOutputStream tos = new FileOutputStream(tempfile);\r
155           ReadableByteChannel channel;\r
156           channel = fileLock.getRaChannel().position(0);\r
157           long ntrans = 0;\r
158           while (ntrans < sourceln) {\r
159             long tlen;\r
160             ntrans += tlen = tos.getChannel().transferFrom(channel, ntrans,\r
161                 sourceln);\r
162             if (log.isDebugEnabled()) {\r
163               log.debug("Transferred " + tlen + " out of " + sourceln\r
164                   + " bytes");\r
165             }\r
166           }\r
167           tos.close();\r
168           if (!channel.isOpen())\r
169             throw new Error("LIBRARY PORTABILITY ISSUE: "\r
170                 + tos.getChannel().getClass()\r
171                 + ".transferFrom closes source channel!");\r
172           if (!lockFile(extantLock))\r
173             throw new Error("LIBRARY PORTABILITY ISSUE: Lost lock for "\r
174                 + sessionFile.getName() + " after backup.");\r
175 \r
176         }\r
177       } catch (FileNotFoundException e1) {\r
178         log.warn("Can't create temp file for " + sessionFile.getName(), e1);\r
179         tempfile = null;\r
180       } catch (IOException e1) {\r
181         log.warn("Error when copying content to temp file for "\r
182             + sessionFile.getName(), e1);\r
183         tempfile = null;\r
184       }\r
185     }\r
186     return tempfile;\r
187   }\r
188 \r
189   /**\r
190    * Replaces data in sessionFile with data from file handled by another\r
191    * sessionFile passes up any exceptions.\r
192    * \r
193    * @param newData\r
194    *          source for new data\r
195    */\r
196   protected void updateFrom(Lock extantLock, SessionFile newData)\r
197       throws IOException {\r
198     log.debug("Updating " + sessionFile.getAbsolutePath() + " from "\r
199         + newData.sessionFile.getAbsolutePath());\r
200     if (newData == null)\r
201       throw new IOException("Null newData object.");\r
202     if (newData.sessionFile == null)\r
203       throw new IOException("Null SessionFile in newData.");\r
204 \r
205     if (!lockFile(extantLock))\r
206       throw new IOException("Failed to get write lock for " + sessionFile);\r
207     if (!newData.lockFile())\r
208       throw new IOException("Failed to get lock for updateFrom "\r
209           + newData.sessionFile);\r
210     RandomAccessFile nrafile = newData.fileLock.getRaFile();\r
211     nrafile.seek(0);\r
212     RandomAccessFile trafile = fileLock.getRaFile();\r
213     /*\r
214      * long tries=5000; while (trafile==null && --tries>0) {\r
215      * log.debug("Lost lock on "+sessionFile+"! Re-trying for a transfer.");\r
216      * lockFile(); trafile = fileLock.getRaFile(); }\r
217      */\r
218     // TODO JBPNote: attempt to ensure save really saves the VamDoc.jar file\r
219     trafile.seek(0);\r
220     trafile.getChannel()\r
221         .transferFrom(nrafile.getChannel(), 0, nrafile.length());\r
222     // JBPNote: attempt to close the streams to flush the data out\r
223     // trafile.close();\r
224     // nrafile.close();\r
225   }\r
226 \r
227   /**\r
228    * remove all trace of the sessionFile file\r
229    * \r
230    */\r
231   protected void eraseExistence() {\r
232     unlockFile();\r
233     if (sessionFile != null) {\r
234       sessionFile.delete();\r
235       sessionFile = null;\r
236     }\r
237   }\r
238 \r
239   /*\r
240    * (non-Javadoc)\r
241    * \r
242    * @see uk.ac.vamsas.client.simpleclient.Lock#getBufferedInputStream(boolean)\r
243    */\r
244   public BufferedInputStream getBufferedInputStream(boolean atStart)\r
245       throws IOException {\r
246     lockFile();\r
247     return fileLock.getBufferedInputStream(atStart);\r
248   }\r
249 \r
250   /*\r
251    * (non-Javadoc)\r
252    * \r
253    * @see uk.ac.vamsas.client.simpleclient.Lock#getBufferedOutputStream(boolean)\r
254    */\r
255   public BufferedOutputStream getBufferedOutputStream(boolean clear)\r
256       throws IOException {\r
257     lockFile();\r
258     return fileLock.getBufferedOutputStream(clear);\r
259   }\r
260 \r
261   /*\r
262    * (non-Javadoc)\r
263    * \r
264    * @see uk.ac.vamsas.client.simpleclient.Lock#getFileInputStream(boolean)\r
265    */\r
266   public FileInputStream getFileInputStream(boolean atStart) throws IOException {\r
267     lockFile();\r
268     return fileLock.getFileInputStream(atStart);\r
269   }\r
270 \r
271   /*\r
272    * (non-Javadoc)\r
273    * \r
274    * @see uk.ac.vamsas.client.simpleclient.Lock#getFileOutputStream(boolean)\r
275    */\r
276   public FileOutputStream getFileOutputStream(boolean clear) throws IOException {\r
277     lockFile();\r
278     return fileLock.getFileOutputStream(clear);\r
279   }\r
280 \r
281 }\r