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