refactored org to uk
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / SessionsFile.java
1 /* EMBL - The European Bioinformatics institute
2 * MSD Group
3 * VAMSAS Project
4 *
5 *  Copyright (c) 2005-2006 Thr European Bioinformatics Institute. All rights reserved.
6 *
7 *   Redistribution and use in source and binary forms, with or without
8 *   modification, are permitted provided that the following conditions
9 *   are met:
10 *  
11 *   1. Redistributions of source code must retain the above copyright
12 *      notice, this list of conditions and the following disclaimer. 
13 *  
14 *   2. Redistributions in binary form must reproduce the above copyright
15 *      notice, this list of conditions and the following disclaimer in
16 *      the documentation and/or other materials provided with the
17 *      distribution.
18 *  
19 *   3. The name MSD must not be used to endorse or promote products 
20 *      derived from this software without prior written permission. For 
21 *      written permission, please contact msd-help@ebi.ac.uk
22 *  
23 *   4. Products derived from this software may not be called "MSD"
24 *      nor may "MSD" appear in their names without prior written
25 *      permission of the MSD developers.
26 *  
27 *   5. Redistributions of any form whatsoever must retain the following
28 *      acknowledgment:
29 *      "This product includes software developed by MSD 
30 *       (http://www.ebi.ac.uk/)"
31 *  
32 *   THIS SOFTWARE IS PROVIDED BY THE MSD GROUP ``AS IS'' AND ANY
33 *   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35 *   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ENSEMBL GROUP OR
36 *   ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39 *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41 *   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42 *   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43 *   OF THE POSSIBILITY OF SUCH DAMAGE.
44 *
45 * The European Bioinformatics Institute may publish revised and/or new
46 * versions of this license with new releases of VAMSAS software.
47 *==============================================================================
48 *
49 *  @author <a href="mailto:pierre@ebi.ac.uk">Pierre MARGUERITE</a>
50
51 * Dec 13, 2006  - VamsasClientV4
52 *
53 */
54  
55 package uk.ac.vamsas.client.simpleclient;
56
57 import java.io.File;
58 import java.io.FileNotFoundException;
59 import java.io.FileOutputStream;
60 import java.io.ObjectInputStream;
61 import java.io.ObjectOutputStream;
62
63
64 import org.apache.commons.logging.Log;
65 import org.apache.commons.logging.LogFactory;
66 import org.vamsas.client.SessionHandle;
67
68 /**
69  * @author <a href="mailto:pierre@ebi.ac.uk">Pierre MARGUERITE</a>
70  * 
71  * 
72  */
73 public class SessionsFile extends ListFile {
74   
75   private static Log log = LogFactory.getLog(SessionsFile.class);
76   /**
77    * when set true - get FileNotFoundExceptions on WinXP when writing to locked stream after the backup has been made (via the backupFile method)
78    */
79   boolean backup=false;
80   /**
81    * number of my session in list - passed back when a session 
82    * is added to list, and used (if valid) for quickly 
83    * looking up presence of session handle in the list.
84    */
85   private int syncnum = 1;
86   /**
87    * @param file
88    */
89   public SessionsFile(File file) throws java.io.IOException {
90     super(file);
91   }
92   
93   
94   /**
95    * internal method for getting sessionsList - ensures a lock has been made but
96    * does not release it.
97    * 
98    * @return list of clients
99    */
100   private SessionHandle[] retrieveSessionHandles() {
101     if (lockFile()) {
102       try {
103         SessionHandle[] clients=null;
104         if (this.fileLock.length()>0) {
105           
106           ObjectInputStream is = new ObjectInputStream(this.fileLock.getBufferedInputStream(true));
107           Object o;
108           o=is.readObject();
109           if (o!=null) {
110             try {
111               clients = (SessionHandle[]) o;
112             }
113             catch (Exception e) {
114               log.error("Garbage in the clientHandle list "+this.sessionFile,e);
115             }
116           }
117         }
118         return clients;
119       } catch (FileNotFoundException e) {
120        // e.printStackTrace(System.err);
121         log.error(e);
122       } catch (Exception e) {
123         log.error(e);
124         //e.printStackTrace(System.err);
125       }
126     }
127     return null;
128   }
129
130   /**
131    * get the SessionsList from the file. May return null if lock failed!
132    * @return sessionsList
133    */
134   public SessionHandle[] retrieveSessionsList() {
135     if (lockFile()) {
136       SessionHandle[] clients = retrieveSessionHandles();
137       unlockFile();
138       return clients;
139     }
140     return null;
141   }
142
143   /**
144    * get list from the locked ClientList.
145    * @param extantlock
146    * @return clientList or null if lock failed (or file was empty)
147    */
148   public SessionHandle[] retrieveSessionsList(Lock extantlock) {
149     if (lockFile(extantlock)) {
150      SessionHandle[] clients = retrieveSessionHandles();
151       unlockFile();
152       return clients;
153     }
154     return null;
155   }
156  
157   
158   /**
159    * adds clientHandle me to the clientList under an existing lock extantLock.
160    * @param me
161    * @param extantLock
162    * @return client index in list or 0 if lock was invalid or addClient operation failed.
163    */
164   public int addSession(SessionHandle me, Lock extantLock) {
165     return addSession(me, true, extantLock);
166   }
167   
168   /**
169    * adds SessionsHandle me to the sessionsList under an existing lock.
170    * @param me - sessionsHandle
171    * @param disambig - if true then add will fail if an identical clientHandle already exists
172    * @param extantLock - existing lock
173    * @return client index in list or 0 if addClient (or the lock) failed.
174    */
175   
176   public int addSession(SessionHandle session, boolean disambig, Lock extantLock) {
177     if (lockFile(extantLock)) {
178       this.syncnum = addSession(session, disambig);
179       unlockFile();
180       return this.syncnum;
181     }
182     return 0;
183   }
184   
185   /**
186    * removes the current session from the  SessionsList without complaint if the session isn't in the sessionsList already.
187    * @param me client handle to be removed
188    * @param clientlock existing lock passed from watcher.
189    */
190   public void removeSession(SessionHandle session, Lock clientlock) {
191     int mynum =-1;
192     if (lockFile(clientlock)) {
193       SessionHandle[] sessions = retrieveSessionHandles();
194       if (sessions != null) {
195         if ((this.syncnum<=0 || this.syncnum>sessions.length) || sessions[this.syncnum-1]!=session) {
196           for (int i = 0, j = sessions.length; i < j; i++) 
197             if (sessions[i].equals(session)) {
198               mynum=i;
199               break;
200             }
201         } else {
202           mynum=this.syncnum-1;
203         }
204         if (mynum>-1) {
205           SessionHandle[] newlist = new SessionHandle[sessions.length - 1];
206           for (int k=0,i = 0, j = sessions.length; i < j; i++)
207             if (i!=mynum)
208               newlist[k++] = sessions[i];
209           if (!putSessionsList(newlist))
210             throw new Error("Failed to write new sessionsList!"); // failed to put the sessionList to disk.
211         }
212       }
213       unlockFile();
214     } else {
215       throw new Error("Couldn't get lock for "+((sessionFile==null) ? "Unitialised sessionFile in SessionsFile" : this.sessionFile.getAbsolutePath()));
216     }
217   }
218   /**
219    * Adds a SessionHandle to the SessionList file - optionally disambiguating 
220    * the SessionHandle (modifes the URN). 
221    * Note: Caller is left to release the lock on the SessionList.
222    * @param me
223    * @param disambiguate -
224    *          flag indicating if the URN for me should be disambiguated to
225    *          differentiate between sessions.
226    * @return index of sessionHandle in new list, or -1-position of existing
227    *         sessionHandle (if disambiguate is true)
228    */
229   protected int addSession(SessionHandle session, boolean disambiguate) {
230     int newsession = 0;
231     int tries=5;
232     while (tries-->0 && !lockFile())
233       try { Thread.sleep(1); } catch (Exception e){};
234     if (lockFile()) {
235       SessionHandle[] sessions = retrieveSessionHandles();
236
237       if (sessions == null) {
238         sessions = new SessionHandle[1];
239         sessions[0] = session;
240         newsession = 1;
241       } else {
242         int k = 0;
243         for (int i = 0, j = sessions.length; i < j; i++) {
244           if ( sessions[i].equals(session)) {
245             if (disambiguate) {
246               while (sessions[i].equals(session)) {
247                // me.setClientUrn(me.getClientUrn() + k++); // TODO: make a better
248                                                           // disambiguation of
249                                                           // urn.
250               }
251             } else {
252               // will not write the ambiguous clientHandle to disk, just return
253               // its index.
254               return -1 - i;
255             }
256           }
257         }
258         int i, j;
259         SessionHandle[] newlist = new SessionHandle[sessions.length + 1];
260         for (i = 0, j = sessions.length; i < j; i++)
261           newlist[i] = sessions[i];
262         newlist[j] = session;
263         sessions = newlist;
264         newsession = j+1;
265       }
266       if (!putSessionsList(sessions))
267         return 0; // failed to put the clientList to disk.
268     }
269     return newsession;
270   }
271   
272   
273   /**
274    * safely writes sessions array to the file referred to by sessionFile.
275    * 
276    * @param clients
277    * @return true if successful write. Throws Errors otherwise.
278    */
279   protected boolean putSessionsList(SessionHandle[] clients) {
280     if (lockFile()) {
281       File templist=null;
282       if (!this.backup || (templist = backupSessionFile()) != null) {
283         int retries=3;
284         while (retries-->0) {
285           try {
286             ObjectOutputStream os = 
287               new ObjectOutputStream(this.fileLock.getBufferedOutputStream(true));
288             log.debug("About to write "+clients.length+" sessionHandles to output stream.");
289             os.writeObject(clients);
290             os.close();
291           // All done - remove the backup.
292           if (this.backup)
293             templist.delete();
294           templist = null;
295           retries=-1;
296           } catch (Exception e) {
297            // System.err
298             //.println("Serious - problems writing to sessionFile.");
299             log.error("Serious - problems writing to sessionFile.",e);
300             if (retries>0 && templist != null) {
301             //  System.err.println("Recovering from Backup in "
302               //        + templist.getAbsolutePath());
303               log.error("Recovering from Backup in "+ templist.getAbsolutePath());
304               templist.renameTo(this.fileLock.target);
305             }
306             //e.printStackTrace(System.err);
307             log.error(e);
308           }
309         }
310         if (retries>-2) {
311          // System.err
312          // .println("Serious - problems writing to sessionFile. Giving Up.");
313           log.error("Serious - problems writing to sessionFile. Giving Up.");
314           return false;
315         }
316       } else {
317         throw new Error(
318             "Couldn't create backup of the clientList before writing to it!");
319       }
320     } else {
321       throw new Error("Could not lock the clientList: "
322           + ((this.sessionFile == null) ? "Unitialized ClientsFile"
323               : " failed to get lock on " + this.sessionFile.getAbsolutePath()));
324     }
325     // successful!
326     return true;
327   }
328
329   public void clearList() {
330     if (lockFile()) {
331       try {
332         FileOutputStream fout = this.fileLock.getFileOutputStream(true);
333         fout.flush();
334         fout.close();
335       } catch (Exception e) {
336         throw new Error("Problems trying to clear clientlist!",e);
337         
338       }
339     }
340     
341   }
342 }