backtrack
[vamsas.git] / src / org / vamsas / client / simpleclient / EventGeneratorThread.java
diff --git a/src/org/vamsas/client/simpleclient/EventGeneratorThread.java b/src/org/vamsas/client/simpleclient/EventGeneratorThread.java
new file mode 100644 (file)
index 0000000..d9aa1db
--- /dev/null
@@ -0,0 +1,277 @@
+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();
+  }
+  
+
+}