739cf581958a0a50cb60a62519fb5321ed6ac7db
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / SimpleClient.java
1 /*
2  * Created on 15-Sep-2005
3  *
4  * TODO To change the template for this generated file go to
5  * Window - Preferences - Java - Code Style - Code Templates
6  */
7 package uk.ac.vamsas.client.simpleclient;
8
9 import java.beans.EventHandler;
10 import java.beans.PropertyChangeEvent;
11 import java.beans.PropertyChangeListener;
12 import java.beans.PropertyChangeSupport;
13 import java.io.BufferedReader;
14 import java.io.File;
15 import java.io.IOException;
16 import java.net.MalformedURLException;
17 import java.util.Hashtable;
18 import java.util.Vector;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22
23 import uk.ac.vamsas.client.ClientHandle;
24 import uk.ac.vamsas.client.Events;
25 import uk.ac.vamsas.client.IClient;
26 import uk.ac.vamsas.client.IClientDocument;
27 import uk.ac.vamsas.client.IObjectUpdate;
28 import uk.ac.vamsas.client.InvalidSessionUrnException;
29 import uk.ac.vamsas.client.SessionHandle;
30 import uk.ac.vamsas.client.UserHandle;
31 import uk.ac.vamsas.client.picking.IPickManager;
32 import uk.ac.vamsas.objects.core.ApplicationData;
33 import uk.ac.vamsas.objects.core.Entry;
34 import uk.ac.vamsas.objects.core.LockFile;
35 import uk.ac.vamsas.objects.core.VamsasDocument;
36 import uk.ac.vamsas.objects.utils.AppDataReference;
37 import uk.ac.vamsas.objects.utils.ProvenanceStuff;
38 import uk.ac.vamsas.objects.utils.document.VersionEntries;
39
40 /**
41  * @author jimp
42  */
43 public class SimpleClient implements IClient {
44   
45   private static Log log = LogFactory.getLog(SimpleClient.class);
46   
47   protected UserHandle user = null;
48   
49   protected SessionUrn session = null;
50   protected VamsasSession _session;
51   protected ClientHandle client = null;
52   protected EventGeneratorThread evgen = null;
53   protected ClientDocument cdocument = null;
54   /**
55    * object hash table that persists in each client holding vorbaIds and hash values after a document write
56    */
57   protected Hashtable extantobjects=null;
58   /**
59    * construct a transient IdFactory instance - this should last only as long as the 
60    * SimpleClient object holds the lock on the vamsas document being created/manipulated.
61    * @return
62    */
63   private IdFactory makeVorbaIdFactory() {
64     return new IdFactory(getSessionHandle(), client, user);
65   }
66   
67   /**
68    * construct SimpleClient for user, client and VamsasSession directory
69    * use the SimpleClientFactory rather than this constructor directly. 
70    * @param user
71    * @param client
72    * @param sess
73    */
74   protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess) throws InvalidSessionUrnException {
75     // TODO: validate user/client/session
76     _session = sess;
77     this.user = user;
78     this.client = client;
79     //try {
80       session = new SessionUrn(_session);
81     /*} catch (MalformedURLException e) {
82       log.error("Couldn't form a valid SessionUrn object!",e);
83       throw new InvalidSessionUrnException(_session.toString());
84     }*/
85   }
86   /**
87    * construct new session by importing objects from an existing vamsas document
88    * @param user
89    * @param client
90    * @param sess
91    * @param importingArchive
92    * @throws Exception IOExceptions for Session IO problems, and general Exception if importing document is invalid.
93    */
94   protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess, File importingArchive) throws Exception {
95     this(user, client, sess);
96     VamsasArchive sessdoc = _session.getVamsasDocument();
97     try {
98       VamsasArchiveReader odoc = new VamsasArchiveReader(importingArchive);
99       SimpleDocument sdoc = new SimpleDocument(makeVorbaIdFactory());
100       VamsasDocument doc = sdoc.getVamsasDocument(odoc);
101       sessdoc.putVamsasDocument(doc, sdoc.vorba);
102       sessdoc.closeArchive();
103     } catch (Exception e) {
104       sessdoc.cancelArchive();
105       // write a dummy archive
106       _session.slog.info("Exception when importing document data from "+importingArchive);
107       throw new Exception("Failed to import data from "+importingArchive, e);
108     }
109   }
110   
111   /*
112    * (non-Javadoc)
113    * LATER: check that build substitution variables are correct
114    * @see uk.ac.vamsas.client.IClient#getAbout()
115    */
116   public String getAbout() {
117     return new String("VORBA SimpleClient version $version$ build $build$");
118   }
119   
120   /*
121    * (non-Javadoc)
122    * 
123    * @see uk.ac.vamsas.client.IClient#getSessionUrn()
124    */
125   public String getSessionUrn() {
126     return session.getSessionUrn();
127   }
128   
129   /*
130    * (non-Javadoc)
131    * 
132    * @see uk.ac.vamsas.client.IClient#getSessionHandle()
133    */
134   public SessionHandle getSessionHandle() {
135     // TODO: eliminate SessionHandle ? need to refactor interfaces.
136     SessionHandle sh = new SessionHandle(session.getSessionUrn());
137     return sh;
138   }
139   
140   /*
141    * (non-Javadoc)
142    * 
143    * @see uk.ac.vamsas.client.IClient#getClientHandle()
144    */
145   public ClientHandle getClientHandle() {
146     return client;
147   }
148   
149   /*
150    * (non-Javadoc)
151    * 
152    * @see uk.ac.vamsas.client.IClient#getUserHandle()
153    */
154   public UserHandle getUserHandle() {
155     return user;
156   }
157   /**
158    * 
159    * @return user field for a provenance entry
160    */
161   protected String getProvenanceUser() {
162     return new String(user.getFullName());
163   }
164   /**
165    * construct a provenance entry for this client with the specified action string.
166    * @param action
167    * @return properly completed provenance entry
168    */
169   protected Entry getProvenanceEntry(String action) {
170     Entry prov = ProvenanceStuff.newProvenanceEntry(client.getClientUrn(), getProvenanceUser(), action);
171     return prov;
172   }
173   private Hashtable handlers = initHandlers();
174   
175   private Vector listeners = new Vector();
176   
177   /**
178    * make all the PropertyChangeSupport objects for the
179    * events described in uk.ac.vamsas.client.Event
180    * @return
181    */
182   private static Hashtable initHandlers() {
183     Hashtable events = new Hashtable();
184     java.util.Iterator evt = Events.EventList.iterator();
185     while (evt.hasNext()) {
186       Object ths = evt.next();
187       events.put(ths, (Object) new PropertyChangeSupport(ths));
188     }
189     return events;
190   }
191   
192   /*
193    * (non-Javadoc)
194    * 
195    * @see uk.ac.vamsas.client.IClient#addDocumentUpdateHandler(java.util.EventListener)
196    */
197   public void addDocumentUpdateHandler(PropertyChangeListener evt) {
198     if (handlers.containsKey(Events.DOCUMENT_UPDATE)) {
199       Object handler;
200       ((PropertyChangeSupport) (handler = handlers.get(Events.DOCUMENT_UPDATE)))
201       .addPropertyChangeListener(evt);
202       listeners.add(handler);
203       listeners.add((Object) evt);
204     }
205   }
206   boolean finalized=false;
207   /*
208    * (non-Javadoc)
209    * 
210    * @see uk.ac.vamsas.client.IClient#finalizeClient()
211    */
212   public void finalizeClient() {
213     // TODO: determine if this is last client in session
214     // TODO: raise events like : ((lst_client && document.request.to.close), (client_finalization), (
215     
216     // if (handlers.containsKey(Events.))
217     // if (handlers.containsKey(Events.CLIENT_FINALIZATION))
218     // deregister listeners.
219     // mark this instance as finalized
220     this._session.removeClient(this);
221   }
222   
223   /*
224    * (non-Javadoc)
225    * 
226    * @see uk.ac.vamsas.client.IClient#getClientDocument()
227    */
228   public IClientDocument getClientDocument() throws IOException {
229     if (cdocument!=null) {
230       // cdocument is non-nill if the ClientDocument.finalise() method hasn't been called.
231       return cdocument;
232     }
233     VamsasArchive va = null;
234     try {
235       // LATER: bail out if it takes too long to get the lock ?
236       va = _session.getVamsasDocument();
237     }
238     catch (IOException e) {
239       throw new IOException("Failed to get lock on session document");
240     }
241     VamsasDocument doc=null;
242     IdFactory vorba = null;
243     // TODO: reduce size of vorba ids generated from these parameters to IdFactory (mainly sessionHandle rationalization ?)
244     try {
245       va.setVorba(vorba=makeVorbaIdFactory());
246       // if session currently holds data - read it in - or get a dummy
247       _session.slog.debug("Accessing document");
248       doc = 
249         va.getVamsasDocument(getProvenanceUser(),
250             "created new session document.", null);
251       if (doc!=null)
252         _session.slog.debug("Successfully retrieved document.");
253       else
254         log.error("Unexpectedly retrieved null document!");
255     }
256     catch (Exception e) {
257       log.error("Failed to get session document for session directory '"+_session.sessionDir+"'", e);
258       throw new IOException("Failed to get session document for session directory '"+_session.sessionDir+"'");
259     }
260     // Construct the IClientDocument instance
261     
262     ClientDocument cdoc = new ClientDocument(doc, va, vorba, this);
263     return cdoc;
264   }
265   
266   /*
267    * (non-Javadoc)
268    * @throws Errors for invalid newdoc parameter
269    * @see uk.ac.vamsas.client.IClient#updateDocument(uk.ac.vamsas.client.IClientDocument)
270    */
271   public void updateDocument(IClientDocument newdoc) {
272     if (!(newdoc instanceof ClientDocument)) {
273       throw new Error("Invalid IClientDocument instance for SimpleClient.");
274     }
275     if (cdocument==null)
276       throw new Error("Client Error - updateDocument() called before getClientDocument().");
277     if (newdoc!=cdocument)
278       throw new Error("Client Error - SimpleClient.updateDocument() can only take the IClientDocument instance returned from SimpleClient.getClientDocument()");
279     if (!cdocument.isModified()) {
280       if (log.isDebugEnabled())
281         log.debug("updateDocument for "+session.getSessionUrn()+" with unmodified IClientDocument.");
282     } else {
283       try {
284         if (!cdocument.updateSessionDocument()) {
285           log.warn("Session document did not update properly for session directory "+_session.sessionDir);
286           // cdocument.archive.cancelArchive(); // LATER: could be done - would need to prevent updateSessionDocument closing the archive.
287           _session.slog.warn("Session Document updated but may not be valid (false return from org.vamsas.simpleclient.ClientDocument.updateSessionDocument()");
288         }
289       }
290       catch (IOException e) {
291         log.warn("IO Problems when updating document!",e);
292         _session.slog.error("IO problems when attempting to update document.");
293       }
294     }
295     // garbage collect the ClientDocument instance.
296     try {
297       cdocument.finalize();
298
299     } catch (Throwable e) {
300       log.error("Exception when trying to garbage collect ClientDocument for "+session.getSessionUrn(), e);
301     }
302     cdocument = null; // this is probably done by finalize
303   }
304   
305   /*
306    * (non-Javadoc)
307    * 
308    * @see uk.ac.vamsas.client.IClient#storeDocument(java.io.File)
309    */
310   public void storeDocument(File location) {
311     
312     // write storeDocument file to inform other clients that they should raise
313     Lock vamlock = evgen.want_to_store();
314     // Events.DOCUMENT_FINALIZEAPPDATA
315     try {
316       _session.writeVamsasDocument(location, vamlock);
317       _session.clearUnsavedFlag();
318     } catch (Exception e) {
319       log.warn("Exception whilst trying to store document in "+location,e);
320     }
321     
322     vamlock.release();
323   }
324   
325   /*
326    * (non-Javadoc)
327    * 
328    * @see uk.ac.vamsas.client.IClient#addVorbaEventHandler(java.lang.String,
329    *      java.beans.PropertyChangeListener)
330    */
331   public void addVorbaEventHandler(String EventChain, PropertyChangeListener evt) {
332     if (handlers.containsKey(EventChain)) {
333       Object handler;
334       ((PropertyChangeSupport) (handler = handlers.get(EventChain)))
335       .addPropertyChangeListener(evt);
336       listeners.add(handler);
337       listeners.add((Object) evt);
338     }
339   }
340   
341   /* (non-Javadoc)
342    * @see uk.ac.vamsas.client.IClient#pollUpdate()
343    */
344   public void pollUpdate() {
345     
346     if (evgen==null) {
347       log.warn("pollUpdate called on incomplete SimpleClient object.");
348       return;
349     }
350     
351     if (!evgen.isAlive()) {
352       log.warn("pollUpdate called before joinSession() - trying to do this.");
353       try {
354         joinSession();
355       } catch (Exception e) {
356         log.error("Unexpected exception on default call to joinSession",e);
357       }
358     }
359     
360     //TODO ensure event generator robustly handles these interrupts.
361     log.debug("interrrupting event generator.");
362     evgen.interrupt();
363     log.debug("interrrupted event generator.");
364   }
365   
366   /* (non-Javadoc)
367    * @see uk.ac.vamsas.client.IClient#joinSession()
368    */
369   public void joinSession() throws Exception {
370     // start the EventGenerator thread.
371     if (evgen==null) {
372       log.warn("joinSession called on incomplete SimpleClient object.");
373       return;
374     }
375     if (evgen.isAlive())
376       throw new Error("Join session called twice for the same SimpleClient (IClient instance).");
377     evgen.start();
378     if (evgen.isAlive())
379       log.debug("Started EventGenerator thread.");
380     else {
381       log.warn("Failed to start EventGenerator thread.");
382       throw new Exception("Failed to start event generator thread - client cannot be instantiated.");
383     }
384     if (evgen.countHandlersFor(Events.DOCUMENT_CREATE)>0) {
385       //TODO: is this application connecting to a newly created session document ?
386       //evgen.raise(Events.DOCUMENT_CREATE);
387     }
388   }
389   
390   
391   
392   /* (non-Javadoc)
393    * @see uk.ac.vamsas.client.IClient#importDocument(java.io.File)
394    */
395   public void importDocument(File location) {
396     // TODO LATER: implement SimpleClient.importDocument()
397     log.error("importDocument is not yet implemented for a SimpleClient Session.");
398   }
399
400   public IObjectUpdate getUpdateHandler(Class rootObject) {
401     // TODO Auto-generated method stub
402     return null;
403   }
404
405   public IObjectUpdate[] getUpdateHandlers() {
406     // TODO Auto-generated method stub
407     return null;
408   }
409
410   public void removeUpdateHandler(Class rootObject) {
411     // TODO Auto-generated method stub
412     
413   }
414
415   public void setUpdateHandler(IObjectUpdate handler) {
416     // TODO Auto-generated method stub
417     
418   }
419
420   /**
421    * retrieves the current VamsasSession to which belong the client
422    * @return the _session
423    */
424   protected VamsasSession get_session() {
425     return this._session;
426   }
427
428   /* (non-Javadoc)
429    * @see uk.ac.vamsas.client.IClient#getPickManager()
430    */
431   public IPickManager getPickManager() {
432     // TODO Auto-generated method stub
433     return null;
434   }
435 }