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();
+
}