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