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