--- /dev/null
+package org.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;
+
+/**
+ * monitors watcher objects and generates events.
+ */
+public class EventGeneratorThread extends Thread implements Runnable {
+ private static Log log = LogFactory.getLog(EventGeneratorThread.class);
+ private SimpleClient client;
+ private Hashtable handlers; // manager object
+ private VamsasSession session;
+
+ /**
+ * list with all the clientHandles for the session
+ */
+ protected FileWatcher clientfile=null;
+ /**
+ * the session's vamsasDocument
+ */
+ protected FileWatcher vamsasfile=null;
+ /**
+ * written to by client when its app calls storeDocument.
+ */
+ protected FileWatcher storeFile=null;
+
+ private boolean watch=false;
+
+
+ EventGeneratorThread(VamsasSession s, SimpleClient _client, Hashtable eventhandlers) {
+ if (eventhandlers==null || s==null || _client==null)
+ throw new Error("Null arguments to EventGeneratorThread constructor.");
+ handlers = eventhandlers;
+ session = s;
+ client = _client;
+ setName(s.sessionDir.getName());
+ 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();
+ }
+ 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;
+ // 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) {
+ // 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";
+ */ // 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";
+ */ // 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 ?
+
+ }
+ /**
+ * 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: <Vamsas-session URN>
+ * 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() {
+
+ }
+ /**
+ * Events raised by IClient and propagated to others in session
+ */
+
+ /**
+ * number of milliseconds between any file state check.
+ */
+ long POLL_UNIT = 20;
+ protected void wait(int u) {
+ if (u<=0)
+ u=1;
+ long l = System.currentTimeMillis()+POLL_UNIT*u;
+ while (System.currentTimeMillis()<l)
+ ;
+ }
+
+
+ private boolean block_document_updates=false;
+ int STORE_WAIT=5; // how many units before we decide all clients have finalized their appdatas
+
+ /**
+ * client App requests offline storage of vamsas data.
+ * Call blocks whilst other apps do any appData finalizing
+ * and then returns (after locking the vamsasDocument in the session)
+ * Note - the calling app may also receive events through the EventGeneratorThread for document updates.
+ *
+ * @return Lock for session.vamArchive
+ * @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.");
+ 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.");
+ }
+ // LATER: refine this semaphore process
+ // to make a robust signalling mechanism:
+ // app1 requests, app1..n do something (or don't - they may be dead),
+ // app1 realises all apps have done their thing, it then continues with synchronized data.
+ // 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;
+ while (units<STORE_WAIT) {
+ wait(1);
+ if (storeFile.hasChanged() || vamsasfile.hasChanged())
+ units=0;
+ else
+ units++;
+ }
+
+ block_document_updates=false;
+ 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())
+ units=0;
+ else
+ units++;
+ }
+
+
+ log.debug("finished waiting.");
+ return session.vamArchive.getLock();
+ }
+ /**
+ * count handlers for a particular vamsas event
+ * @param event string enumeration from org.vamsas.client.Events
+ * @return -1 for an invalid event, otherwise the number of handlers
+ */
+ protected int countHandlersFor(String event) {
+ if (handlers.containsKey(event)) {
+ PropertyChangeSupport handler = (PropertyChangeSupport) handlers.get(event);
+ PropertyChangeListener[] listeners;
+ if (handler!=null)
+ return ((listeners=handler.getPropertyChangeListeners())==null)
+ ? -1 : listeners.length;
+ }
+ return -1;
+ }
+ /**
+ * probably don't need any of these below.
+ */
+ /* (non-Javadoc)
+ * @see java.lang.Thread#destroy()
+ */
+ public void destroy() {
+ super.destroy();
+ }
+ /* (non-Javadoc)
+ * @see java.lang.Thread#interrupt()
+ */
+ public void interrupt() {
+ // TODO Auto-generated method stub
+ super.interrupt();
+ }
+ /* (non-Javadoc)
+ * @see java.lang.Thread#isInterrupted()
+ */
+ public boolean isInterrupted() {
+ // TODO Auto-generated method stub
+ return super.isInterrupted();
+ }
+ /* (non-Javadoc)
+ * @see java.lang.Thread#run()
+ */
+ public void run() {
+ // TODO Auto-generated method stub
+ super.run();
+ }
+
+
+}