added more extantLock methods and test for clientList monitoring (using FileWatcher...
[vamsas.git] / src / org / vamsas / client / simpleclient / ClientsFile.java
1 package org.vamsas.client.simpleclient;
2
3 import org.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  * @author jim Handler for the clientsFile within a vamsas session thread.
21  */
22 public class ClientsFile {
23   private File filelist;
24
25   /**
26    * number of my client in list (not known at start but used when known to make
27    * lock)
28    */
29   private int syncnum = 1;
30
31   public ClientsFile(File filelist) throws IOException {
32     this.filelist = filelist;
33     if (!this.filelist.exists())
34       this.filelist.createNewFile();
35   }
36
37   private Lock listlock = null;
38
39   protected boolean lockList(Lock extantlock) {
40     if (listlock!=null && !listlock.isLocked()) {
41       listlock.release();// tidy up invalid lock
42     }
43     listlock=extantlock;
44     return lockList();
45   }
46   /**
47    * Get a lock for the ClientsFile
48    * 
49    * @return true if lock was made
50    */
51   protected boolean lockList() {
52     if (listlock != null)
53       if (listlock.isLocked())
54         return true;
55       else 
56         // lock failed for some reason.
57         listlock.release();
58     listlock = null;
59     if (filelist != null) {
60       if (filelist.exists()) {
61         // TODO: see if we need to loop-wait for locks or they just block until
62         // lock is made...
63         do {
64           if (listlock!=null)
65             listlock.release();
66           listlock = new Lock(filelist); // TODO: wait around if we can't get the lock.
67         } while (!listlock.isLocked());
68         // listlock = new Lock(filelist);
69         return listlock.isLocked();
70       }
71     } else
72       throw new Error(
73           "org.vamsas.client.simpleclient.ClientsFile.lockList called for non-initialised ClientsFile!");
74
75     // no lock possible
76     return false;
77   }
78
79   /**
80    * Explicitly release the ClientsFile lock.
81    * 
82    * @return true if lock was released.
83    */
84   protected void unlockList() {
85     if (listlock != null) {
86       listlock.release();    
87       listlock = null;
88     }
89   }
90
91   /**
92    * internal method for getting clientList - ensures a lock has been made but
93    * does not release it.
94    * 
95    * @return list of clients
96    */
97   private ClientHandle[] retrieveClientHandles() {
98     if (lockList()) {
99       try {
100         ClientHandle[] clients=null;
101         if (this.listlock.rafile.length()>0) {
102           ObjectInputStream is = new ObjectInputStream(new BufferedInputStream(
103               new java.io.FileInputStream(filelist)));
104           Object o;
105           o=is.readObject();
106           if (o!=null) {
107             try {
108               clients = (ClientHandle[]) o;
109             }
110             catch (Exception e) {
111               System.err.println("Garbage in the clientHandle list "+this.filelist);
112             }
113           }
114         }
115         return clients;
116       } catch (FileNotFoundException e) {
117         // TODO Auto-generated catch block
118         e.printStackTrace(System.err);
119       } catch (Exception e) {
120         e.printStackTrace(System.err);
121       }
122     }
123     return null;
124   }
125   /**
126    * get the clientList from the file. May return null if lock failed!
127    * @return clientList
128    */
129   public ClientHandle[] retrieveClientList() {
130     if (lockList()) {
131       ClientHandle[] clients = retrieveClientHandles();
132       unlockList();
133       return clients;
134     }
135     return null;
136   }
137   /**
138    * get list from the locked ClientList.
139    * @param extantlock
140    * @return clientList or null if lock failed (or file was empty)
141    */
142   public ClientHandle[] retrieveClientList(Lock extantlock) {
143     if (lockList(extantlock)) {
144       ClientHandle[] clients = retrieveClientHandles();
145       unlockList();
146       return clients;
147     }
148     return null;
149   }
150   /**
151    * adds clientHandle me to the clientList under an existing lock extantLock.
152    * @param me
153    * @param extantLock
154    * @return client index in list or 0 if lock was invalid or addClient operation failed.
155    */
156   public int addClient(ClientHandle me, Lock extantLock) {
157     return addClient(me, true, extantLock);
158   }
159   
160   /**
161    * adds clientHandle me to the clientList under an existing lock.
162    * @param me - clientHandle
163    * @param disambig - if true then add will fail if an identical clientHandle already exists
164    * @param extantLock - existing lock
165    * @return client index in list or 0 if addClient (or the lock) failed.
166    */
167   
168   public int addClient(ClientHandle me, boolean disambig, Lock extantLock) {
169     if (lockList(extantLock)) {
170       syncnum = addClient(me, disambig);
171       unlockList();
172       return syncnum;
173     }
174     return 0;
175   }
176   
177   /**
178    * adds the ClientHandle to the list - if it is not unique, then the
179    * ClientHandle object is modified to make it unique in the list. returns the
180    * clientNumber for the client in the session.
181    * 
182    * @param me
183    * @return
184    */
185
186   public int addClient(ClientHandle me) {
187     syncnum = addClient(me, true);
188     unlockList();
189     return syncnum;
190   }
191
192   /**
193    * removes 'me' from the session ClientList without complaint if 'me' isn't in the clientList already.
194    * @param me client handle to be removed
195    * @param clientlock existing lock passed from watcher.
196    */
197   public void removeClient(ClientHandle me, Lock clientlock) {
198     int mynum=-1;
199     if (lockList(clientlock)) {
200       ClientHandle[] clients = retrieveClientHandles();
201       if (clients != null) {
202         if ((syncnum<=0 || syncnum>clients.length) || clients[syncnum-1]!=me) {
203           for (int i = 0, j = clients.length; i < j; i++) 
204             if (clients[i].equals(me)) {
205               mynum=i;
206               break;
207             }
208         } else {
209           mynum=syncnum-1;
210         }
211         if (mynum>-1) {
212           ClientHandle[] newlist = new ClientHandle[clients.length - 1];
213           for (int k=0,i = 0, j = clients.length; i < j; i++)
214             if (i!=mynum)
215               newlist[k++] = clients[i];
216           if (!putClientList(newlist))
217             throw new Error("Failed to write new clientList!"); // failed to put the clientList to disk.
218         }
219       }
220       unlockList();
221     } else {
222       throw new Error("Couldn't get lock for "+((filelist==null) ? "Unitialised filelist in ClientsFile" : filelist.getAbsolutePath()));
223     }
224   }
225   /**
226    * Adds a ClientHandle to the ClientList file - optionally disambiguating 
227    * the ClientHandle (modifes the URN). 
228    * Note: Caller is left to release the lock on the ClientList.
229    * @param me
230    * @param disambiguate -
231    *          flag indicating if the URN for me should be disambiguated to
232    *          differentiate between sessions.
233    * @return index of clientHandle in new list, or -1-position of existing
234    *         clientHandle (if disambiguate is true)
235    */
236   protected int addClient(ClientHandle me, boolean disambiguate) {
237     int newclient = 0;
238     if (lockList()) {
239       ClientHandle[] clients = retrieveClientHandles();
240       if (me.getClientUrn()==null) {
241         // TODO: move this into ClientUrn as a standard form method.
242         me.setClientUrn("vamsas://"+me.getClientName()+":"+me.getVersion()+"/");
243       }
244       if (clients == null) {
245         clients = new ClientHandle[1];
246         clients[0] = me;
247         newclient = 1;
248       } else {
249         int k = 0;
250         for (int i = 0, j = clients.length; i < j; i++) {
251           if (clients[i].equals(me)) {
252             if (disambiguate) {
253               while (clients[i].equals(me)) {
254                 me.setClientUrn(me.getClientUrn() + k++); // TODO: make a better
255                                                           // disambiguation of
256                                                           // urn.
257               }
258             } else {
259               // will not write the ambiguous clientHandle to disk, just return
260               // its index.
261               return -1 - i;
262             }
263           }
264         }
265         int i, j;
266         ClientHandle[] newlist = new ClientHandle[clients.length + 1];
267         for (i = 0, j = clients.length; i < j; i++)
268           newlist[i] = clients[i];
269         newlist[j] = me;
270         clients = newlist;
271         newclient = j+1;
272       }
273       if (!putClientList(clients))
274         return 0; // failed to put the clientList to disk.
275     }
276     return newclient;
277   }
278
279   /**
280    * safely writes clients array to the file referred to by filelist.
281    * 
282    * @param clients
283    * @return true if successful write. Throws Errors otherwise.
284    */
285   protected boolean putClientList(ClientHandle[] clients) {
286     if (lockList()) {
287       File templist = null;
288       try {
289         templist = File.createTempFile(filelist.getName(),".old", filelist.getParentFile());
290         FileOutputStream tos = new FileOutputStream(templist);
291         tos.getChannel().transferFrom(listlock.rafile.getChannel(), 0,
292             listlock.rafile.length());
293         tos.close();
294       } catch (FileNotFoundException e1) {
295         System.err.println("Can't create temp file for clientlist");
296         e1.printStackTrace(System.err);
297       } catch (IOException e1) {
298         System.err
299             .println("Error when copying content to temp file for clientlist");
300         e1.printStackTrace(System.err);
301       }
302       if (templist != null) {
303         try {
304           listlock.rafile.setLength(0);
305           ObjectOutputStream os = new ObjectOutputStream(
306               new BufferedOutputStream(new FileOutputStream(this.filelist)));
307           os.writeObject(clients);
308           os.close();
309           // All done - remove the backup.
310           templist.delete();
311           templist = null;
312         } catch (Exception e) {
313           if (templist != null) {
314             System.err
315                 .println("Serious - problems writing to filelist. Backup in "
316                     + templist.getAbsolutePath());
317             e.printStackTrace(System.err);
318           }
319         }
320       } else {
321         throw new Error(
322             "Couldn't create backup of the clientList before writing to it!");
323       }
324     } else {
325       throw new Error("Could not lock the clientList: "
326           + ((filelist == null) ? "Unitialized ClientsFile"
327               : " failed to get lock on " + filelist.getAbsolutePath()));
328     }
329     // successful!
330     return true;
331   }
332 }