package uk.ac.vamsas.client.simpleclient;
-import java.io.BufferedWriter;
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 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;
/**
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() {
* @throws IOException
*/
private void initLog() throws IOException {
+ // TODO: fix session event logging
// LATER: make dedicated appender format for session log.
- Appender app = slog.getAppender("SESSION_LOG");
- // slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
-
- //Appender app = slog.getAppender("SESSION_LOG");
- if (app == null) log.info("No appender for SESSION_LOG");
+ /*Appender app = slog.getAppender("log4j.appender.SESSIONLOG");
+ // slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
+ // slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
+ for (Enumeration e = slog.getAllAppenders() ; e.hasMoreElements() ;) {
+ System.out.println(e.nextElement());
+
+ }*/
- if (slog!= null && app != null)
- {
- if (app instanceof FileAppender)
- {
- File sessionLogFile = new File(this.sessionDir, ((FileAppender)app).getFile());
- slog.addAppender(new FileAppender(app.getLayout(), sessionLogFile.getAbsolutePath()));
- }
- // slog.removeAppender("SESSION_LOG");
- }
+ if (slog!= null ) {
+ File sessionLogFile = new File(this.sessionDir, SESSION_LOG);
+ slog.addAppender(new FileAppender(new PatternLayout("%-4r [%t] %-5p %c %x - %m%n"), sessionLogFile.getAbsolutePath(), true));
+ } else {
+ log.info("No appender for SessionLog");
+ }
}
/**
if (sessionDir1.exists()) {
if (!sessionDir1.isDirectory() || !sessionDir1.canWrite() || !sessionDir1.canRead())
throw new IOException("Cannot access '"+sessionDir1+"' as a read/writable Directory.");
- if (checkSessionFiles(sessionDir1)) {
- createSessionFiles();
- }
- // session files exist in the directory
- this.sessionDir = sessionDir1;
- initSessionObjects();
- slog.debug("Initialising additional VamsasSession instance");
- log.debug("Attached to VamsasSession in "+sessionDir1);
+ if (!checkSessionFiles(sessionDir1))
+ log.warn("checkSessionFiles() returned false. Possible client implementation error");
+ this.sessionDir = sessionDir1;
+ initSessionObjects();
+ slog.debug("Initialising additional VamsasSession instance");
+ log.debug("Attached to VamsasSession in "+sessionDir1);
//}
} else {
// start from scratch
if (!sessionDir1.mkdir())
throw new IOException("Failed to make VamsasSession directory in "+sessionDir1);
- this.sessionDir = sessionDir1;
createSessionFiles();
initSessionObjects();
slog.debug("Session directory created.");
throw new IOException("Invalid call to createSessionFiles() with null sessionDir");
File c_file = new File(sessionDir,CLIENT_LIST);
File v_doc = new File(sessionDir,VAMSAS_OBJ);
- if (c_file.createNewFile())
+ if (!c_file.exists() && c_file.createNewFile())
log.debug("Created new ClientFile "+c_file); // don't care if this works or not
- if (v_doc.createNewFile())
+ if (!v_doc.exists() && v_doc.createNewFile())
log.debug("Created new Vamsas Session Document File "+v_doc);
}
/**
* construct SessionFile objects and watchers for each
*/
private void initSessionObjects() throws IOException {
+ createSessionFiles();
if (clist!=null || vamArchive!=null)
throw new IOException("initSessionObjects called for initialised VamsasSession object.");
clist = new ClientsFile(new File(sessionDir,CLIENT_LIST));
vamArchive = new VamsasFile(new File(sessionDir,VAMSAS_OBJ));
+ storedocfile=new ClientsFile(new File(sessionDir, CLOSEANDSAVE_FILE));
initLog();
}
/**
public FileWatcher getClientWatcher() {
return new FileWatcher(clist.sessionFile);
}
- FileWatcher session_doc_watcher=null;
/**
* make a new watcher object for the vamsas Document
* @return new ClientFile watcher instance
*/
public FileWatcher getDocWatcher() {
- if (session_doc_watcher==null)
- return session_doc_watcher = new FileWatcher(vamArchive.sessionFile);
return new FileWatcher(vamArchive.sessionFile);
}
FileWatcher store_doc_file=null;
+ public ClientsFile storedocfile=null;
/**
* make a new watcher object for the messages file
- * (thread safe - keeps a reference to the first watcher)
* @return new watcher instance
*/
public FileWatcher getStoreWatcher() {
- if (store_doc_file==null)
- return store_doc_file = new FileWatcher(new File(CLOSEANDSAVE_FILE));
- return new FileWatcher(new File(CLOSEANDSAVE_FILE));
+ return new FileWatcher(new File(sessionDir,CLOSEANDSAVE_FILE));
}
/**
* @return
*/
public void addStoreDocumentRequest(ClientHandle client, UserHandle user) throws IOException {
+ // TODO: replace this with clientsFile mechanism
SessionFile sfw = new SessionFile(new File(sessionDir, CLOSEANDSAVE_FILE));
while (!sfw.lockFile())
log.debug("Trying to get lock for "+CLOSEANDSAVE_FILE);
if (extlock==null && !vamArchive.lockFile())
while (!vamArchive.lockFile())
log.info("Trying to get lock for "+vamArchive.sessionFile);
- // TODO: LATER: decide if session archive provenance should be written in vamsasDocument file for this export.
+ // TODO: LATER: decide if a provenance entry should be written in the exported document recording the export from the session
newdoc.updateFrom(extlock, vamArchive);
// LATER: LATER: fix use of updateFrom for file systems where locks cannot be made (because they don't have a lockManager, ie NFS/Unix, etc).
vamArchive.unLock();
newdoc.unlockFile();
log.debug("Transfer complete.");
}
-
+ /**
+ * extant archive IO handler
+ */
+ VamsasArchive _va=null;
/**
* Creates a VamsasArchive Vobject for accessing and updating document
* Note: this will lock the Vamsas Document for exclusive access to the client.
* @throws IOException if locks fail or vamsas document read fails.
*/
protected VamsasArchive getVamsasDocument() throws IOException {
- // TODO: check we haven't already done this once
- if (!vamArchive.lockFile())
+ // check we haven't already done this once - probably should be done by caller
+ if (_va!=null)
+ return _va;
+ // patiently wait for a lock on the document. (from ArchiveClient.getUpdateable())
+ long tries=5000;
+ while (vamArchive.getLock()==null && --tries>0) {
+// Thread.sleep(1);
+ log.debug("Trying to get a document lock for the "+tries+"'th time.");
+ }
+ if (tries==0)
throw new IOException("Failed to get lock for vamsas archive.");
-
+
VamsasArchive va = new VamsasArchive(vamArchive.sessionFile, false, true, vamArchive);
-
+
return va;
}
/**
+ * Unlocks the vamsas archive session document after it has been closed.
+ * @throws IOException
+ */
+ protected void unlockVamsasDocument() throws IOException {
+ if (_va!=null)
+ _va.closeArchive();
+ _va=null;
+ if (vamArchive!=null)
+ vamArchive.unLock();
+
+ }
+ /**
* create a uniquely named uk.ac.vamsas.client.simpleclient.ClientsFile.addClient(ClientHandle)ile in the session Directory
* @see java.io.File.createTempFile
* @param pref Prefix for name
protected void addClient(IClient client)
{
if (client == null)
- this.slog.error("Try to add a null client to the session ");
- else
- this.clist.addClient(client.getClientHandle(), getClientWatcher().getChangedState());
+ slog.error("Try to add a null client to the session ");
+ else {
+ log.debug("Adding client "+client.getClientHandle().getClientUrn());
+ getClientWatcherElement().haltWatch();
+ 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 (lock== null)//no update on the list
+ return isWatchEnable;
+ 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
*
* @param client client to remove
*/
- protected void removeClient(IClient client)
+ protected void removeClient(SimpleClient client)//IClient client)
{
if (client == null)
{
- System.out.println("Try to remove a null client.");
- this.slog.error("Try to remove a null client.");
+ log.error("Null client passed to removeClient");
return;
}
- this.clist.removeClient(client.getClientHandle(), getClientWatcher().getChangedState());
+ ClientSessionFileWatcherElement cwe=getClientWatcherElement();
+ if (cwe!=null && cwe.isWatchEnabled()) {
+ cwe.haltWatch();
+ };
+ //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
- System.out.println("last client removed: removing session");
+ slog.info("last client removed: removing session");
+ log.debug("last client removed: removing session");
this.getSessionManager().removeSession(client.getSessionHandle());
}
else
- {
- this.slog.debug("Still "+this.clist.retrieveClientList().length +" active clients");
- System.out.println("Still "+(this.clist.retrieveClientList()==null?"null":this.clist.retrieveClientList().length+"") +" active clients");
- }
+ {
+ 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)
+ {
+ // if lock is null, no client has been added since last, clear.
+ //the client is then the last client
+ if (client != null && lock == 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);
+
+ // / }
+ 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
*/
protected void setSessionManager(SimpleSessionManager sessionManager) {
this.sessionManager = sessionManager;
}
+public ClientsFile getStoreDocFile() {
+ if (storedocfile==null) {
+
+ }
+ return storedocfile;
+}
+
+ClientSessionFileWatcherElement clistWatchElement=null;
+public ClientSessionFileWatcherElement getClientWatcherElement() {
+ if (clistWatchElement==null) {
+ clistWatchElement=new ClientSessionFileWatcherElement(clist,null);
+ }
+ return clistWatchElement;
+}
+/**
+ * writes a vector of vorba Ids to the session.
+ * @param modObjects
+public void setModObjectList(Vector modObjects) {
+ log.debug("Writing "+modObjects.size()+" ids to ModObjectList");
+ // TODO Auto-generated method stub
+}
+**
+ * get current list of modified objects.
+ * @return null or Vector of objects
+ *
+public Vector getModObjectList() {
+ log.debug("Reading modObjectList");
+ return null;
+}
+*/
}