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