use SessionFile constructor and added todo about java.nio file locking.
[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  * Handler for the clientsFile within a vamsas session thread.
21  * @author jim 
22  */
23 public class ClientsFile extends SessionFile {
24   /**
25    * number of my client in list - passed back when a client 
26    * is added to list, and used (if valid) for quickly 
27    * looking up presence of client handle in the list.
28    */
29   private int syncnum = 1;
30
31   public ClientsFile(File filelist) throws IOException {
32     super(filelist);
33     if (!this.sessionFile.exists())
34       this.sessionFile.createNewFile();
35   }
36
37   /**
38    * internal method for getting clientList - ensures a lock has been made but
39    * does not release it.
40    * 
41    * @return list of clients
42    */
43   private ClientHandle[] retrieveClientHandles() {
44     if (lockFile()) {
45       try {
46         ClientHandle[] clients=null;
47         if (this.fileLock.rafile.length()>0) {
48           ObjectInputStream is = new ObjectInputStream(new BufferedInputStream(
49               new java.io.FileInputStream(sessionFile)));
50           Object o;
51           o=is.readObject();
52           if (o!=null) {
53             try {
54               clients = (ClientHandle[]) o;
55             }
56             catch (Exception e) {
57               System.err.println("Garbage in the clientHandle list "+this.sessionFile);
58             }
59           }
60         }
61         return clients;
62       } catch (FileNotFoundException e) {
63         // TODO Auto-generated catch block
64         e.printStackTrace(System.err);
65       } catch (Exception e) {
66         e.printStackTrace(System.err);
67       }
68     }
69     return null;
70   }
71   /**
72    * get the clientList from the file. May return null if lock failed!
73    * @return clientList
74    */
75   public ClientHandle[] retrieveClientList() {
76     if (lockFile()) {
77       ClientHandle[] clients = retrieveClientHandles();
78       unlockFile();
79       return clients;
80     }
81     return null;
82   }
83   /**
84    * get list from the locked ClientList.
85    * @param extantlock
86    * @return clientList or null if lock failed (or file was empty)
87    */
88   public ClientHandle[] retrieveClientList(Lock extantlock) {
89     if (lockFile(extantlock)) {
90       ClientHandle[] clients = retrieveClientHandles();
91       unlockFile();
92       return clients;
93     }
94     return null;
95   }
96   /**
97    * adds clientHandle me to the clientList under an existing lock extantLock.
98    * @param me
99    * @param extantLock
100    * @return client index in list or 0 if lock was invalid or addClient operation failed.
101    */
102   public int addClient(ClientHandle me, Lock extantLock) {
103     return addClient(me, true, extantLock);
104   }
105   
106   /**
107    * adds clientHandle me to the clientList under an existing lock.
108    * @param me - clientHandle
109    * @param disambig - if true then add will fail if an identical clientHandle already exists
110    * @param extantLock - existing lock
111    * @return client index in list or 0 if addClient (or the lock) failed.
112    */
113   
114   public int addClient(ClientHandle me, boolean disambig, Lock extantLock) {
115     if (lockFile(extantLock)) {
116       syncnum = addClient(me, disambig);
117       unlockFile();
118       return syncnum;
119     }
120     return 0;
121   }
122   
123   /**
124    * adds the ClientHandle to the list - if it is not unique, then the
125    * ClientHandle object is modified to make it unique in the list. returns the
126    * clientNumber for the client in the session.
127    * 
128    * @param me
129    * @return
130    */
131
132   public int addClient(ClientHandle me) {
133     syncnum = addClient(me, true);
134     unlockFile();
135     return syncnum;
136   }
137
138   /**
139    * removes 'me' from the session ClientList without complaint if 'me' isn't in the clientList already.
140    * @param me client handle to be removed
141    * @param clientlock existing lock passed from watcher.
142    */
143   public void removeClient(ClientHandle me, Lock clientlock) {
144     int mynum=-1;
145     if (lockFile(clientlock)) {
146       ClientHandle[] clients = retrieveClientHandles();
147       if (clients != null) {
148         if ((syncnum<=0 || syncnum>clients.length) || clients[syncnum-1]!=me) {
149           for (int i = 0, j = clients.length; i < j; i++) 
150             if (clients[i].equals(me)) {
151               mynum=i;
152               break;
153             }
154         } else {
155           mynum=syncnum-1;
156         }
157         if (mynum>-1) {
158           ClientHandle[] newlist = new ClientHandle[clients.length - 1];
159           for (int k=0,i = 0, j = clients.length; i < j; i++)
160             if (i!=mynum)
161               newlist[k++] = clients[i];
162           if (!putClientList(newlist))
163             throw new Error("Failed to write new clientList!"); // failed to put the clientList to disk.
164         }
165       }
166       unlockFile();
167     } else {
168       throw new Error("Couldn't get lock for "+((sessionFile==null) ? "Unitialised sessionFile in ClientsFile" : sessionFile.getAbsolutePath()));
169     }
170   }
171   /**
172    * Adds a ClientHandle to the ClientList file - optionally disambiguating 
173    * the ClientHandle (modifes the URN). 
174    * Note: Caller is left to release the lock on the ClientList.
175    * @param me
176    * @param disambiguate -
177    *          flag indicating if the URN for me should be disambiguated to
178    *          differentiate between sessions.
179    * @return index of clientHandle in new list, or -1-position of existing
180    *         clientHandle (if disambiguate is true)
181    */
182   protected int addClient(ClientHandle me, boolean disambiguate) {
183     int newclient = 0;
184     if (lockFile()) {
185       ClientHandle[] clients = retrieveClientHandles();
186       if (me.getClientUrn()==null) {
187         // TODO: move this into ClientUrn as a standard form method.
188         me.setClientUrn("vamsas://"+me.getClientName()+":"+me.getVersion()+"/");
189       }
190       if (clients == null) {
191         clients = new ClientHandle[1];
192         clients[0] = me;
193         newclient = 1;
194       } else {
195         int k = 0;
196         for (int i = 0, j = clients.length; i < j; i++) {
197           if (clients[i].equals(me)) {
198             if (disambiguate) {
199               while (clients[i].equals(me)) {
200                 me.setClientUrn(me.getClientUrn() + k++); // TODO: make a better
201                                                           // disambiguation of
202                                                           // urn.
203               }
204             } else {
205               // will not write the ambiguous clientHandle to disk, just return
206               // its index.
207               return -1 - i;
208             }
209           }
210         }
211         int i, j;
212         ClientHandle[] newlist = new ClientHandle[clients.length + 1];
213         for (i = 0, j = clients.length; i < j; i++)
214           newlist[i] = clients[i];
215         newlist[j] = me;
216         clients = newlist;
217         newclient = j+1;
218       }
219       if (!putClientList(clients))
220         return 0; // failed to put the clientList to disk.
221     }
222     return newclient;
223   }
224   
225   /**
226    * safely writes clients array to the file referred to by sessionFile.
227    * 
228    * @param clients
229    * @return true if successful write. Throws Errors otherwise.
230    */
231   protected boolean putClientList(ClientHandle[] clients) {
232     if (lockFile()) {
233       File templist = backupSessionFile();
234       if (templist != null) {
235         try {
236           fileLock.rafile.setLength(0);
237           ObjectOutputStream os = new ObjectOutputStream(
238               new BufferedOutputStream(new FileOutputStream(this.sessionFile)));
239           os.writeObject(clients);
240           os.close();
241           // All done - remove the backup.
242           templist.delete();
243           templist = null;
244         } catch (Exception e) {
245           if (templist != null) {
246             System.err
247                 .println("Serious - problems writing to sessionFile. Backup in "
248                     + templist.getAbsolutePath());
249             e.printStackTrace(System.err);
250           }
251         }
252       } else {
253         throw new Error(
254             "Couldn't create backup of the clientList before writing to it!");
255       }
256     } else {
257       throw new Error("Could not lock the clientList: "
258           + ((sessionFile == null) ? "Unitialized ClientsFile"
259               : " failed to get lock on " + sessionFile.getAbsolutePath()));
260     }
261     // successful!
262     return true;
263   }
264 }