X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fuk%2Fac%2Fvamsas%2Fclient%2Fsimpleclient%2FEventGeneratorThread.java;h=b9824b5b7c32a7daebed4887eeffa9d8f02cda72;hb=f372140c5b6c97ebc23c9dd6762d8d518cdcc45d;hp=8d028ef42c5e75448da22393114dd5a8a54a6e47;hpb=5156a824b13ecd091af9f84870f41d83eb59bd71;p=vamsas.git diff --git a/src/uk/ac/vamsas/client/simpleclient/EventGeneratorThread.java b/src/uk/ac/vamsas/client/simpleclient/EventGeneratorThread.java index 8d028ef..b9824b5 100644 --- a/src/uk/ac/vamsas/client/simpleclient/EventGeneratorThread.java +++ b/src/uk/ac/vamsas/client/simpleclient/EventGeneratorThread.java @@ -1,38 +1,38 @@ package uk.ac.vamsas.client.simpleclient; -import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.Hashtable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.vamsas.client.Events; + +import uk.ac.vamsas.client.Events; /** * 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) @@ -40,121 +40,165 @@ public class EventGeneratorThread extends Thread implements Runnable { 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(); + // handler is set in the Vamsas session +/* clientfile.setHandler(new WatcherCallBack() { + + public boolean handleWatchEvent(WatcherElement watcher, Lock lock) { + 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."); + } + /** + * Call registered handlers for a vamsas session event + * @param handlerEvent a named event + * @param property property name to pass to handler + * @param oldval old value of property to pass + * @param newval new value of property to pass + * @return true if event generation did not raise any exceptions. + */ + boolean _raise(String handlerEvent, String property, Object oldval, Object newval) { + PropertyChangeSupport h = (PropertyChangeSupport) handlers.get(handlerEvent); + if (h!=null) { + log.debug("Triggering:"+handlerEvent); + try { + h.firePropertyChange(property, oldval, newval); + } catch (Exception e) { + log.warn("Client Exception during handling of "+handlerEvent, e); + return false; + } + catch (Error e) + { + log.error("Serious! Client Error during handling of "+handlerEvent, e); + return false; + } + log.debug("Finished :"+handlerEvent); + } else + log.debug("No handlers for raised "+handlerEvent); + return true; + } + 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; + if (client.pickmanager!=null) + client.pickmanager.setPassThru(false); + if (log.isDebugEnabled()) { + log.debug("Initiating a documentChanged event. Document is "+(client.cdocument==null ? "closed" : "open")); + } + // TODO: decide if individual object update handlers are called as well as overall event handler + if (!_raise(Events.DOCUMENT_UPDATE, client.getSessionUrn(), null, client)) + { + log.info("Recovering from errors or exceptions generated by client application"); + if (client.cdocument!=null) + { + try { + client.tidyAwaySessionDocumentState(); + } + catch (Exception e) + { + log.warn("Exception generated by vamsas library - when tidying away session document:",e); + } + catch (Error e) + { + log.error("LIBRARY Implementation error - when tidying away session document:",e); + } + } + + } + if (client.pickmanager!=null) + client.pickmanager.setPassThru(true); + if (log.isDebugEnabled()) { + log.debug("Finished handling a documentChanged event. Document is "+(client.cdocument==null ? "closed" : "open")); + } + if (client.cdocument!=null) + { + log.warn("Implementation Error ? ClientDocument instance has not been closed or updated by handler!"); + } + /*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; /** + * Moved to SimpleClientSessionManager * 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...? // /** * Generated when a new vamsas client is attached to a session (Handle is * passed) Note: the newly created client does not receive the event. * - public static final String CLIENT_CREATION = "org.vamsas.client.events.clientCreateEvent"; + public static final String CLIENT_CREATION = "uk.ac.vamsas.client.events.clientCreateEvent"; */ // as the test /** * Generated when a vamsas client leaves a session (Handle is passed to all * others). - public static final String CLIENT_FINALIZATION = "org.vamsas.client.events.clientFinalizationEvent"; + 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 = "org.vamsas.client.events.documentUpdateEvent"; - */ - // read apphandle from 'lastUpdate' session file. - // pass apphandle name to appHandler ? - + watchlock.release(); } - /** - * 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 = "org.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 = "org.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: - * NewValue: org.vamsas.client.IClient for session. - * - public static final String DOCUMENT_FINALIZEAPPDATA = "org.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 @@ -175,7 +219,7 @@ public class EventGeneratorThread extends Thread implements Runnable { 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 @@ -186,16 +230,28 @@ public class EventGeneratorThread extends Thread implements Runnable { * @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), @@ -203,21 +259,33 @@ public class EventGeneratorThread extends Thread implements Runnable { // 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