From 22a4a706dd9c2081f614577235224574bb37fe91 Mon Sep 17 00:00:00 2001 From: pmarguerite Date: Fri, 18 May 2007 14:34:04 +0000 Subject: [PATCH] Added support for crashed client. Crashed client are removed correctly from the session client. When a new client joins a session, it adds itself to the session client list and ensures that it is still in the list while it is active. When a client wants to remove itself from the session, the client clears the session client list to check if it is the last client. (all actives clients should readd themself to the list during two watcher cycles, if the list is still empty. The client consideres itself as the last client of the session and closes properly the session) git-svn-id: https://svn.lifesci.dundee.ac.uk/svn/repository/trunk@394 be28352e-c001-0410-b1a7-c7978e42abec --- .../ClientSessionFileWatcherElement.java | 123 +++++++++++++ .../client/simpleclient/EventGeneratorThread.java | 7 +- .../vamsas/client/simpleclient/SimpleClient.java | 25 ++- .../vamsas/client/simpleclient/VamsasSession.java | 184 +++++++++++++++++--- .../vamsas/client/simpleclient/WatcherElement.java | 25 ++- 5 files changed, 324 insertions(+), 40 deletions(-) create mode 100644 src/uk/ac/vamsas/client/simpleclient/ClientSessionFileWatcherElement.java diff --git a/src/uk/ac/vamsas/client/simpleclient/ClientSessionFileWatcherElement.java b/src/uk/ac/vamsas/client/simpleclient/ClientSessionFileWatcherElement.java new file mode 100644 index 0000000..9d2b2d9 --- /dev/null +++ b/src/uk/ac/vamsas/client/simpleclient/ClientSessionFileWatcherElement.java @@ -0,0 +1,123 @@ + +package uk.ac.vamsas.client.simpleclient; + + +public class ClientSessionFileWatcherElement extends SessionFileWatcherElement { + + private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(VamsasFileWatcherElement.class); + + /** + * count of watch cycle before considering there is no other active client. + */ + private int timeoutBeforeLastCycle = -1; + + private boolean isTimeOutEnable = false; + + /** + * @param watcher + * @param handler + */ + public ClientSessionFileWatcherElement(SessionFile watcher, + WatcherCallBack handler) { + super(watcher, handler); + } + + /** + * @param watcher + * @param handler + * @param enableWatching + */ + public ClientSessionFileWatcherElement(SessionFile watcher, + WatcherCallBack handler, boolean enableWatching) { + super(watcher, handler, enableWatching); + } + + /** + * @see uk.ac.vamsas.client.simpleclient.WatcherElement#doWatch() + * * @return true if the handler was called for a changeEvent + */ + public boolean doWatch() + { + if (!watchForChange || handler==null) + { //log.debug("!watchForChange || handler==null"); + return false; + } + if (watcher==null) + initWatch(); // somehow not done the first time + handlerCalled=false; + Lock doclock=null; + try + { + doclock=watcher.getChangedState(); + } + catch (Exception e) { + log.error("Whilst watching "+watcher.getSubject(), e); + } + if (doclock==null) + {//no change detected + this.cycleCountSinceModif ++; + if (this.isTimeOutEnable && cycleCountSinceModif > timeoutBeforeLastCycle) + { + if(this.handler != null ) + synchronized (this.handler) + { + this.callHandler(doclock); + } + } + //log.debug("no modification"); + return false; + } + this.cycleCountSinceModif =0; //change detected + if(this.handler != null ) + synchronized (this.handler) + { + this.callHandler(doclock); + } + + return true; + } + + + /** + * count of cycles since last modification on the file + */ + protected int cycleCountSinceModif =0; + + /** + * resets count of watch cycles (default value : 0) + * + */ + protected void resetCycleCount () + { + this.cycleCountSinceModif = 0; + } + + /** + * Increases the count of cycles + * + */ + protected void increaseCycleCount () + { + this.cycleCountSinceModif ++; + } + + /** + * Enable the time out if the timeout is greater than zero + * @param timeoutBeforeLastCycle the timeoutBeforeLastCycle to set + */ + public void setTimeoutBeforeLastCycle(int timeoutBeforeLastCycle) { + + this.timeoutBeforeLastCycle = timeoutBeforeLastCycle; + if (this.timeoutBeforeLastCycle>0) + isTimeOutEnable = true; + } + + /** + * Disables the checking on the count of cycles + * + */ + public void disableCycleTimeOut () + { + this.isTimeOutEnable = false; + } +} diff --git a/src/uk/ac/vamsas/client/simpleclient/EventGeneratorThread.java b/src/uk/ac/vamsas/client/simpleclient/EventGeneratorThread.java index 1d1cf09..311c825 100644 --- a/src/uk/ac/vamsas/client/simpleclient/EventGeneratorThread.java +++ b/src/uk/ac/vamsas/client/simpleclient/EventGeneratorThread.java @@ -49,13 +49,13 @@ public class EventGeneratorThread { if (clientfile==null) { log.debug("Initializing clientfile Watcher"); clientfile = session.getClientWatcherElement(); - clientfile.setHandler(new WatcherCallBack() { + // handler is set in the Vamsas session +/* 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; @@ -134,6 +134,7 @@ public class EventGeneratorThread { } boolean ownsf = false; /** + * Moved to SimpleClientSessionManager * scans all watchers and fires changeEvents if necessary * @return number of events generated. */ diff --git a/src/uk/ac/vamsas/client/simpleclient/SimpleClient.java b/src/uk/ac/vamsas/client/simpleclient/SimpleClient.java index dc3df20..5f0836e 100644 --- a/src/uk/ac/vamsas/client/simpleclient/SimpleClient.java +++ b/src/uk/ac/vamsas/client/simpleclient/SimpleClient.java @@ -6,14 +6,10 @@ */ package uk.ac.vamsas.client.simpleclient; -import java.beans.EventHandler; -import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -import java.io.BufferedReader; import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; import java.util.Hashtable; import java.util.Vector; @@ -29,13 +25,9 @@ import uk.ac.vamsas.client.InvalidSessionUrnException; import uk.ac.vamsas.client.SessionHandle; import uk.ac.vamsas.client.UserHandle; import uk.ac.vamsas.client.picking.IPickManager; -import uk.ac.vamsas.objects.core.ApplicationData; import uk.ac.vamsas.objects.core.Entry; -import uk.ac.vamsas.objects.core.LockFile; import uk.ac.vamsas.objects.core.VamsasDocument; -import uk.ac.vamsas.objects.utils.AppDataReference; import uk.ac.vamsas.objects.utils.ProvenanceStuff; -import uk.ac.vamsas.objects.utils.document.VersionEntries; /** * @author jimp @@ -240,11 +232,13 @@ public class SimpleClient implements IClient { // deregister listeners. log.debug("Stopping pickManager"); haltPickmanager(); - log.debug("Stopping EventGenerator.."); - evgen.stopWatching(); - SimpleClient.log.debug("EventGenerator halted."); + log.debug("Deregistering Client"); _session.removeClient(this); + //log.debug("Stopping EventGenerator.."); + //evgen.stopWatching(); + SimpleClient.log.debug("EventGenerator halted."); + this.cdocument = null; log.debug("finalization Complete."); } @@ -365,11 +359,10 @@ public class SimpleClient implements IClient { // Events.DOCUMENT_FINALIZEAPPDATA try { _session.writeVamsasDocument(location, vamlock); - _session.clearUnsavedFlag(); + _session.clearUnsavedFlag(); } catch (Exception e) { log.warn("Exception whilst trying to store document in "+location,e); } - vamlock.release(); } @@ -449,6 +442,12 @@ public class SimpleClient implements IClient { public void importDocument(File location) { // TODO LATER: implement SimpleClient.importDocument() log.error("importDocument is not yet implemented for a SimpleClient Session."); + + /* try { + this._session.setVamsasDocument(location); + } catch (IOException e) { + log.error("importDocument failed."); + }*/ } public IObjectUpdate getUpdateHandler(Class rootObject) { diff --git a/src/uk/ac/vamsas/client/simpleclient/VamsasSession.java b/src/uk/ac/vamsas/client/simpleclient/VamsasSession.java index b30be93..b066f88 100644 --- a/src/uk/ac/vamsas/client/simpleclient/VamsasSession.java +++ b/src/uk/ac/vamsas/client/simpleclient/VamsasSession.java @@ -2,21 +2,16 @@ package uk.ac.vamsas.client.simpleclient; import java.io.File; import java.io.IOException; -import java.io.PrintStream; -import java.io.PrintWriter; import java.io.RandomAccessFile; -import java.io.Writer; -import java.util.Enumeration; -import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Appender; -import org.apache.log4j.Logger; import org.apache.log4j.FileAppender; +import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; import uk.ac.vamsas.client.ClientHandle; +import uk.ac.vamsas.client.Events; import uk.ac.vamsas.client.IClient; import uk.ac.vamsas.client.UserHandle; /** @@ -86,6 +81,16 @@ public class VamsasSession { private SimpleSessionManager sessionManager = null; /** + * Count of cycles before considering the current client as the last one of the session (if no other client registered as active ) + */ + private final int watchCycleCountBeforeLastClient = 2 ; + + /** + * time between checking + */ + public int WATCH_SLEEP=30; + + /** * called to clear update flag after a successful offline storage event */ protected void clearUnsavedFlag() { @@ -364,9 +369,65 @@ public class VamsasSession { clist.addClient(client.getClientHandle()); getClientWatcherElement().enableWatch(); log.debug("Added."); + log.debug("Register Client as Active."); + + //tracks modification to the client list and readds client to the list + getClientWatcherElement().setHandler(new AddClientWatchCallBack(client)); } } + /** + * Handler for the client watcher. + * + * If (the current client is not in the client list, it is added again;) + */ + private class AddClientWatchCallBack implements WatcherCallBack + { + + private IClient client ; + + /** + *Inits the handler with the client to check in the list + * @param client client to monitor in the client list + */ + protected AddClientWatchCallBack (IClient client) + { + this.client = client; + } + + /** + * If the client list is modified, checks if the current is still in the list. otherwise, readds ti. + * @return true to enable watcher, or false to disable it in future WatcherThread cycles. + */ + public boolean handleWatchEvent(WatcherElement watcher, Lock lock) + { + boolean isWatchEnable = watcher.isWatchEnabled(); + if (client != null) + { + + + //checks if the client is not already in the lists + ClientHandle[] cl = clist.retrieveClientList(lock);//clist.retrieveClientList(); + boolean found = false; + if (cl != null) + { + for (int chi = cl.length-1; !found && chi > -1; chi--) { + found = cl[chi].equals(this.client.getClientHandle()); + } + } else + if (! found) + { + if( log.isDebugEnabled()) + log.debug("the client has not been found in the list. Adding it again :"+cl); + addClient(client); + } + + } + + return isWatchEnable; + } + } + /** * * removes a client from the current session @@ -376,18 +437,32 @@ public class VamsasSession { * * @param client client to remove */ - protected void removeClient(IClient client) + protected void removeClient(SimpleClient client)//IClient client) { if (client == null) { log.error("Null client passed to removeClient"); return; } - SessionFileWatcherElement cwe=getClientWatcherElement(); + ClientSessionFileWatcherElement cwe=getClientWatcherElement(); if (cwe!=null && cwe.isWatchEnabled()) { cwe.haltWatch(); }; - clist.removeClient(client.getClientHandle(),null); + //set handler to check is the the last active client of the session + //Wait for several watchers cycle to see if the current client was the last client active in the session. + //if yes, close the session + + getClientWatcherElement().setHandler(new RemoveClientWatchCallBack (client)); + getClientWatcherElement().setTimeoutBeforeLastCycle(this.watchCycleCountBeforeLastClient); + log.info("remove client from list"); + clist.clearList(); + if (cwe!=null) { + cwe.enableWatch(); + } + + + + /*clist.removeClient(client.getClientHandle(),null); if (this.clist.retrieveClientList() == null|| this.clist.retrieveClientList().length<1) {//assume it is the last client has been removed shutting down session slog.info("last client removed: removing session"); @@ -395,15 +470,81 @@ public class VamsasSession { this.getSessionManager().removeSession(client.getSessionHandle()); } else - { - int active=clist.retrieveClientList().length; - log.debug("Still "+active+" active clients"); - slog.info("Still "+active+" active clients"); - } - if (cwe!=null) { - cwe.enableWatch(); - } + { + int active=clist.retrieveClientList().length; + log.debug("Still "+active+" active clients"); + slog.info("Still "+active+" active clients"); + }*/ + } + + /** + * Handler for the client watcher. after a client have been removed + * + * Checks if the client is not the last active one. + * + * If (the current client is not in the client list readd it;) + */ + private class RemoveClientWatchCallBack implements WatcherCallBack + { + + private SimpleClient client ; + + /** + *Inits the handler with the client to check in the list + * @param client client to monitor in the client list + */ + protected RemoveClientWatchCallBack (SimpleClient client) + { + this.client = client; + } + + /** + * If the client list is modified, checks if the current is still in the list. otherwise, readds ti. + * @return true to enable watcher, or false to disable it in future WatcherThread cycles. + */ + public boolean handleWatchEvent(WatcherElement watcher, Lock lock) + { + // boolean isWatchEnable = watcher.isWatchEnabled(); + if (client != null) + { + + //checks if the client is not already in the lists + ClientHandle[] cl = clist.retrieveClientList(lock);//clist.retrieveClientList(); +// ask to the client to cpoy application data into the document + client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null, client,null); + + if(cl == null || cl.length<1 ) + {//no client has registered as active + //the client is the last one, so close current session + log.info("last client removed: closing session"); + +// close document + client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client,null); + log.debug("close document request done"); + + getSessionManager().removeSession(client.getSessionHandle()); + log.debug("Session removed"); + } + else + { + log.debug("not the last client found "); +// ask to the client to cpoy application data into the document + // client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null, client,null); + + } + // watcher.haltWatch(); + log.debug("Stopping EventGenerator.."); + client.evgen.stopWatching(); + + + } + watcher.setHandler(null);//Do not check if the client is the last client. watcher will shutdown anyway + + return false; + } + } + /** * @return the sessionManager */ @@ -422,10 +563,11 @@ public ClientsFile getStoreDocFile() { } return storedocfile; } -SessionFileWatcherElement clistWatchElement=null; -public SessionFileWatcherElement getClientWatcherElement() { + +ClientSessionFileWatcherElement clistWatchElement=null; +public ClientSessionFileWatcherElement getClientWatcherElement() { if (clistWatchElement==null) { - clistWatchElement=new SessionFileWatcherElement(clist,null); + clistWatchElement=new ClientSessionFileWatcherElement(clist,null); } return clistWatchElement; } diff --git a/src/uk/ac/vamsas/client/simpleclient/WatcherElement.java b/src/uk/ac/vamsas/client/simpleclient/WatcherElement.java index 54a31be..87b50db 100644 --- a/src/uk/ac/vamsas/client/simpleclient/WatcherElement.java +++ b/src/uk/ac/vamsas/client/simpleclient/WatcherElement.java @@ -10,7 +10,7 @@ public abstract class WatcherElement { /** * set this to false to stop the thread */ - private boolean watchForChange = true; + protected boolean watchForChange = true; /** * true when the handler is being called for this watcher */ @@ -78,7 +78,7 @@ public abstract class WatcherElement { } if (doclock==null) return false; - handlerCalled=true; + /* handlerCalled=true; if (log.isDebugEnabled()) log.debug("Triggering watchEvent for change on "+watcher.getSubject()); boolean finish=!handler.handleWatchEvent(this, doclock); @@ -87,11 +87,30 @@ public abstract class WatcherElement { haltWatch(); else enableWatch(); - handlerCalled=false; + handlerCalled=false;*/ + this.callHandler(doclock); return true; } /** + * Calls the current eventhandler + * + * @param doclock the lock on the watch file + */ + protected void callHandler(Lock doclock) + { + if (log.isDebugEnabled()) + log.debug("Triggering watchEvent for change on "+watcher.getSubject()); + boolean finish=!handler.handleWatchEvent(this, doclock); + doclock=null; // TODO: check that lock should really be released rather than dereferenced + if (finish) + haltWatch(); + else + enableWatch(); + handlerCalled=false; + } + + /** * @return the handler */ public WatcherCallBack getHandler() { -- 1.7.10.2