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