applied LGPLv3 and source code formatting.
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / EventGeneratorThread.java
index d00e119..47c84ac 100644 (file)
-package uk.ac.vamsas.client.simpleclient;
-
-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 uk.ac.vamsas.client.Events;
-
-/**
- * monitors watcher objects and generates events.
- */
-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;
-  /** 
-   * Watcher element for list of all the clientHandles for the session
-   */
-  protected SessionFileWatcherElement clientfile=null;
-  /**
-   * the session's vamsasDocument
-   */
-  protected VamsasFileWatcherElement vamsasfile=null;
-  /**
-   * written to by client when its app calls storeDocument.
-   */
-  protected SessionFileWatcherElement storeFile=null;
-  
-  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;
-    log.debug("Creating VamsasFileWatcherThread.");
-    watchThread = new VamsasFileWatcherThread(this);
-    initWatchers();
-  }
-  
-  private void initWatchers() {
-    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 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!=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 = "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 = "uk.ac.vamsas.client.events.clientFinalizationEvent";
-       */ // again - as the test.
-      watchlock.release();
-    }
-    return true;
-  }
-  /**
-   * 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
-  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
-   * 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() {
-    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 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), 
-    // 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.
-    FileWatcher sfwatcher=session.getStoreWatcher();
-    FileWatcher vfwatcher=session.getDocWatcher();
-    int units = 0; // zero if updates occured over a sleep period
-    while (units<STORE_WAIT) {
-      try {
-      Thread.sleep(watchThread.WATCH_SLEEP); 
-      } catch (InterruptedException e) {
-        log.debug("interrupted.");
-      }
-      if (sfwatcher.hasChanged() || vfwatcher.hasChanged()) {
-        units=0;
-      } 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) {
-      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();
-  }
-  /**
-   * count handlers for a particular vamsas event 
-   * @param event string enumeration from uk.ac.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;
-  }
-
-  public void disableDocumentWatch() {
-    vamsasfile.haltWatch();
-  }
-
-  public boolean isDocumentWatchEnabled() {
-    return (vamsasfile!=null) && vamsasfile.isWatchEnabled();
-  }
-
-  public void enableDocumentWatch() {
-    vamsasfile.enableWatch();
-  }
-
-  public boolean isWatcherAlive() {
-    return watchThread!=null && watchThread.running && watchThread.isAlive();
-  }
-
-  public void interruptWatching() {
-    if (watchThread!=null && watchThread.isAlive())
-    {
-      // TODO: find a way of interrupting watcher in a way that prevents file IO being interrupted
-      watchThread.interrupt();
-    }
-    
-  }
-  /**
-   * called to start the session watching thread which generates events
-   */
-  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();
-    
-  }
-  
-
-}
+/*\r
+ * This file is part of the Vamsas Client version 0.1. \r
+ * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, \r
+ *  Andrew Waterhouse and Dominik Lindner.\r
+ * \r
+ * Earlier versions have also been incorporated into Jalview version 2.4 \r
+ * since 2008, and TOPALi version 2 since 2007.\r
+ * \r
+ * The Vamsas Client is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *  \r
+ * The Vamsas Client is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public License\r
+ * along with the Vamsas Client.  If not, see <http://www.gnu.org/licenses/>.\r
+ */\r
+package uk.ac.vamsas.client.simpleclient;\r
+\r
+import java.beans.PropertyChangeListener;\r
+import java.beans.PropertyChangeSupport;\r
+import java.util.Hashtable;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+\r
+import uk.ac.vamsas.client.Events;\r
+\r
+/**\r
+ * monitors watcher objects and generates events.\r
+ */\r
+public class EventGeneratorThread {\r
+  private static Log log = LogFactory.getLog(EventGeneratorThread.class);\r
+\r
+  private SimpleClient client;\r
+\r
+  private Hashtable handlers; // manager object\r
+\r
+  private VamsasSession session;\r
+\r
+  /**\r
+   * thread watching all the session's file objects\r
+   */\r
+  protected VamsasFileWatcherThread watchThread = null;\r
+\r
+  /**\r
+   * Watcher element for list of all the clientHandles for the session\r
+   */\r
+  protected SessionFileWatcherElement clientfile = null;\r
+\r
+  /**\r
+   * the session's vamsasDocument\r
+   */\r
+  protected VamsasFileWatcherElement vamsasfile = null;\r
+\r
+  /**\r
+   * written to by client when its app calls storeDocument.\r
+   */\r
+  protected SessionFileWatcherElement storeFile = null;\r
+\r
+  EventGeneratorThread(VamsasSession s, SimpleClient _client,\r
+      Hashtable eventhandlers) {\r
+    if (eventhandlers == null || s == null || _client == null)\r
+      throw new Error("Null arguments to EventGeneratorThread constructor.");\r
+    handlers = eventhandlers;\r
+    session = s;\r
+    client = _client;\r
+    log.debug("Creating VamsasFileWatcherThread.");\r
+    watchThread = new VamsasFileWatcherThread(this);\r
+    initWatchers();\r
+  }\r
+\r
+  private void initWatchers() {\r
+    if (clientfile == null) {\r
+      log.debug("Initializing clientfile Watcher");\r
+      clientfile = session.getClientWatcherElement();\r
+      // handler is set in the Vamsas session\r
+      /*\r
+       * clientfile.setHandler(new WatcherCallBack() {\r
+       * \r
+       * public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {\r
+       * return clientListChanged(watcher, lock); } });\r
+       */\r
+      watchThread.addElement(clientfile);\r
+    }\r
+    final EventGeneratorThread evgen = this;\r
+\r
+    if (vamsasfile == null) {\r
+      log.debug("Initializing VamsasFileWatcher");\r
+      vamsasfile = new VamsasFileWatcherElement(session.vamArchive,\r
+          new WatcherCallBack() {\r
+            public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {\r
+              return evgen.documentChanged(lock);\r
+            }\r
+          });\r
+      watchThread.addElement(vamsasfile);\r
+    }\r
+    if (storeFile == null) {\r
+      storeFile = new SessionFileWatcherElement(session.getStoreDocFile(),\r
+          new WatcherCallBack() {\r
+            public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {\r
+              return evgen.storeDocRequest(lock);\r
+            }\r
+          });\r
+      log.debug("Initializing storeDocFile flag watcher");\r
+    }\r
+    /*\r
+    */\r
+    log.debug("Watchers inited.");\r
+  }\r
+\r
+  /**\r
+   * Call registered handlers for a vamsas session event\r
+   * \r
+   * @param handlerEvent\r
+   *          a named event\r
+   * @param property\r
+   *          property name to pass to handler\r
+   * @param oldval\r
+   *          old value of property to pass\r
+   * @param newval\r
+   *          new value of property to pass\r
+   * @return true if event generation did not raise any exceptions.\r
+   */\r
+  boolean _raise(String handlerEvent, String property, Object oldval,\r
+      Object newval) {\r
+    PropertyChangeSupport h = (PropertyChangeSupport) handlers\r
+        .get(handlerEvent);\r
+    if (h != null) {\r
+      log.debug("Triggering:" + handlerEvent);\r
+      try {\r
+        h.firePropertyChange(property, oldval, newval);\r
+      } catch (Exception e) {\r
+        log.warn("Client Exception during handling of " + handlerEvent, e);\r
+        return false;\r
+      } catch (Error e) {\r
+        log\r
+            .error("Serious! Client Error during handling of " + handlerEvent,\r
+                e);\r
+        return false;\r
+      }\r
+      log.debug("Finished  :" + handlerEvent);\r
+    } else\r
+      log.debug("No handlers for raised " + handlerEvent);\r
+    return true;\r
+  }\r
+\r
+  protected boolean storeDocRequest(Lock lock) {\r
+    if (log.isDebugEnabled())\r
+      log.debug("StoreDocRequest on "\r
+          + (lock == null ? (lock.isLocked() ? "" : "Invalid ") : "Non-")\r
+          + "Existing lock");\r
+    // TODO: define the storeFile semaphore mechanism : file exists - all\r
+    // clients inform their apps, and then the client that wrote the file should\r
+    // delete the file (it should hold the lock to it).\r
+    if (storeFile.getWatcher().exists) {\r
+      _raise(Events.DOCUMENT_FINALIZEAPPDATA, client.getSessionUrn(), null,\r
+          client);\r
+      // expect client to write to document so update watcher state on return\r
+      vamsasfile.getWatcher().setState();\r
+      lock.release();\r
+    }\r
+    return true;\r
+  }\r
+\r
+  protected boolean documentChanged(Lock doclock) {\r
+    boolean continueWatching = true;\r
+    if (!block_document_updates) {\r
+      session.vamArchive.fileLock = doclock;\r
+      if (client.pickmanager != null)\r
+        client.pickmanager.setPassThru(false);\r
+      if (log.isDebugEnabled()) {\r
+        log.debug("Initiating a documentChanged event. Document is "\r
+            + (client.cdocument == null ? "closed" : "open"));\r
+      }\r
+      // TODO: decide if individual object update handlers are called as well as\r
+      // overall event handler\r
+      if (!_raise(Events.DOCUMENT_UPDATE, client.getSessionUrn(), null, client)) {\r
+        log\r
+            .info("Recovering from errors or exceptions generated by client application");\r
+        if (client.cdocument != null) {\r
+          try {\r
+            client.tidyAwaySessionDocumentState();\r
+          } catch (Exception e) {\r
+            log\r
+                .warn(\r
+                    "Exception generated by vamsas library - when tidying away session document:",\r
+                    e);\r
+          } catch (Error e) {\r
+            log\r
+                .error(\r
+                    "LIBRARY Implementation error - when tidying away session document:",\r
+                    e);\r
+          }\r
+        }\r
+\r
+      }\r
+      if (client.pickmanager != null)\r
+        client.pickmanager.setPassThru(true);\r
+      if (log.isDebugEnabled()) {\r
+        log.debug("Finished handling a documentChanged event. Document is "\r
+            + (client.cdocument == null ? "closed" : "open"));\r
+      }\r
+      if (client.cdocument != null) {\r
+        log\r
+            .warn("Implementation Error ?  ClientDocument instance has not been closed or updated by handler!");\r
+      }\r
+      /*\r
+       * try { client._session.getVamsasDocument().closeArchive(); } catch\r
+       * (Exception e)\r
+       * {log.warn("Unexpected exception when closing document after update."\r
+       * ,e);};\r
+       */\r
+    } else {\r
+      // TODO: check documentChanged */\r
+      log.debug("Ignoring documentChanged event for " + client.getSessionUrn());\r
+    }\r
+    return continueWatching;\r
+  }\r
+\r
+  boolean ownsf = false;\r
+\r
+  /**\r
+   * Moved to SimpleClientSessionManager scans all watchers and fires\r
+   * changeEvents if necessary\r
+   * \r
+   * @return number of events generated.\r
+   */\r
+  private boolean clientListChanged(WatcherElement clientfile, Lock watchlock) {\r
+    log.debug("ClientListChanged handler called for "\r
+        + clientfile.getWatcher().getSubject());\r
+    // could make this general - but for now keep simple\r
+    if (watchlock != null) {\r
+      // TODO: compare new client list to old list version. is it changed ?\r
+      // see what happened to the clientfile - compare our internal version with\r
+      // the one in the file, or just send the updated list out...?\r
+      //\r
+      /**\r
+       * Generated when a new vamsas client is attached to a session (Handle is\r
+       * passed) Note: the newly created client does not receive the event.\r
+       * \r
+       * public static final String CLIENT_CREATION =\r
+       * "uk.ac.vamsas.client.events.clientCreateEvent";\r
+       */\r
+      // as the test\r
+      /**\r
+       * Generated when a vamsas client leaves a session (Handle is passed to\r
+       * all others). public static final String CLIENT_FINALIZATION =\r
+       * "uk.ac.vamsas.client.events.clientFinalizationEvent";\r
+       */\r
+      // again - as the test.\r
+      watchlock.release();\r
+    }\r
+    return true;\r
+  }\r
+\r
+  /**\r
+   * Events raised by IClient and propagated to others in session\r
+   */\r
+\r
+  /**\r
+   * number of milliseconds between any file state check.\r
+   */\r
+  long POLL_UNIT = 20;\r
+\r
+  protected void wait(int u) {\r
+    if (u <= 0)\r
+      u = 1;\r
+    long l = System.currentTimeMillis() + POLL_UNIT * u;\r
+    while (System.currentTimeMillis() < l)\r
+      ;\r
+  }\r
+\r
+  private boolean block_document_updates = false;\r
+\r
+  int STORE_WAIT = 5; // how many units before we decide all clients have\r
+                      // finalized their appdatas\r
+\r
+  private boolean in_want_to_store_phase = false;\r
+\r
+  /**\r
+   * client App requests offline storage of vamsas data. Call blocks whilst\r
+   * other apps do any appData finalizing and then returns (after locking the\r
+   * vamsasDocument in the session) Note - the calling app may also receive\r
+   * events through the EventGeneratorThread for document updates.\r
+   * \r
+   * @return Lock for session.vamArchive\r
+   * @param STORE_WAIT\r
+   *          indicates how lock the call will block for when nothing appears to\r
+   *          be happening to the session.\r
+   */\r
+  protected Lock want_to_store() {\r
+    if (in_want_to_store_phase) {\r
+      log\r
+          .error("client error: want_to_store called again before first call has completed.");\r
+      return null;\r
+    }\r
+    in_want_to_store_phase = true;\r
+    // TODO: test the storeDocumentRequest mechanism\r
+    /*\r
+     * / watchThread.haltWatchers();\r
+     */\r
+    log.debug("Stopping document_update watcher");\r
+    vamsasfile.haltWatch();\r
+    // block_document_updates=true;\r
+    log.debug("Cleared flag for ignoring document_update requests");\r
+\r
+    log.debug("Sending Store Document Request");\r
+    try {\r
+      session.addStoreDocumentRequest(client.getClientHandle(), client\r
+          .getUserHandle());\r
+    } catch (Exception e) {\r
+      log.warn("Whilst writing StoreDocumentRequest for "\r
+          + client.getClientHandle().getClientUrn() + " "\r
+          + client.getUserHandle(), e);\r
+      log.info("trying to continue after storeDocumentRequest exception.");\r
+    }\r
+    log.debug("Waiting for other apps to do FinalizeApp handling.");\r
+    // LATER: refine this semaphore process\r
+    // to make a robust signalling mechanism:\r
+    // app1 requests, app1..n do something (or don't - they may be dead),\r
+    // app1 realises all apps have done their thing, it then continues with\r
+    // synchronized data.\r
+    // this probably needs two files - a request file,\r
+    // and a response file which is acknowledged by the app1 requestor for each\r
+    // app.\r
+    // eventually, no more responses are received for the request, and the app\r
+    // can then only continue with its store.\r
+    FileWatcher sfwatcher = session.getStoreWatcher();\r
+    FileWatcher vfwatcher = session.getDocWatcher();\r
+    int units = 0; // zero if updates occured over a sleep period\r
+    while (units < STORE_WAIT) {\r
+      try {\r
+        Thread.sleep(watchThread.WATCH_SLEEP);\r
+      } catch (InterruptedException e) {\r
+        log.debug("interrupted.");\r
+      }\r
+      if (sfwatcher.hasChanged() || vfwatcher.hasChanged()) {\r
+        units = 0;\r
+      } else {\r
+        units++;\r
+      }\r
+    }\r
+\r
+    block_document_updates = false;\r
+    vamsasfile.enableWatch();\r
+    log.debug("Cleared flag for ignoring document_update requests");\r
+    // wait around again (until our own watcher has woken up and synchronized).\r
+    while (units < STORE_WAIT) {\r
+      try {\r
+        Thread.sleep(watchThread.WATCH_SLEEP);\r
+      } catch (InterruptedException e) {\r
+        log.debug("interrupted.");\r
+      }\r
+      if (sfwatcher.hasChanged() || vfwatcher.hasChanged())\r
+        units = 0;\r
+      else\r
+        units++;\r
+    }\r
+\r
+    log.debug("finished waiting.");\r
+    in_want_to_store_phase = false;\r
+    return session.vamArchive.getLock();\r
+  }\r
+\r
+  /**\r
+   * count handlers for a particular vamsas event\r
+   * \r
+   * @param event\r
+   *          string enumeration from uk.ac.vamsas.client.Events\r
+   * @return -1 for an invalid event, otherwise the number of handlers\r
+   */\r
+  protected int countHandlersFor(String event) {\r
+    if (handlers.containsKey(event)) {\r
+      PropertyChangeSupport handler = (PropertyChangeSupport) handlers\r
+          .get(event);\r
+      PropertyChangeListener[] listeners;\r
+      if (handler != null)\r
+        return ((listeners = handler.getPropertyChangeListeners()) == null) ? -1\r
+            : listeners.length;\r
+    }\r
+    return -1;\r
+  }\r
+\r
+  public void disableDocumentWatch() {\r
+    vamsasfile.haltWatch();\r
+  }\r
+\r
+  public boolean isDocumentWatchEnabled() {\r
+    return (vamsasfile != null) && vamsasfile.isWatchEnabled();\r
+  }\r
+\r
+  public void enableDocumentWatch() {\r
+    vamsasfile.enableWatch();\r
+  }\r
+\r
+  public boolean isWatcherAlive() {\r
+    return watchThread != null && watchThread.running && watchThread.isAlive();\r
+  }\r
+\r
+  public void interruptWatching() {\r
+    if (watchThread != null && watchThread.isAlive()) {\r
+      // TODO: find a way of interrupting watcher in a way that prevents file IO\r
+      // being interrupted\r
+      watchThread.interrupt();\r
+    }\r
+\r
+  }\r
+\r
+  /**\r
+   * called to start the session watching thread which generates events\r
+   */\r
+  public void startWatching() {\r
+    enableDocumentWatch();\r
+    watchThread.start();\r
+    while (!watchThread.running && watchThread.isAlive())\r
+      log.debug("Waiting until watcher is really started.");\r
+  }\r
+\r
+  public void stopWatching() {\r
+    interruptWatching();\r
+    watchThread.haltWatchers();\r
+\r
+  }\r
+\r
+}\r