added legacy import of version 0.1 SessionLists
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / SessionsFile.java
1 /*\r
2  * This file is part of the Vamsas Client version 0.2. \r
3  * Copyright 2010 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.File;\r
25 import java.io.FileNotFoundException;\r
26 import java.io.FileOutputStream;\r
27 import java.io.ObjectInputStream;\r
28 import java.io.ObjectOutputStream;\r
29 import java.net.URL;\r
30 \r
31 import org.apache.commons.logging.Log;\r
32 import org.apache.commons.logging.LogFactory;\r
33 \r
34 import uk.ac.vamsas.client.SessionHandle;\r
35 import uk.ac.vamsas.client.simpleclient.SimpleSessionHandle;\r
36 \r
37 /**\r
38  * @author <a href="mailto:pierre@ebi.ac.uk">Pierre MARGUERITE</a>\r
39  * \r
40  * \r
41  */\r
42 public class SessionsFile extends ListFile {\r
43 \r
44   private static Log log = LogFactory.getLog(SessionsFile.class);\r
45 \r
46   /**\r
47    * when set true - get FileNotFoundExceptions on WinXP when writing to locked\r
48    * stream after the backup has been made (via the backupFile method)\r
49    */\r
50   boolean backup = false;\r
51 \r
52   /**\r
53    * number of my session in list - passed back when a session is added to list,\r
54    * and used (if valid) for quickly looking up presence of session handle in\r
55    * the list.\r
56    */\r
57   private int syncnum = 1;\r
58 \r
59   /**\r
60    * @param file\r
61    */\r
62   public SessionsFile(File file) throws java.io.IOException {\r
63     super(file);\r
64   }\r
65 \r
66   /**\r
67    * internal method for getting sessionsList - ensures a lock has been made but\r
68    * does not release it.\r
69    * \r
70    * @return list of clients\r
71    */\r
72   private SimpleSessionHandle[] retrieveSessionHandles() {\r
73     if (lockFile()) {\r
74       try {\r
75         SimpleSessionHandle[] sessions = null;\r
76         if (this.fileLock.length() > 0) {\r
77 \r
78           ObjectInputStream is = new ObjectInputStream(this.fileLock\r
79               .getBufferedInputStream(true));\r
80           Object o;\r
81           o = is.readObject();\r
82           if (o != null) {\r
83             try {\r
84               if (o instanceof SimpleSessionHandle[]) {\r
85                 sessions = (SimpleSessionHandle[]) o;\r
86               } else {\r
87                 // legacy support or incorrect path.\r
88                 if (o instanceof SessionHandle[]) {\r
89                   log.info("Importing VAMSAS 0.1 clientHandle list.");\r
90                   SessionHandle[] oldsess = (SessionHandle[]) o;\r
91                   File sessdir;\r
92                   sessions = new SimpleSessionHandle[oldsess.length];\r
93                   for (int s=0;s<oldsess.length;s++)\r
94                   {\r
95                     try {\r
96                       sessdir = new SessionUrn(oldsess[s].getSessionUrn()).asFile();\r
97                       // try to locate it.\r
98                       sessions[s] = new SimpleSessionHandle(oldsess[s].getSessionUrn(), sessdir);\r
99                     } catch (Exception e)\r
100                     {\r
101                       // give up and report an error.\r
102                       sessions = null;\r
103                       throw new Exception("Legacy SessionFile problem. Couldn't locate "+oldsess[s]+" as a file.",e);\r
104                     }\r
105                   }\r
106                 }\r
107               }\r
108             } catch (Exception e) {\r
109               log.error("Garbage in the clientHandle list " + this.sessionFile,\r
110                   e);\r
111             }\r
112           }\r
113           // is.close();\r
114         }\r
115         return sessions;\r
116       } catch (FileNotFoundException e) {\r
117         // e.printStackTrace(System.err);\r
118         log.error(e);\r
119       } catch (Exception e) {\r
120         log.error(e);\r
121         // e.printStackTrace(System.err);\r
122       }\r
123     }\r
124     return null;\r
125   }\r
126 \r
127   /**\r
128    * get the SessionsList from the file. May return null if lock failed!\r
129    * \r
130    * @return sessionsList\r
131    */\r
132   public SimpleSessionHandle[] retrieveSessionsList() {\r
133     if (lockFile()) {\r
134       SimpleSessionHandle[] clients = retrieveSessionHandles();\r
135       unlockFile();\r
136       return clients;\r
137     }\r
138     return null;\r
139   }\r
140 \r
141   /**\r
142    * get list from the locked SessionsList.\r
143    * \r
144    * @param extantlock\r
145    * @return sessionList or null if lock failed (or file was empty)\r
146    */\r
147   public SimpleSessionHandle[] retrieveSessionsList(Lock extantlock) {\r
148     if (lockFile(extantlock)) {\r
149       SimpleSessionHandle[] sessions = retrieveSessionHandles();\r
150       unlockFile();\r
151       return sessions;\r
152     }\r
153     return null;\r
154   }\r
155 \r
156   /**\r
157    * adds SessionHandle me to the sessionList under an existing lock extantLock.\r
158    * \r
159    * @param newSession\r
160    * @param extantLock\r
161    * @return session index in list or 0 if lock was invalid or addSession\r
162    *         operation failed.\r
163    */\r
164   public int addSession(SimpleSessionHandle newSession, Lock extantLock) {\r
165     return addSession(newSession, true, extantLock);\r
166   }\r
167 \r
168   /**\r
169    * adds SessionsHandle me to the sessionsList under an existing lock.\r
170    * \r
171    * @param newSession\r
172    *          - sessionsHandle\r
173    * @param disambig\r
174    *          - if true then add will fail if an identical sessionHandle already\r
175    *          exists\r
176    * @param extantLock\r
177    *          - existing lock\r
178    * @return client index in list or 0 if addSession (or the lock) failed.\r
179    */\r
180 \r
181   public int addSession(SimpleSessionHandle newSession, boolean disambig,\r
182       Lock extantLock) {\r
183     if (lockFile(extantLock)) {\r
184       this.syncnum = addSession(newSession, disambig);\r
185       unlockFile();\r
186       return this.syncnum;\r
187     }\r
188     return 0;\r
189   }\r
190 \r
191   /**\r
192    * removes the current session from the SessionsList without complaint if the\r
193    * session isn't in the sessionsList already.\r
194    * \r
195    * @param session\r
196    *          session handle to be removed\r
197    * @param sessionlock\r
198    *          existing lock passed from watcher.\r
199    */\r
200   public void removeSession(SessionHandle session, Lock sessionlock) {\r
201     int mynum = -1;\r
202     if (lockFile(sessionlock)) {\r
203 \r
204       SimpleSessionHandle[] sessions = retrieveSessionHandles();\r
205       if (sessions != null) {\r
206         if ((this.syncnum <= 0 || this.syncnum > sessions.length)\r
207             || !session.equals(sessions[this.syncnum - 1])) {\r
208           for (int i = 0, j = sessions.length; i < j; i++) {\r
209             if (sessions[i].equals(session)) {\r
210               mynum = i;\r
211               break;\r
212             }\r
213           }\r
214         } else {\r
215           mynum = this.syncnum - 1;\r
216         }\r
217 \r
218         if (mynum > -1) {\r
219           SimpleSessionHandle[] newlist = new SimpleSessionHandle[sessions.length - 1];\r
220           for (int k = 0, i = 0, j = sessions.length; i < j; i++)\r
221             if (i != mynum)\r
222               newlist[k++] = sessions[i];\r
223           if (!putSessionsList(newlist))\r
224             throw new Error("Failed to write new sessionsList!"); // failed to\r
225                                                                   // put the\r
226                                                                   // sessionList\r
227                                                                   // to disk.\r
228         }\r
229       }\r
230       unlockFile();\r
231     } else {\r
232       throw new Error(\r
233           "Couldn't get lock for "\r
234               + ((this.sessionFile == null) ? "Unitialised sessionFile in SessionsFile"\r
235                   : this.sessionFile.getAbsolutePath()));\r
236     }\r
237   }\r
238 \r
239   /**\r
240    * Adds a SessionHandle to the SessionList file - optionally disambiguating\r
241    * the SessionHandle (modifes the URN). Note: Caller is left to release the\r
242    * lock on the SessionList.\r
243    * \r
244    * @param session\r
245    * @param disambiguate\r
246    *          - flag indicating if the URN for me should be disambiguated to\r
247    *          differentiate between sessions.\r
248    * @return index of sessionHandle in new list, or -1-position of existing\r
249    *         sessionHandle (if disambiguate is true)\r
250    */\r
251   protected int addSession(SimpleSessionHandle session, boolean disambiguate) {\r
252     int newsession = 0;\r
253     int tries = 5;\r
254     while (tries-- > 0 && !lockFile())\r
255       try {\r
256         Thread.sleep(1);\r
257       } catch (Exception e) {\r
258       }\r
259     ;\r
260     if (lockFile()) {\r
261       SimpleSessionHandle[] sessions = retrieveSessionHandles();\r
262 \r
263       if (sessions == null) {\r
264         sessions = new SimpleSessionHandle[1];\r
265         sessions[0] = session;\r
266         newsession = 1;\r
267       } else {\r
268         int k = 0;\r
269         for (int i = 0, j = sessions.length; i < j; i++) {\r
270           if (sessions[i].equals(session)) {\r
271             if (disambiguate) {\r
272               while (sessions[i].equals(session)) {\r
273                 // me.setClientUrn(me.getClientUrn() + k++); // TODO: make a\r
274                 // better\r
275                 // disambiguation of\r
276                 // urn.\r
277               }\r
278             } else {\r
279               // will not write the ambiguous clientHandle to disk, just return\r
280               // its index.\r
281               return -1 - i;\r
282             }\r
283           }\r
284         }\r
285         int i, j;\r
286         SimpleSessionHandle[] newlist = new SimpleSessionHandle[sessions.length + 1];\r
287         for (i = 0, j = sessions.length; i < j; i++)\r
288           newlist[i] = sessions[i];\r
289         newlist[j] = session;\r
290         sessions = newlist;\r
291         newsession = j + 1;\r
292       }\r
293       if (!putSessionsList(sessions))\r
294         return 0; // failed to put the clientList to disk.\r
295     }\r
296     return newsession;\r
297   }\r
298 \r
299   /**\r
300    * safely writes sessions array to the file referred to by sessionFile.\r
301    * \r
302    * @param clients\r
303    * @return true if successful write. Throws Errors otherwise.\r
304    */\r
305   protected boolean putSessionsList(SimpleSessionHandle[] clients) {\r
306     if (lockFile()) {\r
307       File templist = null;\r
308       if (!this.backup || (templist = backupSessionFile()) != null) {\r
309         int retries = 3;\r
310         while (retries-- > 0) {\r
311           try {\r
312             ObjectOutputStream os = new ObjectOutputStream(this.fileLock\r
313                 .getBufferedOutputStream(true));\r
314             log.debug("About to write " + clients.length\r
315                 + " sessionHandles to output stream.");\r
316             os.writeObject(clients);\r
317             // os.flush();\r
318             os.close();\r
319             // All done - remove the backup.\r
320             if (this.backup)\r
321               templist.delete();\r
322             templist = null;\r
323             retries = -1;\r
324           } catch (Exception e) {\r
325             // System.err\r
326             // .println("Serious - problems writing to sessionFile.");\r
327             log.error("Serious - problems writing to sessionFile.", e);\r
328             if (retries > 0 && templist != null) {\r
329               // System.err.println("Recovering from Backup in "\r
330               // + templist.getAbsolutePath());\r
331               log.error("Recovering from Backup in "\r
332                   + templist.getAbsolutePath());\r
333               templist.renameTo(this.fileLock.target);\r
334             }\r
335             // e.printStackTrace(System.err);\r
336             log.error(e);\r
337           }\r
338         }\r
339         if (retries > -2) {\r
340           // System.err\r
341           // .println("Serious - problems writing to sessionFile. Giving Up.");\r
342           log.error("Serious - problems writing to sessionFile. Giving Up.");\r
343           return false;\r
344         }\r
345       } else {\r
346         throw new Error(\r
347             "Couldn't create backup of the clientList before writing to it!");\r
348       }\r
349     } else {\r
350       throw new Error("Could not lock the clientList: "\r
351           + ((this.sessionFile == null) ? "Unitialized ClientsFile"\r
352               : " failed to get lock on " + this.sessionFile.getAbsolutePath()));\r
353     }\r
354     // successful!\r
355     return true;\r
356   }\r
357 \r
358   public void clearList() {\r
359     if (lockFile()) {\r
360       try {\r
361         FileOutputStream fout = this.fileLock.getFileOutputStream(true);\r
362         fout.flush();\r
363         fout.close();\r
364       } catch (Exception e) {\r
365         throw new Error("Problems trying to clear clientlist!", e);\r
366 \r
367       }\r
368     }\r
369 \r
370   }\r
371 }\r