added a getter for the pickhandler interface in IClient
[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   }
221   
222   /*
223    * (non-Javadoc)
224    * 
225    * @see uk.ac.vamsas.client.IClient#getClientDocument()
226    */
227   public IClientDocument getClientDocument() throws IOException {
228     if (cdocument!=null) {
229       // cdocument is non-nill if the ClientDocument.finalise() method hasn't been called.
230       return cdocument;
231     }
232     VamsasArchive va = null;
233     try {
234       // LATER: bail out if it takes too long to get the lock ?
235       va = _session.getVamsasDocument();
236     }
237     catch (IOException e) {
238       throw new IOException("Failed to get lock on session document");
239     }
240     VamsasDocument doc=null;
241     IdFactory vorba = null;
242     // TODO: reduce size of vorba ids generated from these parameters to IdFactory (mainly sessionHandle rationalization ?)
243     try {
244       va.setVorba(vorba=makeVorbaIdFactory());
245       // if session currently holds data - read it in - or get a dummy
246       _session.slog.debug("Accessing document");
247       doc = 
248         va.getVamsasDocument(getProvenanceUser(),
249             "created new session document.", null);
250       if (doc!=null)
251         _session.slog.debug("Successfully retrieved document.");
252       else
253         log.error("Unexpectedly retrieved null document!");
254     }
255     catch (Exception e) {
256       log.error("Failed to get session document for session directory '"+_session.sessionDir+"'", e);
257       throw new IOException("Failed to get session document for session directory '"+_session.sessionDir+"'");
258     }
259     // Construct the IClientDocument instance
260     
261     ClientDocument cdoc = new ClientDocument(doc, va, vorba, this);
262     return cdoc;
263   }
264   
265   /*
266    * (non-Javadoc)
267    * @throws Errors for invalid newdoc parameter
268    * @see uk.ac.vamsas.client.IClient#updateDocument(uk.ac.vamsas.client.IClientDocument)
269    */
270   public void updateDocument(IClientDocument newdoc) {
271     if (!(newdoc instanceof ClientDocument)) {
272       throw new Error("Invalid IClientDocument instance for SimpleClient.");
273     }
274     if (cdocument==null)
275       throw new Error("Client Error - updateDocument() called before getClientDocument().");
276     if (newdoc!=cdocument)
277       throw new Error("Client Error - SimpleClient.updateDocument() can only take the IClientDocument instance returned from SimpleClient.getClientDocument()");
278     if (!cdocument.isModified()) {
279       if (log.isDebugEnabled())
280         log.debug("updateDocument for "+session.getSessionUrn()+" with unmodified IClientDocument.");
281     } else {
282       try {
283         if (!cdocument.updateSessionDocument()) {
284           log.warn("Session document did not update properly for session directory "+_session.sessionDir);
285           // cdocument.archive.cancelArchive(); // LATER: could be done - would need to prevent updateSessionDocument closing the archive.
286           _session.slog.warn("Session Document updated but may not be valid (false return from org.vamsas.simpleclient.ClientDocument.updateSessionDocument()");
287         }
288       }
289       catch (IOException e) {
290         log.warn("IO Problems when updating document!",e);
291         _session.slog.error("IO problems when attempting to update document.");
292       }
293     }
294     // garbage collect the ClientDocument instance.
295     try {
296       cdocument.finalize();
297
298     } catch (Throwable e) {
299       log.error("Exception when trying to garbage collect ClientDocument for "+session.getSessionUrn(), e);
300     }
301     cdocument = null; // this is probably done by finalize
302   }
303   
304   /*
305    * (non-Javadoc)
306    * 
307    * @see uk.ac.vamsas.client.IClient#storeDocument(java.io.File)
308    */
309   public void storeDocument(File location) {
310     
311     // write storeDocument file to inform other clients that they should raise
312     Lock vamlock = evgen.want_to_store();
313     // Events.DOCUMENT_FINALIZEAPPDATA
314     try {
315       _session.writeVamsasDocument(location, vamlock);
316       _session.clearUnsavedFlag();
317     } catch (Exception e) {
318       log.warn("Exception whilst trying to store document in "+location,e);
319     }
320     
321     vamlock.release();
322   }
323   
324   /*
325    * (non-Javadoc)
326    * 
327    * @see uk.ac.vamsas.client.IClient#addVorbaEventHandler(java.lang.String,
328    *      java.beans.PropertyChangeListener)
329    */
330   public void addVorbaEventHandler(String EventChain, PropertyChangeListener evt) {
331     if (handlers.containsKey(EventChain)) {
332       Object handler;
333       ((PropertyChangeSupport) (handler = handlers.get(EventChain)))
334       .addPropertyChangeListener(evt);
335       listeners.add(handler);
336       listeners.add((Object) evt);
337     }
338   }
339   
340   /* (non-Javadoc)
341    * @see uk.ac.vamsas.client.IClient#pollUpdate()
342    */
343   public void pollUpdate() {
344     
345     if (evgen==null) {
346       log.warn("pollUpdate called on incomplete SimpleClient object.");
347       return;
348     }
349     
350     if (!evgen.isAlive()) {
351       log.warn("pollUpdate called before joinSession() - trying to do this.");
352       try {
353         joinSession();
354       } catch (Exception e) {
355         log.error("Unexpected exception on default call to joinSession",e);
356       }
357     }
358     
359     //TODO ensure event generator robustly handles these interrupts.
360     log.debug("interrrupting event generator.");
361     evgen.interrupt();
362     log.debug("interrrupted event generator.");
363   }
364   
365   /* (non-Javadoc)
366    * @see uk.ac.vamsas.client.IClient#joinSession()
367    */
368   public void joinSession() throws Exception {
369     // start the EventGenerator thread.
370     if (evgen==null) {
371       log.warn("joinSession called on incomplete SimpleClient object.");
372       return;
373     }
374     if (evgen.isAlive())
375       throw new Error("Join session called twice for the same SimpleClient (IClient instance).");
376     evgen.start();
377     if (evgen.isAlive())
378       log.debug("Started EventGenerator thread.");
379     else {
380       log.warn("Failed to start EventGenerator thread.");
381       throw new Exception("Failed to start event generator thread - client cannot be instantiated.");
382     }
383     if (evgen.countHandlersFor(Events.DOCUMENT_CREATE)>0) {
384       //TODO: is this application connecting to a newly created session document ?
385       //evgen.raise(Events.DOCUMENT_CREATE);
386     }
387   }
388   
389   
390   
391   /* (non-Javadoc)
392    * @see uk.ac.vamsas.client.IClient#importDocument(java.io.File)
393    */
394   public void importDocument(File location) {
395     // TODO LATER: implement SimpleClient.importDocument()
396     log.error("importDocument is not yet implemented for a SimpleClient Session.");
397   }
398
399   public IObjectUpdate getUpdateHandler(Class rootObject) {
400     // TODO Auto-generated method stub
401     return null;
402   }
403
404   public IObjectUpdate[] getUpdateHandlers() {
405     // TODO Auto-generated method stub
406     return null;
407   }
408
409   public void removeUpdateHandler(Class rootObject) {
410     // TODO Auto-generated method stub
411     
412   }
413
414   public void setUpdateHandler(IObjectUpdate handler) {
415     // TODO Auto-generated method stub
416     
417   }
418
419   /* (non-Javadoc)
420    * @see uk.ac.vamsas.client.IClient#getPickManager()
421    */
422   public IPickManager getPickManager() {
423     // TODO Auto-generated method stub
424     return null;
425   }
426 }