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