2 * Created on 15-Sep-2005
4 * TODO To change the template for this generated file go to
5 * Window - Preferences - Java - Code Style - Code Templates
7 package uk.ac.vamsas.client.simpleclient;
9 import java.beans.PropertyChangeListener;
10 import java.beans.PropertyChangeSupport;
12 import java.io.IOException;
13 import java.util.Hashtable;
14 import java.util.Vector;
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
19 import uk.ac.vamsas.client.ClientHandle;
20 import uk.ac.vamsas.client.Events;
21 import uk.ac.vamsas.client.IClient;
22 import uk.ac.vamsas.client.IClientDocument;
23 import uk.ac.vamsas.client.IObjectUpdate;
24 import uk.ac.vamsas.client.InvalidSessionUrnException;
25 import uk.ac.vamsas.client.SessionHandle;
26 import uk.ac.vamsas.client.UserHandle;
27 import uk.ac.vamsas.client.picking.IPickManager;
28 import uk.ac.vamsas.objects.core.Entry;
29 import uk.ac.vamsas.objects.core.VamsasDocument;
30 import uk.ac.vamsas.objects.utils.ProvenanceStuff;
35 public class SimpleClient implements IClient {
37 private static Log log = LogFactory.getLog(SimpleClient.class);
39 protected UserHandle user = null;
41 protected SessionUrn session = null;
42 protected VamsasSession _session;
43 protected ClientHandle client = null;
44 protected EventGeneratorThread evgen = null;
45 protected ClientDocument cdocument = null;
47 * object hash table that persists in each client holding vorbaIds and hash values after a document write
49 protected Hashtable extantobjects=null;
51 * construct a transient IdFactory instance - this should last only as long as the
52 * SimpleClient object holds the lock on the vamsas document being created/manipulated.
55 private IdFactory makeVorbaIdFactory() {
56 if (extantobjects==null)
57 extantobjects=new Hashtable();
58 return new IdFactory(getSessionHandle(), client, user, extantobjects);
62 * construct SimpleClient for user, client and VamsasSession directory
63 * use the SimpleClientFactory rather than this constructor directly.
68 protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess) throws InvalidSessionUrnException {
69 // TODO: validate user/client/session
74 log.debug("Creating new session for "+_session);
75 session = new SessionUrn(_session);
76 log.debug("Creating new Event Generator");
77 evgen = new EventGeneratorThread(_session, this, handlers);
78 /*} catch (MalformedURLException e) {
79 log.error("Couldn't form a valid SessionUrn object!",e);
80 throw new InvalidSessionUrnException(_session.toString());
82 log.debug("SimpleClient constructed for session "+session.getSessionUrn());
86 * construct new session by importing objects from an existing vamsas document
90 * @param importingArchive
91 * @throws Exception IOExceptions for Session IO problems, and general Exception if importing document is invalid.
93 protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess, File importingArchive) throws Exception {
94 this(user, client, sess);
95 VamsasArchive sessdoc = _session.getVamsasDocument();
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 log.debug("Imported new vamsas data from "+importingArchive);
103 } catch (Exception e) {
104 sessdoc.cancelArchive();
105 // write a dummy iohandler
106 _session.slog.info("Exception when importing document data from "+importingArchive);
107 log.warn("While importing session data from existing archive in "+importingArchive, e);
108 throw new Exception("Failed to import data from "+importingArchive, e);
114 * LATER: check that build substitution variables are correct
115 * @see uk.ac.vamsas.client.IClient#getAbout()
117 public String getAbout() {
118 return new String("VORBA SimpleClient version $version$ build $build$");
124 * @see uk.ac.vamsas.client.IClient#getSessionUrn()
126 public String getSessionUrn() {
127 return session.getSessionUrn();
133 * @see uk.ac.vamsas.client.IClient#getSessionHandle()
135 public SessionHandle getSessionHandle() {
136 // TODO: eliminate SessionHandle ? need to refactor interfaces.
137 SessionHandle sh = new SessionHandle(session.getSessionUrn());
144 * @see uk.ac.vamsas.client.IClient#getClientHandle()
146 public ClientHandle getClientHandle() {
153 * @see uk.ac.vamsas.client.IClient#getUserHandle()
155 public UserHandle getUserHandle() {
160 * @return user field for a provenance entry
162 protected String getProvenanceUser() {
163 return new String(user.getFullName());
166 * construct a provenance entry for this client with the specified action string.
168 * @return properly completed provenance entry
170 protected Entry getProvenanceEntry(String action) {
171 Entry prov = ProvenanceStuff.newProvenanceEntry(client.getClientUrn(), getProvenanceUser(), action);
174 private Hashtable handlers = initHandlers();
176 private Vector listeners = new Vector();
179 * make all the PropertyChangeSupport objects for the
180 * events described in uk.ac.vamsas.client.Event
183 private static Hashtable initHandlers() {
184 Hashtable events = new Hashtable();
185 java.util.Iterator evt = Events.EventList.iterator();
186 while (evt.hasNext()) {
187 Object ths = evt.next();
188 events.put(ths, (Object) new PropertyChangeSupport(ths));
196 * @see uk.ac.vamsas.client.IClient#addDocumentUpdateHandler(java.util.EventListener)
198 public void addDocumentUpdateHandler(PropertyChangeListener evt) {
199 this.addVorbaEventHandler(Events.DOCUMENT_UPDATE, evt);
201 boolean finalized=false;
202 private void haltPickmanager() {
203 if (pickmanager!=null) {
204 final SimpleClient dying=this;
207 SimpleClient.log.debug("Stopping pickManager..");
208 dying.pickmanager.shutdown();
209 SimpleClient.log.debug("pickManager halted.");
217 * @see uk.ac.vamsas.client.IClient#finalizeClient()
219 public void finalizeClient() {
221 throw new Error("VAMSAS Client Implementation Error: Finalized called twice for same client instance.");
223 // mark this instance as finalized
226 // TODO: determine if this is last client in session
228 // TODO: raise events like : ((lst_client && document.request.to.close), (client_finalization), (
229 evgen._raise(Events.CLIENT_FINALIZATION, null, this,null);
230 // if (handlers.containsKey(Events.))
231 // if (handlers.containsKey(Events.CLIENT_FINALIZATION))
232 // deregister listeners.
233 log.debug("Stopping pickManager");
236 log.debug("Deregistering Client");
237 _session.removeClient(this);
238 //log.debug("Stopping EventGenerator..");
239 //evgen.stopWatching();
240 SimpleClient.log.debug("EventGenerator halted.");
241 this.cdocument = null;
242 log.debug("finalization Complete.");
248 * @see uk.ac.vamsas.client.IClient#getClientDocument()
250 public IClientDocument getClientDocument() throws IOException {
251 log.debug("getClientDocument");
252 if (cdocument!=null) {
253 // cdocument is non-nill if the ClientDocument.finalise() method hasn't been called.
256 evgen.disableDocumentWatch();
257 VamsasArchive va = null;
259 // LATER: bail out if it takes too long to get the lock ?
260 va = _session.getVamsasDocument();
262 catch (IOException e) {
263 throw new IOException("Failed to get lock on session document");
265 VamsasDocument doc=null;
266 IdFactory vorba = null;
267 // TODO: LATER: reduce size of vorba ids generated from these parameters to IdFactory (mainly sessionHandle rationalization ?)
269 va.setVorba(vorba=makeVorbaIdFactory());
270 // if session currently holds data - read it in - or get a dummy
271 log.debug("Accessing document");
273 va.getVamsasDocument(getProvenanceUser(),
274 "created new session document.", null);
276 log.debug("Successfully retrieved document.");
278 log.error("Unexpectedly retrieved null document!");
280 catch (Exception e) {
281 log.error("Failed to get session document for session directory '"+_session.sessionDir+"'", e);
282 throw new IOException("Failed to get session document for session directory '"+_session.sessionDir+"'");
284 // Construct the IClientDocument instance
286 cdocument = new ClientDocument(doc, va, vorba, this);
292 * @throws Errors for invalid newdoc parameter
293 * @see uk.ac.vamsas.client.IClient#updateDocument(uk.ac.vamsas.client.IClientDocument)
295 public void updateDocument(IClientDocument newdoc) {
296 log.debug("updateDocument:");
297 // Check validity of simpleclient instance and that it holds a lock on the session's document
298 if (!(newdoc instanceof ClientDocument)) {
299 throw new Error("Invalid IClientDocument passsed to SimpleClient.");
302 throw new Error("Client Error - updateDocument() called before getClientDocument() on this SimpleClient instance.");
303 if (newdoc!=cdocument)
304 throw new Error("Client Error - SimpleClient.updateDocument() can only take the IClientDocument instance returned from SimpleClient.getClientDocument()");
306 if (evgen.isDocumentWatchEnabled())
307 throw new Error("Client Error - or Library Bug : Document watcher still enabled whilst ClientDocument instance exists.");
309 if (!cdocument.isModified()) {
310 // client document is silently got rid of, with no session update events.
311 if (log.isDebugEnabled())
312 log.debug("updateDocument for "+session.getSessionUrn()+" with unmodified IClientDocument (skipping the write)");
315 boolean updated=cdocument.updateSessionDocument();
317 log.warn("Session document did not update properly for session directory "+_session.sessionDir);
318 // cdocument.archive.cancelArchive(); // LATER: could be done - would need to prevent updateSessionDocument closing the iohandler.
319 _session.slog.warn("Session Document updated but may not be valid (false return from org.vamsas.simpleclient.ClientDocument.updateSessionDocument()");
321 log.debug("Document update successful.");
323 _session.setUnsavedFlag();
325 catch (IOException e) {
326 log.warn("IO Problems when updating document!",e);
327 _session.slog.error("IO problems when attempting to update document.");
330 // garbage collect the ClientDocument instance.
332 log.debug("Finalizing ClientDocument instance.");
333 cdocument.finalize();
334 } catch (Throwable e) {
335 log.error("Exception when trying to garbage collect ClientDocument for "+session.getSessionUrn(), e);
337 cdocument = null; // this is probably done by finalize
340 _session.unlockVamsasDocument();
341 evgen.enableDocumentWatch();
342 } catch (IOException e) {
343 log.warn("IO Problems when releasing lock on session document!",e);
344 _session.slog.error("IO problems when attempting to release lock on session document.");
351 * @see uk.ac.vamsas.client.IClient#storeDocument(java.io.File)
353 public void storeDocument(File location) {
355 throw new Error("Vamsas Client API Usage Error: storeDocument called with null location.");
356 log.debug("StoreDocument to "+location);
357 // write storeDocument file to inform other clients that they should raise
358 Lock vamlock = evgen.want_to_store();
359 // Events.DOCUMENT_FINALIZEAPPDATA
361 _session.writeVamsasDocument(location, vamlock);
362 _session.clearUnsavedFlag();
363 } catch (Exception e) {
364 log.warn("Exception whilst trying to store document in "+location,e);
372 * @see uk.ac.vamsas.client.IClient#addVorbaEventHandler(java.lang.String,
373 * java.beans.PropertyChangeListener)
375 public void addVorbaEventHandler(String EventChain, PropertyChangeListener evt) {
376 if (handlers.containsKey(EventChain)) {
377 log.debug("Adding new handler for "+EventChain);
379 ((PropertyChangeSupport) (handler = handlers.get(EventChain)))
380 .addPropertyChangeListener(evt);
381 listeners.add(handler);
382 listeners.add((Object) evt);
387 * @see uk.ac.vamsas.client.IClient#pollUpdate()
389 public void pollUpdate() {
390 log.debug("pollUpdate");
392 log.warn("pollUpdate called on incomplete SimpleClient object.");
396 if (!evgen.isWatcherAlive()) {
397 log.warn("pollUpdate called before joinSession() - trying to do this.");
400 } catch (Exception e) {
401 log.error("Unexpected exception on default call to joinSession",e);
405 //TODO ensure event generator robustly handles these interrupts.
406 log.debug("interrrupting event generator.");
407 evgen.interruptWatching();
408 log.debug("interrrupted event generator.");
412 * @see uk.ac.vamsas.client.IClient#joinSession()
414 public void joinSession() throws Exception {
415 log.debug("Joining Session.");
416 // start the EventGenerator thread.
418 log.warn("joinSession called on incomplete SimpleClient object.");
421 if (evgen.isWatcherAlive())
422 throw new Error("Join session called twice for the same SimpleClient (IClient instance).");
423 evgen.startWatching();
424 if (evgen.isWatcherAlive())
425 log.debug("Started EventGenerator thread.");
427 log.warn("Failed to start EventGenerator thread.");
428 throw new Exception("Failed to start event generator thread - client cannot be instantiated.");
430 if (evgen.countHandlersFor(Events.DOCUMENT_CREATE)>0) {
431 //TODO: LATER: is this application connecting to a newly created session document ?
432 //evgen.raise(Events.DOCUMENT_CREATE);
440 * @see uk.ac.vamsas.client.IClient#importDocument(java.io.File)
442 public void importDocument(File location) {
443 // TODO LATER: implement SimpleClient.importDocument()
444 log.error("importDocument is not yet implemented for a SimpleClient Session.");
447 this._session.setVamsasDocument(location);
448 } catch (IOException e) {
449 log.error("importDocument failed.");
453 public IObjectUpdate getUpdateHandler(Class rootObject) {
454 // TODO Auto-generated method stub
458 public IObjectUpdate[] getUpdateHandlers() {
459 // TODO Auto-generated method stub
463 public void removeUpdateHandler(Class rootObject) {
464 // TODO Auto-generated method stub
468 public void setUpdateHandler(IObjectUpdate handler) {
469 // TODO Auto-generated method stub
474 * retrieves the current VamsasSession to which belong the client
475 * @return the _session
477 protected VamsasSession get_session() {
478 return this._session;
480 SimplePickManager pickmanager=null;
482 * @see uk.ac.vamsas.client.IClient#getPickManager()
484 public IPickManager getPickManager() {
489 private void createPickManager() {
490 if (pickmanager==null){
491 // TODO: Construct PickManager for session using details from sessionURN!
492 log.debug("Creating PickManager (not from sessionURN yet)");
493 pickmanager = new SimplePickManager(new uk.ac.vamsas.client.picking.SocketManager());