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