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