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