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