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.EventHandler;
10 import java.beans.PropertyChangeEvent;
11 import java.beans.PropertyChangeListener;
12 import java.beans.PropertyChangeSupport;
13 import java.io.BufferedReader;
15 import java.io.IOException;
16 import java.net.MalformedURLException;
17 import java.util.Hashtable;
18 import java.util.Vector;
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
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.client.picking.IPickManager;
32 import uk.ac.vamsas.objects.core.ApplicationData;
33 import uk.ac.vamsas.objects.core.Entry;
34 import uk.ac.vamsas.objects.core.LockFile;
35 import uk.ac.vamsas.objects.core.VamsasDocument;
36 import uk.ac.vamsas.objects.utils.AppDataReference;
37 import uk.ac.vamsas.objects.utils.ProvenanceStuff;
38 import uk.ac.vamsas.objects.utils.document.VersionEntries;
43 public class SimpleClient implements IClient {
45 private static Log log = LogFactory.getLog(SimpleClient.class);
47 protected UserHandle user = null;
49 protected SessionUrn session = null;
50 protected VamsasSession _session;
51 protected ClientHandle client = null;
52 protected EventGeneratorThread evgen = null;
53 protected ClientDocument cdocument = null;
55 * object hash table that persists in each client holding vorbaIds and hash values after a document write
57 protected Hashtable extantobjects=null;
59 * construct a transient IdFactory instance - this should last only as long as the
60 * SimpleClient object holds the lock on the vamsas document being created/manipulated.
63 private IdFactory makeVorbaIdFactory() {
64 if (extantobjects==null)
65 extantobjects=new Hashtable();
66 return new IdFactory(getSessionHandle(), client, user, extantobjects);
70 * construct SimpleClient for user, client and VamsasSession directory
71 * use the SimpleClientFactory rather than this constructor directly.
76 protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess) throws InvalidSessionUrnException {
77 // TODO: validate user/client/session
82 log.debug("Creating new session for "+_session);
83 session = new SessionUrn(_session);
84 log.debug("Creating new Event Generator");
85 evgen = new EventGeneratorThread(_session, this, handlers);
86 /*} catch (MalformedURLException e) {
87 log.error("Couldn't form a valid SessionUrn object!",e);
88 throw new InvalidSessionUrnException(_session.toString());
90 log.debug("SimpleClient constructed for session "+session.getSessionUrn());
94 * construct new session by importing objects from an existing vamsas document
98 * @param importingArchive
99 * @throws Exception IOExceptions for Session IO problems, and general Exception if importing document is invalid.
101 protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess, File importingArchive) throws Exception {
102 this(user, client, sess);
103 VamsasArchive sessdoc = _session.getVamsasDocument();
105 VamsasArchiveReader odoc = new VamsasArchiveReader(importingArchive);
106 SimpleDocument sdoc = new SimpleDocument(makeVorbaIdFactory());
107 VamsasDocument doc = sdoc.getVamsasDocument(odoc);
108 sessdoc.putVamsasDocument(doc, sdoc.vorba);
109 sessdoc.closeArchive();
110 log.debug("Imported new vamsas data from "+importingArchive);
111 } catch (Exception e) {
112 sessdoc.cancelArchive();
113 // write a dummy iohandler
114 _session.slog.info("Exception when importing document data from "+importingArchive);
115 log.warn("While importing session data from existing archive in "+importingArchive, e);
116 throw new Exception("Failed to import data from "+importingArchive, e);
122 * LATER: check that build substitution variables are correct
123 * @see uk.ac.vamsas.client.IClient#getAbout()
125 public String getAbout() {
126 return new String("VORBA SimpleClient version $version$ build $build$");
132 * @see uk.ac.vamsas.client.IClient#getSessionUrn()
134 public String getSessionUrn() {
135 return session.getSessionUrn();
141 * @see uk.ac.vamsas.client.IClient#getSessionHandle()
143 public SessionHandle getSessionHandle() {
144 // TODO: eliminate SessionHandle ? need to refactor interfaces.
145 SessionHandle sh = new SessionHandle(session.getSessionUrn());
152 * @see uk.ac.vamsas.client.IClient#getClientHandle()
154 public ClientHandle getClientHandle() {
161 * @see uk.ac.vamsas.client.IClient#getUserHandle()
163 public UserHandle getUserHandle() {
168 * @return user field for a provenance entry
170 protected String getProvenanceUser() {
171 return new String(user.getFullName());
174 * construct a provenance entry for this client with the specified action string.
176 * @return properly completed provenance entry
178 protected Entry getProvenanceEntry(String action) {
179 Entry prov = ProvenanceStuff.newProvenanceEntry(client.getClientUrn(), getProvenanceUser(), action);
182 private Hashtable handlers = initHandlers();
184 private Vector listeners = new Vector();
187 * make all the PropertyChangeSupport objects for the
188 * events described in uk.ac.vamsas.client.Event
191 private static Hashtable initHandlers() {
192 Hashtable events = new Hashtable();
193 java.util.Iterator evt = Events.EventList.iterator();
194 while (evt.hasNext()) {
195 Object ths = evt.next();
196 events.put(ths, (Object) new PropertyChangeSupport(ths));
204 * @see uk.ac.vamsas.client.IClient#addDocumentUpdateHandler(java.util.EventListener)
206 public void addDocumentUpdateHandler(PropertyChangeListener evt) {
207 this.addVorbaEventHandler(Events.DOCUMENT_UPDATE, evt);
209 boolean finalized=false;
210 private void haltPickmanager() {
211 if (pickmanager!=null) {
212 final SimpleClient dying=this;
215 SimpleClient.log.debug("Stopping pickManager..");
216 dying.pickmanager.shutdown();
217 SimpleClient.log.debug("pickManager halted.");
225 * @see uk.ac.vamsas.client.IClient#finalizeClient()
227 public void finalizeClient() {
229 throw new Error("VAMSAS Client Implementation Error: Finalized called twice for same client instance.");
231 // mark this instance as finalized
234 // TODO: determine if this is last client in session
236 // TODO: raise events like : ((lst_client && document.request.to.close), (client_finalization), (
237 evgen._raise(Events.CLIENT_FINALIZATION, null, this,null);
238 // if (handlers.containsKey(Events.))
239 // if (handlers.containsKey(Events.CLIENT_FINALIZATION))
240 // deregister listeners.
241 log.debug("Stopping pickManager");
243 log.debug("Stopping EventGenerator..");
244 evgen.stopWatching();
245 SimpleClient.log.debug("EventGenerator halted.");
246 log.debug("Deregistering Client");
247 _session.removeClient(this);
248 log.debug("finalization Complete.");
254 * @see uk.ac.vamsas.client.IClient#getClientDocument()
256 public IClientDocument getClientDocument() throws IOException {
257 log.debug("getClientDocument");
258 if (cdocument!=null) {
259 // cdocument is non-nill if the ClientDocument.finalise() method hasn't been called.
262 evgen.disableDocumentWatch();
263 VamsasArchive va = null;
265 // LATER: bail out if it takes too long to get the lock ?
266 va = _session.getVamsasDocument();
268 catch (IOException e) {
269 throw new IOException("Failed to get lock on session document");
271 VamsasDocument doc=null;
272 IdFactory vorba = null;
273 // TODO: LATER: reduce size of vorba ids generated from these parameters to IdFactory (mainly sessionHandle rationalization ?)
275 va.setVorba(vorba=makeVorbaIdFactory());
276 // if session currently holds data - read it in - or get a dummy
277 log.debug("Accessing document");
279 va.getVamsasDocument(getProvenanceUser(),
280 "created new session document.", null);
282 log.debug("Successfully retrieved document.");
284 log.error("Unexpectedly retrieved null document!");
286 catch (Exception e) {
287 log.error("Failed to get session document for session directory '"+_session.sessionDir+"'", e);
288 throw new IOException("Failed to get session document for session directory '"+_session.sessionDir+"'");
290 // Construct the IClientDocument instance
292 cdocument = new ClientDocument(doc, va, vorba, this);
298 * @throws Errors for invalid newdoc parameter
299 * @see uk.ac.vamsas.client.IClient#updateDocument(uk.ac.vamsas.client.IClientDocument)
301 public void updateDocument(IClientDocument newdoc) {
302 log.debug("updateDocument:");
303 // Check validity of simpleclient instance and that it holds a lock on the session's document
304 if (!(newdoc instanceof ClientDocument)) {
305 throw new Error("Invalid IClientDocument passsed to SimpleClient.");
308 throw new Error("Client Error - updateDocument() called before getClientDocument() on this SimpleClient instance.");
309 if (newdoc!=cdocument)
310 throw new Error("Client Error - SimpleClient.updateDocument() can only take the IClientDocument instance returned from SimpleClient.getClientDocument()");
312 if (evgen.isDocumentWatchEnabled())
313 throw new Error("Client Error - or Library Bug : Document watcher still enabled whilst ClientDocument instance exists.");
315 if (!cdocument.isModified()) {
316 // client document is silently got rid of, with no session update events.
317 if (log.isDebugEnabled())
318 log.debug("updateDocument for "+session.getSessionUrn()+" with unmodified IClientDocument (skipping the write)");
321 boolean updated=cdocument.updateSessionDocument();
323 log.warn("Session document did not update properly for session directory "+_session.sessionDir);
324 // cdocument.archive.cancelArchive(); // LATER: could be done - would need to prevent updateSessionDocument closing the iohandler.
325 _session.slog.warn("Session Document updated but may not be valid (false return from org.vamsas.simpleclient.ClientDocument.updateSessionDocument()");
327 log.debug("Document update successful.");
329 _session.setUnsavedFlag();
331 catch (IOException e) {
332 log.warn("IO Problems when updating document!",e);
333 _session.slog.error("IO problems when attempting to update document.");
336 // garbage collect the ClientDocument instance.
338 log.debug("Finalizing ClientDocument instance.");
339 cdocument.finalize();
340 } catch (Throwable e) {
341 log.error("Exception when trying to garbage collect ClientDocument for "+session.getSessionUrn(), e);
343 cdocument = null; // this is probably done by finalize
346 _session.unlockVamsasDocument();
347 evgen.enableDocumentWatch();
348 } catch (IOException e) {
349 log.warn("IO Problems when releasing lock on session document!",e);
350 _session.slog.error("IO problems when attempting to release lock on session document.");
357 * @see uk.ac.vamsas.client.IClient#storeDocument(java.io.File)
359 public void storeDocument(File location) {
361 throw new Error("Vamsas Client API Usage Error: storeDocument called with null location.");
362 log.debug("StoreDocument to "+location);
363 // write storeDocument file to inform other clients that they should raise
364 Lock vamlock = evgen.want_to_store();
365 // Events.DOCUMENT_FINALIZEAPPDATA
367 _session.writeVamsasDocument(location, vamlock);
368 _session.clearUnsavedFlag();
369 } catch (Exception e) {
370 log.warn("Exception whilst trying to store document in "+location,e);
379 * @see uk.ac.vamsas.client.IClient#addVorbaEventHandler(java.lang.String,
380 * java.beans.PropertyChangeListener)
382 public void addVorbaEventHandler(String EventChain, PropertyChangeListener evt) {
383 if (handlers.containsKey(EventChain)) {
384 log.debug("Adding new handler for "+EventChain);
386 ((PropertyChangeSupport) (handler = handlers.get(EventChain)))
387 .addPropertyChangeListener(evt);
388 listeners.add(handler);
389 listeners.add((Object) evt);
394 * @see uk.ac.vamsas.client.IClient#pollUpdate()
396 public void pollUpdate() {
397 log.debug("pollUpdate");
399 log.warn("pollUpdate called on incomplete SimpleClient object.");
403 if (!evgen.isWatcherAlive()) {
404 log.warn("pollUpdate called before joinSession() - trying to do this.");
407 } catch (Exception e) {
408 log.error("Unexpected exception on default call to joinSession",e);
412 //TODO ensure event generator robustly handles these interrupts.
413 log.debug("interrrupting event generator.");
414 evgen.interruptWatching();
415 log.debug("interrrupted event generator.");
419 * @see uk.ac.vamsas.client.IClient#joinSession()
421 public void joinSession() throws Exception {
422 log.debug("Joining Session.");
423 // start the EventGenerator thread.
425 log.warn("joinSession called on incomplete SimpleClient object.");
428 if (evgen.isWatcherAlive())
429 throw new Error("Join session called twice for the same SimpleClient (IClient instance).");
430 evgen.startWatching();
431 if (evgen.isWatcherAlive())
432 log.debug("Started EventGenerator thread.");
434 log.warn("Failed to start EventGenerator thread.");
435 throw new Exception("Failed to start event generator thread - client cannot be instantiated.");
437 if (evgen.countHandlersFor(Events.DOCUMENT_CREATE)>0) {
438 //TODO: LATER: is this application connecting to a newly created session document ?
439 //evgen.raise(Events.DOCUMENT_CREATE);
447 * @see uk.ac.vamsas.client.IClient#importDocument(java.io.File)
449 public void importDocument(File location) {
450 // TODO LATER: implement SimpleClient.importDocument()
451 log.error("importDocument is not yet implemented for a SimpleClient Session.");
454 public IObjectUpdate getUpdateHandler(Class rootObject) {
455 // TODO Auto-generated method stub
459 public IObjectUpdate[] getUpdateHandlers() {
460 // TODO Auto-generated method stub
464 public void removeUpdateHandler(Class rootObject) {
465 // TODO Auto-generated method stub
469 public void setUpdateHandler(IObjectUpdate handler) {
470 // TODO Auto-generated method stub
475 * retrieves the current VamsasSession to which belong the client
476 * @return the _session
478 protected VamsasSession get_session() {
479 return this._session;
481 SimplePickManager pickmanager=null;
483 * @see uk.ac.vamsas.client.IClient#getPickManager()
485 public IPickManager getPickManager() {
490 private void createPickManager() {
491 if (pickmanager==null){
492 // TODO: Construct PickManager for session using details from sessionURN!
493 log.debug("Creating PickManager (not from sessionURN yet)");
494 pickmanager = new SimplePickManager(new uk.ac.vamsas.client.picking.SocketManager());