ensured single instance of vamsasarchive io handler to prevent lock contention.
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / VamsasSession.java
index e2e170c..05014ab 100644 (file)
@@ -1,20 +1,22 @@
 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 java.util.Enumeration;
 
 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.PatternLayout;
 
 import uk.ac.vamsas.client.ClientHandle;
+import uk.ac.vamsas.client.IClient;
 import uk.ac.vamsas.client.UserHandle;
 /**
  * Does all the IO operations for a SimpleClient instance accessing 
@@ -79,6 +81,9 @@ public class VamsasSession {
    */
   public static final String MODIFIEDDOC_FILE="modified";
 
+  
+  private SimpleSessionManager sessionManager = null;
+  
   /**
    * called to clear update flag after a successful offline storage event
    */
@@ -115,22 +120,22 @@ public class VamsasSession {
    * @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");
+    }
   }
   
   /**
@@ -158,20 +163,17 @@ public class VamsasSession {
     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.");
@@ -199,19 +201,21 @@ public class VamsasSession {
       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();
   }
   /**
@@ -221,26 +225,21 @@ public class VamsasSession {
   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));
 
   }
   /**
@@ -251,6 +250,7 @@ public class VamsasSession {
    * @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);
@@ -287,14 +287,17 @@ public class VamsasSession {
     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.
@@ -302,16 +305,36 @@ public class VamsasSession {
    * @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;
   }
   /**
-   * create a uniquely named file in the session Directory
+   * 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
    * @param suff Suffix for name
@@ -323,6 +346,90 @@ public class VamsasSession {
     SessionFile tempFile = new SessionFile(tfile);
     return tempFile;
   }
+  
+  /**
+   * add a IClient to the session
+   * 
+   * add the client to the client list file
+   * @param client client to add to the session
+   */
+  protected void addClient(IClient client)
+  {
+    if (client == null)
+      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.");
+    }
+  }
+  
+/**
+ *  
+ * removes a client from the current session
+ *  removes the client from the session client list
+ *  if the client is the last one from the session  (ClientList), the current session is removed 
+ *  from active session list.
+ *  
+ * @param client client to remove
+ */
+  protected void removeClient(IClient client)
+  {
+    if (client == null)
+      {
+        log.error("Null client passed to removeClient");
+        return;
+      }
+    SessionFileWatcherElement cwe=getClientWatcherElement();
+    if (cwe!=null && cwe.isWatchEnabled()) {
+      cwe.haltWatch();
+    } else {
+      cwe=null;
+    }
+    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");
+        log.debug("last client removed: removing session");
+        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();
+    }
+  }
+/**
+ * @return the sessionManager
+ */
+protected SimpleSessionManager getSessionManager() {
+  return sessionManager;
+}
+/**
+ * @param sessionManager the sessionManager to set
+ */
+protected void setSessionManager(SimpleSessionManager sessionManager) {
+  this.sessionManager = sessionManager;
+}
+public ClientsFile getStoreDocFile() {
+  if (storedocfile==null) {
+    
+  }
+  return storedocfile;
+}
+SessionFileWatcherElement clistWatchElement=null;
+public SessionFileWatcherElement getClientWatcherElement() {
+  if (clistWatchElement==null) {
+    clistWatchElement=new SessionFileWatcherElement(clist,null);
+  }
+  return clistWatchElement;
+}
 }