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