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