import uk.ac.vamsas.test.objects.Core;
/**
- * Maintains a collection of vamsas objects, appdatas and states, and provides api for a SimpleClient's client.
+ * Maintains a collection of vamsas objects, appdatas and states,
+ * and provides api for a SimpleClient's client.
+ * TODO: test and migrate ArchiveClient.getAppData methods to here and retest in ExampleApplication
* @author jimp
*/
public class ClientDocument extends uk.ac.vamsas.client.ClientDocument implements IClientDocument {
private static Log log = LogFactory.getLog(ClientDocument.class);
private VamsasDocument doc;
protected SimpleClient sclient;
- protected VamsasArchive archive = null;
+ protected VamsasArchive iohandler = null;
/**
* indicate if new data has been incorporated
*/
public boolean isModified() {
return isModified;
}
- private Vector updatedObjects=null;
/**
*
- * prepare Application-side dataset from the vamsas Document archive
+ * prepare Application-side dataset from the vamsas Document iohandler
* @param doc - the dataset
* @param docHandler - the sessionFile IO handler
* @param Factory - the source of current and new vorbaIds
/**
- * prepare Application-side dataset from the vamsas Document archive
+ * prepare Application-side dataset from the vamsas Document iohandler
*/
this.sclient = sclient;
- archive = docHandler;
+ iohandler = docHandler;
this.doc = doc;
- updatedObjects=null; /// TODO: correct this line
+ _VamsasRoots=doc.getVAMSAS();
}
/*
* internal reference to single copy of Document Roots array
*/
private VAMSAS[] _VamsasRoots=null;
- /*
+
+ protected void updateDocumentRoots() {
+ if (doc==null) {
+ log.error("updateDocumentRoots called on null document. Probably an implementation error.");
+ return;
+ }
+ if (isModified) {
+ if (_VamsasRoots!=null) {
+ doc.setVAMSAS(_VamsasRoots);
+ _VamsasRoots=null;
+ }
+ }
+ }
+/*
* (non-Javadoc)
* LATER: currently there is only one Vector of roots ever passed to client - decide if this is correct (means this is not thread safe and may behave unexpectedly)
* @see uk.ac.vamsas.client.IClientDocument#getVamsasRoots()
log.debug("Null document for getVamsasRoots(), returning null");
return null;
}
- if (archive==null) {
+ if (iohandler==null) {
// LATER: decide on read-only status of ClientDocument object
log.warn("getVamsasRoots() called on possibly read-only document.");
}
if (roots == null) {
// Make a new one to return to client to get filled.
_VamsasRoots = new VAMSAS[] { new VAMSAS() };
+ registerObject(_VamsasRoots[0]);
// Do provenance now. just in case.
doc.getProvenance().addEntry(sclient.getProvenanceEntry("Created new document root [id="+_VamsasRoots[0].getId()+"]"));
doc.addVAMSAS(_VamsasRoots[0]);
return -1;
if (docRoots==null || docRoots.length==0)
return -1;
- String r_id = root.getId();
+ VorbaId d_id=null,r_id = root.getVorbaId();
for (int i=0,j=docRoots.length; i<j; i++)
- if (docRoots[i]==root || (docRoots[i]!=null && docRoots[i].getId().equals(r_id)))
+ if (docRoots[i]==root || (docRoots[i]!=null && (d_id=docRoots[i].getVorbaId())!=null) && d_id.equals(r_id))
return i;
return -1;
}
if (oldVersion==newVersion) {
// may be a virgin root element.
if (!newVersion.isRegistered())
- _registerObject(newVersion);
+ _registerObject(newVersion); // TODO: check - this call hasn't been tested.
// Should retrieve original version and compare - unless local hashes can be used to determine if resultSet has been truncated.
// just do internal validation for moment.
if (newVersion.isValid())
}
return false;
/**
- * LATER isValidUpdate : Ideally. we efficiently walk down, comparing hashes, to deal with merging and verifying provenance for objects
+ * LATER: MUCH LATER! - not needed for simple case and this routine shouldn't live in this class anymore
+ * isValidUpdate : Ideally. we efficiently walk down, comparing hashes, to deal with merging and verifying provenance for objects
// extract root objects
if (newroots != null) {
log.debug("addVamsasRoots called on null document.");
return;
}
- VAMSAS[] newroots = _combineRoots(new VAMSAS[] {newroot}, _VamsasRoots, this);
+ VAMSAS[] newroots = _combineRoots(new VAMSAS[] {newroot}, getVamsasRoots(), this);
_VamsasRoots = newroots;
}
log.warn("registerObjects called for null vamsasObjects hasharray.");
return null;
}
+ if (iohandler==null) {
+ log.warn("registerObjects called for read only document.");
+ return null;
+ }
+
if (unregistered!=null) {
VorbaId id = _registerObject(unregistered);
log.debug("Registered object - total of "+vamsasObjects.size()+" ids.");
* @see uk.ac.vamsas.client.IClientDocument#getClientAppdata()
*/
public IClientAppdata getClientAppdata() {
+ // TODO: getClientAppdata not tested in ArchiveClient mockup
+ log.error("TODO: TEST Client Appdata access methods");
if (doc==null) {
log.warn("getClientAppdata called on null document.");
return null;
* @return
*/
protected VamsasArchiveReader getVamsasArchiveReader() {
+ if (iohandler==null) {
+ log.error("Near fatal. Null VamsasArchive iohandler so can't get VamsasArchiveReader");
+ return null;
+ }
try {
- return archive.getOriginalArchiveReader();
+ log.info("TODO: test getVamsasArchiveReader");
+ return iohandler.getOriginalArchiveReader();
} catch (Exception e) {
log.warn("Unable to create OriginalArchiveReader!", e);
}
log.warn("updateSessionDocument called on null document.");
throw new java.io.IOException("Document is closed.");
}
- if (archive==null) {
- log.warn("updateSessionDocument called document archive handler.");
+ if (iohandler==null) {
+ log.warn("updateSessionDocument called document iohandler handler.");
throw new java.io.IOException("Document is closed.");
}
// update the VamsasDocument structure with any new appData's.
// try to update the sessionFile
log.debug("Attempting to update session "+sclient.session.getSessionUrn());
- if (scappd.isModified()) {
+ if (scappd!=null && scappd.isModified()) {
ClientHandle client = sclient.client;
UserHandle user = sclient.user;
scappd.closeForWriting();
scappd.appsGlobal.setDataReference(AppDataReference.uniqueAppDataReference(doc, sclient.client.getClientUrn()));
}
// LATER: use a switch to decide if the data should be written as a reference or as an embedded data chunk
- scappd.updateAnAppdataEntry(archive, scappd.appsGlobal, scappd.newAppData);
+ scappd.updateAnAppdataEntry(iohandler, scappd.appsGlobal, scappd.newAppData);
log.debug("...Successfully updated Global Appdata Entry.");
}
if (scappd.newUserData!=null && scappd.newUserData.sessionFile.exists()) {
}
appd.setDataReference(AppDataReference.uniqueAppDataReference(doc, sclient.client.getClientUrn()+safe_username));
}
- scappd.updateAnAppdataEntry(archive, scappd.usersData, scappd.newUserData);
+ scappd.updateAnAppdataEntry(iohandler, scappd.usersData, scappd.newUserData);
log.debug("...Successfully updated user AppData entry.");
}
}
+ try {
+ if (iohandler.transferRemainingAppDatas())
+ log.debug("Remaining appdatas were transferred.");
+ else
+ log.debug("No remaining appdatas were transferred. (Correct?)");
+ } catch (Exception e) {
+ log.error("While transferring remaining AppDatas", e);
+ }
log.debug("Updating Document...");
- // now update the document.
+ // now update the document. - this was basically the doUpdate method in test.ArchiveClient
+ updateDocumentRoots();
try {
- archive.putVamsasDocument(doc);
+ iohandler.putVamsasDocument(doc);
log.debug("Successfully written document entry.");
}
catch (Exception e) {
log.error("Marshalling error for vamsas document.",e);
docupdate = false; // pass on the (probable) object validation error
}
- if (archive.transferRemainingAppDatas())
- log.debug("Remaining appdatas were transferred.");
- else
- log.debug("No remaining appdatas were transferred. (Correct?)");
- archive.closeArchive();
+ iohandler.closeArchive();
+ iohandler=null; // so this method cannot be called again for this instance
log.debug("...successully finished and closed.");
return docupdate; // no errors ?
}
super.finalize();
}
public Vector getUpdatedObjects() {
- return updatedObjects;
+ // TODO: WALK through the document objects calling the update mechanism for each one, or just pass this vector back to client ?return updatedObjects;
+ return null;
}
}
package uk.ac.vamsas.client.simpleclient;
-import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Hashtable;
/**
* monitors watcher objects and generates events.
*/
-public class EventGeneratorThread extends Thread implements Runnable {
+public class EventGeneratorThread {
private static Log log = LogFactory.getLog(EventGeneratorThread.class);
private SimpleClient client;
private Hashtable handlers; // manager object
private VamsasSession session;
-
+ /**
+ * thread watching all the session's file objects
+ */
+ protected VamsasFileWatcherThread watchThread=null;
/**
- * list with all the clientHandles for the session
+ * Watcher element for list of all the clientHandles for the session
*/
- protected FileWatcher clientfile=null;
+ protected SessionFileWatcherElement clientfile=null;
/**
* the session's vamsasDocument
*/
- protected FileWatcher vamsasfile=null;
+ protected VamsasFileWatcherElement vamsasfile=null;
/**
* written to by client when its app calls storeDocument.
*/
- protected FileWatcher storeFile=null;
-
- private boolean watch=false;
-
+ protected SessionFileWatcherElement storeFile=null;
EventGeneratorThread(VamsasSession s, SimpleClient _client, Hashtable eventhandlers) {
if (eventhandlers==null || s==null || _client==null)
handlers = eventhandlers;
session = s;
client = _client;
- setName(s.sessionDir.getName());
+ log.debug("Creating VamsasFileWatcherThread.");
+ watchThread = new VamsasFileWatcherThread(this);
initWatchers();
}
private void initWatchers() {
- if (clientfile==null)
- clientfile = session.getClientWatcher();
- if (vamsasfile ==null)
- vamsasfile = session.getDocWatcher();
- if (storeFile == null)
- storeFile = session.getStoreWatcher();
- clientfile.setState();
- vamsasfile.setState();
- storeFile.setState();
+ if (clientfile==null) {
+ log.debug("Initializing clientfile Watcher");
+ clientfile = session.getClientWatcherElement();
+ clientfile.setHandler(new WatcherCallBack() {
+
+ public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {
+ // TODO Auto-generated method stub
+ return clientListChanged(watcher, lock);
+ }
+ });
+ watchThread.addElement(clientfile);
+ }
+ final EventGeneratorThread evgen=this;
+
+ if (vamsasfile ==null) {
+ log.debug("Initializing VamsasFileWatcher");
+ vamsasfile = new VamsasFileWatcherElement(session.vamArchive,
+ new WatcherCallBack() {
+ public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {
+ return evgen.documentChanged(lock);
+ }
+ });
+ watchThread.addElement(vamsasfile);
+ }
+ if (storeFile == null) {
+ storeFile = new SessionFileWatcherElement(session.getStoreDocFile(),
+ new WatcherCallBack() {
+ public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {
+ return evgen.storeDocRequest(lock);
+ }
+ });
+ log.debug("Initializing storeDocFile flag watcher");
+ }
+ /*
+ */
+ log.debug("Watchers inited.");
+ }
+ void _raise(String handlerEvent, String property, Object oldval, Object newval) {
+ PropertyChangeSupport h = (PropertyChangeSupport) handlers.get(handlerEvent);
+ if (h!=null) {
+ log.debug("Triggering:"+handlerEvent);
+ h.firePropertyChange(property, oldval, newval);
+ log.debug("Finished :"+handlerEvent);
+ } else
+ log.debug("No handlers for raised "+handlerEvent);
+ }
+ protected boolean storeDocRequest(Lock lock) {
+ if (log.isDebugEnabled())
+ log.debug("StoreDocRequest on "+(lock==null ? (lock.isLocked() ? "" : "Invalid ") : "Non-")+"Existing lock");
+ // TODO: define the storeFile semaphore mechanism : file exists - all clients inform their apps, and then the client that wrote the file should delete the file (it should hold the lock to it).
+ if (storeFile.getWatcher().exists) {
+ _raise(Events.DOCUMENT_FINALIZEAPPDATA, client.getSessionUrn(), null, client);
+ // expect client to write to document so update watcher state on return
+ vamsasfile.getWatcher().setState();
+ lock.release();
+ }
+ return true;
+ }
+
+ protected boolean documentChanged(Lock doclock) {
+ boolean continueWatching=true;
+ if (!block_document_updates) {
+ session.vamArchive.fileLock=doclock;
+ // TODO: decide if individual object update handlers are called as well as overall event handler
+ _raise(Events.DOCUMENT_UPDATE, client.getSessionUrn(), null, client);
+ if (log.isDebugEnabled()) {
+ log.debug("Finished handling a documentChanged event. Document is "+(client.cdocument==null ? "closed" : "open"));
+ }
+ /*try {
+ client._session.getVamsasDocument().closeArchive();
+ } catch (Exception e) {log.warn("Unexpected exception when closing document after update.",e);};
+ */
+ } else {
+ // TODO: check documentChanged */
+ log.debug("Ignoring documentChanged event for "+client.getSessionUrn());
+ }
+ return continueWatching;
}
boolean ownsf = false;
/**
* scans all watchers and fires changeEvents if necessary
* @return number of events generated.
*/
- private int checkforEvents() {
- Lock watchlock;
- //TODO : leave slog.info messages for the events that occur.
- int raised=0;
+ private boolean clientListChanged(WatcherElement clientfile, Lock watchlock) {
+ log.debug("ClientListChanged handler called for "+clientfile.getWatcher().getSubject());
// could make this general - but for now keep simple
- if ((watchlock=storeFile.getChangedState())!=null) {
- // TODO: define the storeFile semaphore mechanism : file exists - all clients inform their apps, and then the client that wrote the file should delete the file (it should hold the lock to it).
- if (storeFile.exists) {
- PropertyChangeSupport h = (PropertyChangeSupport) handlers.get(Events.DOCUMENT_FINALIZEAPPDATA);
- if (h!=null) {
- log.debug("Triggering DOCUMENT_FINALIZEAPPDATA");
- raised++;
- h.firePropertyChange(client.getSessionUrn(), null, client);
- // expect client to
- vamsasfile.setState();
- }
- }
- }
- if ((watchlock=clientfile.getChangedState())!=null) {
+ if (watchlock!=null) {
+ // TODO: compare new client list to old list version. is it changed ?
// see what happened to the clientfile - compare our internal version with the one in the file, or just send the updated list out...?
//
/**
* others).
public static final String CLIENT_FINALIZATION = "uk.ac.vamsas.client.events.clientFinalizationEvent";
*/ // again - as the test.
- raised++;
- }
- if ((watchlock=vamsasfile.getChangedState())!=null) {
-
- /**
- * Generated when a client has finished updating the document. Passes
- * applicationHandle of client so the updating client can recognise its own
- * updates.
- public static final String DOCUMENT_UPDATE = "uk.ac.vamsas.client.events.documentUpdateEvent";
- */
- // read apphandle from 'lastUpdate' session file.
- // pass apphandle name to appHandler ?
-
}
- /**
- * Generated when a new vamsas document is created (perhaps from some existing
- * Vamsas data) so an application may do its own data space initialization.
- * TODO: decide if this is called when an app is connected to a stored
- * session...
- public static final String DOCUMENT_CREATE = "uk.ac.vamsas.client.events.documentCreateEvent";
- */
- // check if this session's appInit flag is set - if not - generate event for this app.
- // prolly don't need this at the moment - when an app does getDocument it can to the initing then.
-
-
- /**
- * Generated prior to session Shutdown, after the last participating vamsas
- * client has finalized.
- * TODO: decide on purpose of this ? is this for benefit of multi-session Apps only ?
- public static final String SESSION_SHUTDOWN = "uk.ac.vamsas.client.events.SessionShutdownEvent";
- */
-
- /**
- * Generated for all clients when any client calls IClient.storeDocument() to
- * allow them to store any updates before an offline copy of the session is
- * created. Any client that handles this should call the
- * IClient.getDocument(), update and then IClient.updateDocument in the same
- * handler thread.
- * EventName: <Vamsas-session URN>
- * NewValue: uk.ac.vamsas.client.IClient for session.
- *
- public static final String DOCUMENT_FINALIZEAPPDATA = "uk.ac.vamsas.client.events.DocumentFinalizeAppData";
-*/
- // watch for finalization semaphore (last finalised sessionFile).
-
- /**
- * Generated by Vorba stub after the penultimate client makes a call to
- * closeDocument(). Sequence is as follows : 1. All other vamsas clients have
- * called closeDocument() 2. Final living client monitors closures, and
- * realises that it is last. 3. Final client generates event to prompt
- * associated application to inquire if the user wishes to save the document
- * for future reference.
- * * Any call to closeDocument in a thread other than the registered
- * EventListener will block until the RequestToClose handler has exited.
- *
- */
- // public static final String DOCUMENT_REQUESTTOCLOSE = "org.vamas.client.DocumentRequestToCloseEvent";
-
- return raised;
- }
-
- private void initEvents() {
-
+ return true;
}
/**
* Events raised by IClient and propagated to others in session
private boolean block_document_updates=false;
int STORE_WAIT=5; // how many units before we decide all clients have finalized their appdatas
-
+ private boolean in_want_to_store_phase=false;
/**
* client App requests offline storage of vamsas data.
* Call blocks whilst other apps do any appData finalizing
* @param STORE_WAIT indicates how lock the call will block for when nothing appears to be happening to the session.
*/
protected Lock want_to_store() {
- log.debug("Setting flag for document_update requests to be ignored");
- block_document_updates=true;
- log.debug("Waiting for other apps to do FinalizeApp handling.");
+ if (in_want_to_store_phase) {
+ log.error("client error: want_to_store called again before first call has completed.");
+ return null;
+ }
+ in_want_to_store_phase=true;
+ // TODO: test the storeDocumentRequest mechanism
+ /*/ watchThread.haltWatchers();
+ */
+ log.debug("Stopping document_update watcher");
+ vamsasfile.haltWatch();
+ // block_document_updates=true;
+ log.debug("Cleared flag for ignoring document_update requests");
+
+ log.debug("Sending Store Document Request");
try {
session.addStoreDocumentRequest(client.getClientHandle(), client.getUserHandle());
} catch (Exception e) {
log.warn("Whilst writing StoreDocumentRequest for "+client.getClientHandle().getClientUrn()+" "+client.getUserHandle(),
e);
- log.info("trying to continue.");
+ log.info("trying to continue after storeDocumentRequest exception.");
}
+ log.debug("Waiting for other apps to do FinalizeApp handling.");
// LATER: refine this semaphore process
// to make a robust signalling mechanism:
// app1 requests, app1..n do something (or don't - they may be dead),
// this probably needs two files - a request file,
// and a response file which is acknowledged by the app1 requestor for each app.
// eventually, no more responses are received for the request, and the app can then only continue with its store.
- int units = 0;
+ FileWatcher sfwatcher=session.getStoreWatcher();
+ FileWatcher vfwatcher=session.getDocWatcher();
+ int units = 0; // zero if updates occured over a sleep period
while (units<STORE_WAIT) {
- wait(1);
- if (storeFile.hasChanged() || vamsasfile.hasChanged())
+ try {
+ Thread.sleep(watchThread.WATCH_SLEEP);
+ } catch (InterruptedException e) {
+ log.debug("interrupted.");
+ }
+ if (sfwatcher.hasChanged() || vfwatcher.hasChanged()) {
units=0;
- else
- units++;
+ } else {
+ units++;
+ }
}
block_document_updates=false;
+ vamsasfile.enableWatch();
log.debug("Cleared flag for ignoring document_update requests");
// wait around again (until our own watcher has woken up and synchronized).
while (units<STORE_WAIT) {
- wait(1);
- if (storeFile.hasChanged() || vamsasfile.hasChanged())
+ try {
+ Thread.sleep(watchThread.WATCH_SLEEP);
+ } catch (InterruptedException e) {
+ log.debug("interrupted.");
+ }
+ if (sfwatcher.hasChanged() || vfwatcher.hasChanged())
units=0;
else
units++;
log.debug("finished waiting.");
+ in_want_to_store_phase=false;
return session.vamArchive.getLock();
}
/**
}
return -1;
}
- /**
- * probably don't need any of these below.
- */
- /* (non-Javadoc)
- * @see java.lang.Thread#destroy()
- */
- public void destroy() {
- super.destroy();
+
+ public void disableDocumentWatch() {
+ vamsasfile.haltWatch();
}
- /* (non-Javadoc)
- * @see java.lang.Thread#interrupt()
- */
- public void interrupt() {
- // TODO Auto-generated method stub
- super.interrupt();
+
+ public boolean isDocumentWatchEnabled() {
+ return (vamsasfile!=null) && vamsasfile.isWatchEnabled();
}
- /* (non-Javadoc)
- * @see java.lang.Thread#isInterrupted()
- */
- public boolean isInterrupted() {
- // TODO Auto-generated method stub
- return super.isInterrupted();
+
+ public void enableDocumentWatch() {
+ vamsasfile.enableWatch();
+ }
+
+ public boolean isWatcherAlive() {
+ return watchThread!=null && watchThread.running && watchThread.isAlive();
+ }
+
+ public void interruptWatching() {
+ if (watchThread!=null && watchThread.isAlive())
+ watchThread.interrupt();
+
}
- /* (non-Javadoc)
- * @see java.lang.Thread#run()
+ /**
+ * called to start the session watching thread which generates events
*/
- public void run() {
- // TODO Auto-generated method stub
- super.run();
+ public void startWatching() {
+ enableDocumentWatch();
+ watchThread.start();
+ while (!watchThread.running && watchThread.isAlive())
+ log.debug("Waiting until watcher is really started.");
+ }
+
+ public void stopWatching() {
+ interruptWatching();
+ watchThread.haltWatchers();
+
}
this.user = user;
this.client = client;
//try {
+ log.debug("Creating new session for "+_session);
session = new SessionUrn(_session);
- /*} catch (MalformedURLException e) {
+ log.debug("Creating new Event Generator");
+ evgen = new EventGeneratorThread(_session, this, handlers);
+ /*} catch (MalformedURLException e) {
log.error("Couldn't form a valid SessionUrn object!",e);
throw new InvalidSessionUrnException(_session.toString());
}*/
+ log.debug("SimpleClient constructed for session "+session.getSessionUrn());
+
}
/**
* construct new session by importing objects from an existing vamsas document
VamsasDocument doc = sdoc.getVamsasDocument(odoc);
sessdoc.putVamsasDocument(doc, sdoc.vorba);
sessdoc.closeArchive();
+ log.debug("Imported new vamsas data from "+importingArchive);
} catch (Exception e) {
sessdoc.cancelArchive();
- // write a dummy archive
+ // write a dummy iohandler
_session.slog.info("Exception when importing document data from "+importingArchive);
+ log.warn("While importing session data from existing archive in "+importingArchive, e);
throw new Exception("Failed to import data from "+importingArchive, e);
}
}
* @see uk.ac.vamsas.client.IClient#addDocumentUpdateHandler(java.util.EventListener)
*/
public void addDocumentUpdateHandler(PropertyChangeListener evt) {
- if (handlers.containsKey(Events.DOCUMENT_UPDATE)) {
- Object handler;
- ((PropertyChangeSupport) (handler = handlers.get(Events.DOCUMENT_UPDATE)))
- .addPropertyChangeListener(evt);
- listeners.add(handler);
- listeners.add((Object) evt);
- }
+ this.addVorbaEventHandler(Events.DOCUMENT_UPDATE, evt);
}
boolean finalized=false;
+ private void haltPickmanager() {
+ if (pickmanager!=null) {
+ final SimpleClient dying=this;
+ new Thread() {
+ public void run() {
+ SimpleClient.log.debug("Stopping pickManager..");
+ dying.pickmanager.haltPickManager();
+ SimpleClient.log.debug("pickManager halted.");
+ }
+ }.start();
+ }
+ }
/*
* (non-Javadoc)
*
* @see uk.ac.vamsas.client.IClient#finalizeClient()
*/
public void finalizeClient() {
+ if (finalized)
+ throw new Error("VAMSAS Client Implementation Error: Finalized called twice for same client instance.");
+
+ // mark this instance as finalized
+ finalized=true;
+
// TODO: determine if this is last client in session
- // TODO: raise events like : ((lst_client && document.request.to.close), (client_finalization), (
+ // TODO: raise events like : ((lst_client && document.request.to.close), (client_finalization), (
+ evgen._raise(Events.CLIENT_FINALIZATION, null, this,null);
// if (handlers.containsKey(Events.))
// if (handlers.containsKey(Events.CLIENT_FINALIZATION))
// deregister listeners.
- // mark this instance as finalized
- this._session.removeClient(this);
+ haltPickmanager();
+ SimpleClient.log.debug("Stopping EventGenerator..");
+ evgen.stopWatching();
+ SimpleClient.log.debug("EventGenerator halted.");
+ log.debug("Deregistering Client");
+ _session.removeClient(this);
+ log.debug("finalization Complete.");
}
/*
* @see uk.ac.vamsas.client.IClient#getClientDocument()
*/
public IClientDocument getClientDocument() throws IOException {
+ log.debug("getClientDocument");
if (cdocument!=null) {
// cdocument is non-nill if the ClientDocument.finalise() method hasn't been called.
return cdocument;
}
+ evgen.disableDocumentWatch();
VamsasArchive va = null;
try {
// LATER: bail out if it takes too long to get the lock ?
}
VamsasDocument doc=null;
IdFactory vorba = null;
- // TODO: reduce size of vorba ids generated from these parameters to IdFactory (mainly sessionHandle rationalization ?)
+ // TODO: LATER: reduce size of vorba ids generated from these parameters to IdFactory (mainly sessionHandle rationalization ?)
try {
va.setVorba(vorba=makeVorbaIdFactory());
// if session currently holds data - read it in - or get a dummy
- _session.slog.debug("Accessing document");
+ log.debug("Accessing document");
doc =
va.getVamsasDocument(getProvenanceUser(),
"created new session document.", null);
if (doc!=null)
- _session.slog.debug("Successfully retrieved document.");
+ log.debug("Successfully retrieved document.");
else
log.error("Unexpectedly retrieved null document!");
}
}
// Construct the IClientDocument instance
- ClientDocument cdoc = new ClientDocument(doc, va, vorba, this);
- return cdoc;
+ cdocument = new ClientDocument(doc, va, vorba, this);
+ return cdocument;
}
/*
* @see uk.ac.vamsas.client.IClient#updateDocument(uk.ac.vamsas.client.IClientDocument)
*/
public void updateDocument(IClientDocument newdoc) {
+ log.debug("updateDocument:");
+ // Check validity of simpleclient instance and that it holds a lock on the session's document
if (!(newdoc instanceof ClientDocument)) {
- throw new Error("Invalid IClientDocument instance for SimpleClient.");
+ throw new Error("Invalid IClientDocument passsed to SimpleClient.");
}
if (cdocument==null)
- throw new Error("Client Error - updateDocument() called before getClientDocument().");
+ throw new Error("Client Error - updateDocument() called before getClientDocument() on this SimpleClient instance.");
if (newdoc!=cdocument)
throw new Error("Client Error - SimpleClient.updateDocument() can only take the IClientDocument instance returned from SimpleClient.getClientDocument()");
+
+ if (evgen.isDocumentWatchEnabled())
+ throw new Error("Client Error - or Library Bug : Document watcher still enabled whilst ClientDocument instance exists.");
+
if (!cdocument.isModified()) {
+ // client document is silently got rid of, with no session update events.
if (log.isDebugEnabled())
- log.debug("updateDocument for "+session.getSessionUrn()+" with unmodified IClientDocument.");
+ log.debug("updateDocument for "+session.getSessionUrn()+" with unmodified IClientDocument (skipping the write)");
} else {
try {
if (!cdocument.updateSessionDocument()) {
log.warn("Session document did not update properly for session directory "+_session.sessionDir);
- // cdocument.archive.cancelArchive(); // LATER: could be done - would need to prevent updateSessionDocument closing the archive.
+ // cdocument.archive.cancelArchive(); // LATER: could be done - would need to prevent updateSessionDocument closing the iohandler.
_session.slog.warn("Session Document updated but may not be valid (false return from org.vamsas.simpleclient.ClientDocument.updateSessionDocument()");
}
+ log.debug("Document update successful.");
}
catch (IOException e) {
log.warn("IO Problems when updating document!",e);
_session.slog.error("IO problems when attempting to update document.");
}
}
+ try {
+ _session.setUnsavedFlag();
+ _session.unlockVamsasDocument();
+ evgen.enableDocumentWatch();
+ } catch (IOException e) {
+ log.warn("IO Problems when releasing lock on session document!",e);
+ _session.slog.error("IO problems when attempting to release lock on session document.");
+ }
// garbage collect the ClientDocument instance.
try {
+ log.debug("Finalizing ClientDocument instance.");
cdocument.finalize();
-
} catch (Throwable e) {
log.error("Exception when trying to garbage collect ClientDocument for "+session.getSessionUrn(), e);
}
* @see uk.ac.vamsas.client.IClient#storeDocument(java.io.File)
*/
public void storeDocument(File location) {
-
+ if (location==null)
+ throw new Error("Vamsas Client API Usage Error: storeDocument called with null location.");
+ log.debug("StoreDocument to "+location);
// write storeDocument file to inform other clients that they should raise
Lock vamlock = evgen.want_to_store();
// Events.DOCUMENT_FINALIZEAPPDATA
*/
public void addVorbaEventHandler(String EventChain, PropertyChangeListener evt) {
if (handlers.containsKey(EventChain)) {
+ log.debug("Adding new handler for "+EventChain);
Object handler;
((PropertyChangeSupport) (handler = handlers.get(EventChain)))
.addPropertyChangeListener(evt);
* @see uk.ac.vamsas.client.IClient#pollUpdate()
*/
public void pollUpdate() {
-
+ log.debug("pollUpdate");
if (evgen==null) {
log.warn("pollUpdate called on incomplete SimpleClient object.");
return;
}
- if (!evgen.isAlive()) {
+ if (!evgen.isWatcherAlive()) {
log.warn("pollUpdate called before joinSession() - trying to do this.");
try {
joinSession();
//TODO ensure event generator robustly handles these interrupts.
log.debug("interrrupting event generator.");
- evgen.interrupt();
+ evgen.interruptWatching();
log.debug("interrrupted event generator.");
}
* @see uk.ac.vamsas.client.IClient#joinSession()
*/
public void joinSession() throws Exception {
+ log.debug("Joining Session.");
// start the EventGenerator thread.
if (evgen==null) {
log.warn("joinSession called on incomplete SimpleClient object.");
return;
}
- if (evgen.isAlive())
+ if (evgen.isWatcherAlive())
throw new Error("Join session called twice for the same SimpleClient (IClient instance).");
- evgen.start();
- if (evgen.isAlive())
+ evgen.startWatching();
+ if (evgen.isWatcherAlive())
log.debug("Started EventGenerator thread.");
else {
log.warn("Failed to start EventGenerator thread.");
throw new Exception("Failed to start event generator thread - client cannot be instantiated.");
}
if (evgen.countHandlersFor(Events.DOCUMENT_CREATE)>0) {
- //TODO: is this application connecting to a newly created session document ?
+ //TODO: LATER: is this application connecting to a newly created session document ?
//evgen.raise(Events.DOCUMENT_CREATE);
}
+
}
protected VamsasSession get_session() {
return this._session;
}
-
+ SimplePickManager pickmanager=null;
/* (non-Javadoc)
* @see uk.ac.vamsas.client.IClient#getPickManager()
*/
public IPickManager getPickManager() {
- // TODO Auto-generated method stub
- return null;
+ createPickManager();
+ return pickmanager;
+ }
+
+ private void createPickManager() {
+ if (pickmanager==null){
+ // TODO: Construct PickManager for session using details from sessionURN!
+ log.debug("Creating PickManager (not from sessionURN yet)");
+ pickmanager = new SimplePickManager(new uk.ac.vamsas.client.picking.SocketManager());
+ }
}
}
package uk.ac.vamsas.client.simpleclient;
-import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.Writer;
+import java.util.Enumeration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Appender;
import org.apache.log4j.Logger;
import org.apache.log4j.FileAppender;
+import org.apache.log4j.PatternLayout;
import uk.ac.vamsas.client.ClientHandle;
import uk.ac.vamsas.client.IClient;
* @throws IOException
*/
private void initLog() throws IOException {
+ // TODO: fix session event logging
// LATER: make dedicated appender format for session log.
- Appender app = slog.getAppender("SESSION_LOG");
- // slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
-
- //Appender app = slog.getAppender("SESSION_LOG");
- if (app == null) log.info("No appender for SESSION_LOG");
+ /*Appender app = slog.getAppender("log4j.appender.SESSIONLOG");
+ // slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
+ // slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
+ for (Enumeration e = slog.getAllAppenders() ; e.hasMoreElements() ;) {
+ System.out.println(e.nextElement());
+
+ }*/
- if (slog!= null && app != null)
- {
- if (app instanceof FileAppender)
- {
- File sessionLogFile = new File(this.sessionDir, ((FileAppender)app).getFile());
- slog.addAppender(new FileAppender(app.getLayout(), sessionLogFile.getAbsolutePath()));
- }
- // slog.removeAppender("SESSION_LOG");
- }
+ if (slog!= null ) {
+ File sessionLogFile = new File(this.sessionDir, SESSION_LOG);
+ slog.addAppender(new FileAppender(new PatternLayout("%-4r [%t] %-5p %c %x - %m%n"), sessionLogFile.getAbsolutePath(), true));
+ } else {
+ log.info("No appender for SessionLog");
+ }
}
/**
if (sessionDir1.exists()) {
if (!sessionDir1.isDirectory() || !sessionDir1.canWrite() || !sessionDir1.canRead())
throw new IOException("Cannot access '"+sessionDir1+"' as a read/writable Directory.");
- this.sessionDir = sessionDir1;//createSessionFiles, need sessionDir attribute set
- if (!checkSessionFiles(sessionDir1))
- {
- createSessionFiles();
- }
- // session files exist in the directory
-
- initSessionObjects();
- slog.debug("Initialising additional VamsasSession instance");
- log.debug("Attached to VamsasSession in "+sessionDir1);
+ if (!checkSessionFiles(sessionDir1))
+ log.warn("checkSessionFiles() returned false. Possible client implementation error");
+ this.sessionDir = sessionDir1;
+ initSessionObjects();
+ slog.debug("Initialising additional VamsasSession instance");
+ log.debug("Attached to VamsasSession in "+sessionDir1);
//}
} else {
// start from scratch
if (!sessionDir1.mkdir())
throw new IOException("Failed to make VamsasSession directory in "+sessionDir1);
- this.sessionDir = sessionDir1;
createSessionFiles();
initSessionObjects();
slog.debug("Session directory created.");
throw new IOException("Invalid call to createSessionFiles() with null sessionDir");
File c_file = new File(sessionDir,CLIENT_LIST);
File v_doc = new File(sessionDir,VAMSAS_OBJ);
- if (c_file.createNewFile())
+ if (!c_file.exists() && c_file.createNewFile())
log.debug("Created new ClientFile "+c_file); // don't care if this works or not
- if (v_doc.createNewFile())
+ if (!v_doc.exists() && v_doc.createNewFile())
log.debug("Created new Vamsas Session Document File "+v_doc);
}
/**
* construct SessionFile objects and watchers for each
*/
private void initSessionObjects() throws IOException {
+ createSessionFiles();
if (clist!=null || vamArchive!=null)
throw new IOException("initSessionObjects called for initialised VamsasSession object.");
clist = new ClientsFile(new File(sessionDir,CLIENT_LIST));
vamArchive = new VamsasFile(new File(sessionDir,VAMSAS_OBJ));
+ storedocfile=new ClientsFile(new File(sessionDir, CLOSEANDSAVE_FILE));
initLog();
}
/**
public FileWatcher getClientWatcher() {
return new FileWatcher(clist.sessionFile);
}
- FileWatcher session_doc_watcher=null;
/**
* make a new watcher object for the vamsas Document
* @return new ClientFile watcher instance
*/
public FileWatcher getDocWatcher() {
- if (session_doc_watcher==null)
- return session_doc_watcher = new FileWatcher(vamArchive.sessionFile);
return new FileWatcher(vamArchive.sessionFile);
}
FileWatcher store_doc_file=null;
+ public ClientsFile storedocfile=null;
/**
* make a new watcher object for the messages file
- * (thread safe - keeps a reference to the first watcher)
* @return new watcher instance
*/
public FileWatcher getStoreWatcher() {
- if (store_doc_file==null)
- return store_doc_file = new FileWatcher(new File(CLOSEANDSAVE_FILE));
- return new FileWatcher(new File(CLOSEANDSAVE_FILE));
+ return new FileWatcher(new File(sessionDir,CLOSEANDSAVE_FILE));
}
/**
* @return
*/
public void addStoreDocumentRequest(ClientHandle client, UserHandle user) throws IOException {
+ // TODO: replace this with clientsFile mechanism
SessionFile sfw = new SessionFile(new File(sessionDir, CLOSEANDSAVE_FILE));
while (!sfw.lockFile())
log.debug("Trying to get lock for "+CLOSEANDSAVE_FILE);
if (extlock==null && !vamArchive.lockFile())
while (!vamArchive.lockFile())
log.info("Trying to get lock for "+vamArchive.sessionFile);
- // TODO: LATER: decide if session archive provenance should be written in vamsasDocument file for this export.
+ // TODO: LATER: decide if a provenance entry should be written in the exported document recording the export from the session
newdoc.updateFrom(extlock, vamArchive);
// LATER: LATER: fix use of updateFrom for file systems where locks cannot be made (because they don't have a lockManager, ie NFS/Unix, etc).
vamArchive.unLock();
* @throws IOException if locks fail or vamsas document read fails.
*/
protected VamsasArchive getVamsasDocument() throws IOException {
- // TODO: check we haven't already done this once
- if (!vamArchive.lockFile())
+ // TODO: check we haven't already done this once - probably should be done by caller
+ // patiently wait for a lock on the document. (from ArchiveClient.getUpdateable())
+ long tries=5000;
+ while (vamArchive.getLock()==null && --tries>0) {
+// Thread.sleep(1);
+ log.debug("Trying to get a document lock for the "+tries+"'th time.");
+ }
+ if (tries==0)
throw new IOException("Failed to get lock for vamsas archive.");
-
+
VamsasArchive va = new VamsasArchive(vamArchive.sessionFile, false, true, vamArchive);
-
+
return va;
}
/**
+ * Unlocks the vamsas archive session document after it has been closed.
+ * @throws IOException
+ */
+ protected void unlockVamsasDocument() throws IOException {
+ if (vamArchive!=null)
+ vamArchive.unLock();
+ }
+ /**
* create a uniquely named uk.ac.vamsas.client.simpleclient.ClientsFile.addClient(ClientHandle)ile in the session Directory
* @see java.io.File.createTempFile
* @param pref Prefix for name
protected void addClient(IClient client)
{
if (client == null)
- this.slog.error("Try to add a null client to the session ");
- else
- this.clist.addClient(client.getClientHandle(), getClientWatcher().getChangedState());
+ slog.error("Try to add a null client to the session ");
+ else {
+ log.debug("Adding client "+client.getClientHandle().getClientUrn());
+ getClientWatcherElement().haltWatch();
+ clist.addClient(client.getClientHandle());
+ getClientWatcherElement().enableWatch();
+ log.debug("Added.");
+ }
}
/**
{
if (client == null)
{
- //System.out.println("Try to remove a null client.");
- this.slog.error("Try to remove a null client.");
+ log.error("Null client passed to removeClient");
return;
}
- this.clist.removeClient(client.getClientHandle(), getClientWatcher().getChangedState());
+ SessionFileWatcherElement cwe=getClientWatcherElement();
+ if (cwe!=null && cwe.isWatchEnabled()) {
+ cwe.haltWatch();
+ } else {
+ cwe=null;
+ }
+ clist.removeClient(client.getClientHandle(),null);
if (this.clist.retrieveClientList() == null|| this.clist.retrieveClientList().length<1)
{//assume it is the last client has been removed shutting down session
- System.out.println("last client removed: removing session");
+ slog.info("last client removed: removing session");
+ log.debug("last client removed: removing session");
this.getSessionManager().removeSession(client.getSessionHandle());
}
else
{
- this.slog.debug("Still "+this.clist.retrieveClientList().length +" active clients");
- System.out.println("Still "+(this.clist.retrieveClientList()==null?"null":this.clist.retrieveClientList().length+"") +" active clients");
+ int active=clist.retrieveClientList().length;
+ log.debug("Still "+active+" active clients");
+ slog.info("Still "+active+" active clients");
+ }
+ if (cwe!=null) {
+ cwe.enableWatch();
}
}
/**
protected void setSessionManager(SimpleSessionManager sessionManager) {
this.sessionManager = sessionManager;
}
+public ClientsFile getStoreDocFile() {
+ if (storedocfile==null) {
+
+ }
+ return storedocfile;
+}
+SessionFileWatcherElement clistWatchElement=null;
+public SessionFileWatcherElement getClientWatcherElement() {
+ if (clistWatchElement==null) {
+ clistWatchElement=new SessionFileWatcherElement(clist,null);
+ }
+ return clistWatchElement;
+}
}