IObjectUpdate interface introduction.
[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.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    * construct a transient IdFactory instance - this should last only as long as the 
54    * SimpleClient object holds the lock on the vamsas document being created/manipulated.
55    * @return
56    */
57   private IdFactory makeVorbaIdFactory() {
58     return new IdFactory(getSessionHandle(), client, user);
59   }
60   
61   /**
62    * construct SimpleClient for user, client and VamsasSession directory
63    * use the SimpleClientFactory rather than this constructor directly. 
64    * @param user
65    * @param client
66    * @param sess
67    */
68   protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess) throws InvalidSessionUrnException {
69     // TODO: validate user/client/session
70     _session = sess;
71     this.user = user;
72     this.client = client;
73     try {
74       session = new SessionUrn(_session);
75     } catch (MalformedURLException e) {
76       log.error("Couldn't form a valid SessionUrn object!",e);
77       throw new InvalidSessionUrnException(_session.toString());
78     }
79   }
80   /**
81    * construct new session by importing objects from an existing vamsas document
82    * @param user
83    * @param client
84    * @param sess
85    * @param importingArchive
86    * @throws Exception IOExceptions for Session IO problems, and general Exception if importing document is invalid.
87    */
88   protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess, File importingArchive) throws Exception {
89     this(user, client, sess);
90     VamsasArchive sessdoc = _session.getVamsasDocument();
91     try {
92       VamsasArchiveReader odoc = new VamsasArchiveReader(importingArchive);
93       SimpleDocument sdoc = new SimpleDocument(makeVorbaIdFactory());
94       VamsasDocument doc = sdoc.getVamsasDocument(odoc);
95       sessdoc.putVamsasDocument(doc, sdoc.vorba);
96       sessdoc.closeArchive();
97     } catch (Exception e) {
98       sessdoc.cancelArchive();
99       // write a dummy archive
100       _session.slog.info("Exception when importing document data from "+importingArchive);
101       throw new Exception("Failed to import data from "+importingArchive, e);
102     }
103   }
104   
105   /*
106    * (non-Javadoc)
107    * LATER: check that build substitution variables are correct
108    * @see org.vamsas.client.IClient#getAbout()
109    */
110   public String getAbout() {
111     return new String("VORBA SimpleClient version $version$ build $build$");
112   }
113   
114   /*
115    * (non-Javadoc)
116    * 
117    * @see org.vamsas.client.IClient#getSessionUrn()
118    */
119   public String getSessionUrn() {
120     return session.getSessionUrn();
121   }
122   
123   /*
124    * (non-Javadoc)
125    * 
126    * @see org.vamsas.client.IClient#getSessionHandle()
127    */
128   public SessionHandle getSessionHandle() {
129     // TODO: eliminate SessionHandle ? need to refactor interfaces.
130     SessionHandle sh = new SessionHandle(session.getSessionUrn());
131     return sh;
132   }
133   
134   /*
135    * (non-Javadoc)
136    * 
137    * @see org.vamsas.client.IClient#getClientHandle()
138    */
139   public ClientHandle getClientHandle() {
140     return client;
141   }
142   
143   /*
144    * (non-Javadoc)
145    * 
146    * @see org.vamsas.client.IClient#getUserHandle()
147    */
148   public UserHandle getUserHandle() {
149     return user;
150   }
151   /**
152    * 
153    * @return user field for a provenance entry
154    */
155   protected String getProvenanceUser() {
156     return new String(user.getFullName());
157   }
158   /**
159    * construct a provenance entry for this client with the specified action string.
160    * @param action
161    * @return properly completed provenance entry
162    */
163   protected Entry getProvenanceEntry(String action) {
164     Entry prov = ProvenanceStuff.newProvenanceEntry(client.getClientUrn(), 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
393   public IObjectUpdate getUpdateHandler(Class rootObject) {
394     // TODO Auto-generated method stub
395     return null;
396   }
397
398   public IObjectUpdate[] getUpdateHandlers() {
399     // TODO Auto-generated method stub
400     return null;
401   }
402
403   public void removeUpdateHandler(Class rootObject) {
404     // TODO Auto-generated method stub
405     
406   }
407
408   public void setUpdateHandler(IObjectUpdate handler) {
409     // TODO Auto-generated method stub
410     
411   }
412 }