made SimpleClientConfig externally configurable and added mechanism to prevent invali...
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / SimpleClient.java
index dc3df20..d13fe24 100644 (file)
@@ -6,14 +6,14 @@
  */
 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.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+
+import java.nio.channels.OverlappingFileLockException;
 import java.util.Hashtable;
 import java.util.Vector;
 
@@ -25,17 +25,14 @@ import uk.ac.vamsas.client.Events;
 import uk.ac.vamsas.client.IClient;
 import uk.ac.vamsas.client.IClientDocument;
 import uk.ac.vamsas.client.IObjectUpdate;
+import uk.ac.vamsas.client.InvalidSessionDocumentException;
 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
@@ -51,6 +48,13 @@ public class SimpleClient implements IClient {
   protected ClientHandle client = null;
   protected EventGeneratorThread evgen = null;
   protected ClientDocument cdocument = null;
+  
+  
+  
+  
+  private Lock activeClientFilelock = null;
+  private  File clientlockFile = null;
+  
   /**
    * object hash table that persists in each client holding vorbaIds and hash values after a document write
    */
@@ -91,31 +95,22 @@ public class SimpleClient implements IClient {
       
   }
   /**
-   * construct new session by importing objects from an existing vamsas document
+   * construct new SimpleClientsession by importing objects from an existing vamsas document
    * @param user
    * @param client
    * @param sess
    * @param importingArchive
    * @throws Exception IOExceptions for Session IO problems, and general Exception if importing document is invalid.
-   */
   protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess, File importingArchive) throws Exception {
     this(user, client, sess);
-    VamsasArchive sessdoc = _session.getVamsasDocument();
-    try {
-      VamsasArchiveReader odoc = new VamsasArchiveReader(importingArchive);
-      SimpleDocument sdoc = new SimpleDocument(makeVorbaIdFactory());
-      VamsasDocument doc = sdoc.getVamsasDocument(odoc);
-      sessdoc.putVamsasDocument(doc, sdoc.vorba);
-      sessdoc.closeArchive();
-      log.debug("Imported new vamsas data from "+importingArchive);
-    } catch (Exception e) {
-      sessdoc.cancelArchive();
-      // write a dummy iohandler
-      _session.slog.info("Exception when importing document data from "+importingArchive);
-      log.warn("While importing session data from existing archive in "+importingArchive, e);      
-      throw new Exception("Failed to import data from "+importingArchive, e);
+    if (log.isDebugEnabled())
+    {
+      log.debug("Attempting to overwrite session document with file: "+importingArchive);
     }
+  // TODO: write provenance entry for new session indicating the import.
+    
   }
+   */
   
   /*
    * (non-Javadoc)
@@ -240,11 +235,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();
+    this.cdocument = null;
+    SimpleClient.log.debug("EventGenerator halted.");
     log.debug("finalization Complete.");
   }
   
@@ -308,32 +305,30 @@ public class SimpleClient implements IClient {
       throw new Error("Client Error - updateDocument() called before getClientDocument() on this SimpleClient instance.");
     if (newdoc!=cdocument)
       throw new Error("Client Error - SimpleClient.updateDocument() can only take the IClientDocument instance returned from SimpleClient.getClientDocument()");
-
-    if (evgen.isDocumentWatchEnabled())
-      throw new Error("Client Error - or Library Bug : Document watcher still enabled whilst ClientDocument instance exists.");
     
-    if (!cdocument.isModified()) {
-      // client document is silently got rid of, with no session update events.
-      if (log.isDebugEnabled())
-        log.debug("updateDocument for "+session.getSessionUrn()+" with unmodified IClientDocument (skipping the write)");
+    if (evgen.isDocumentWatchEnabled())
+      throw new Error("Probable Client Error (did you remember to call SimpleClient.updateDocument(clientdoc) at the end of the document update handler?) - or Library Bug : Document watcher still enabled whilst ClientDocument instance exists.");
+    if (cdocument.isInvalidModification())
+    {
+      log.info("Client has corrupted the vamsas document. It will not be written back to the session - sorry.");
+      // TODO: modify updateDocument signature: We should really raise an exception here to tell the client that it broke the datamodel.
     } else {
-      try {
-        boolean updated=cdocument.updateSessionDocument();
-        if (!updated) {
-          log.warn("Session document did not update properly for session directory "+_session.sessionDir);
-          // cdocument.archive.cancelArchive(); // LATER: could be done - would need to prevent updateSessionDocument closing the iohandler.
-          _session.slog.warn("Session Document updated but may not be valid (false return from org.vamsas.simpleclient.ClientDocument.updateSessionDocument()");
-        } else {
-               log.debug("Document update successful.");
-        }
-        _session.setUnsavedFlag();
-      }
-      catch (IOException e) {
-        log.warn("IO Problems when updating document!",e);
-        _session.slog.error("IO problems when attempting to update document.");
+      // actually try to write - if necessary.
+      if (!cdocument.isModified()) {
+        // client document is silently got rid of, with no session update events.
+        if (log.isDebugEnabled())
+        log.debug("updateDocument for "+session.getSessionUrn()+" with unmodified IClientDocument (skipping the write)");
+      } else {
+        writeSessionDocument();
       }
     }
-    // garbage collect the ClientDocument instance.
+    // release locks, reset and start to receive events again
+    tidyAwaySessionDocumentState(); 
+  }
+  /**
+   * garbage collect the ClientDocument instance and re-enable watchers.
+   */
+  protected void tidyAwaySessionDocumentState() {
     try {
       log.debug("Finalizing ClientDocument instance.");
       cdocument.finalize();
@@ -350,7 +345,28 @@ public class SimpleClient implements IClient {
       _session.slog.error("IO problems when attempting to release lock on session document.");
     }
   }
-  
+
+  /**
+   * write the cdocument instance to the session for real.
+   */
+  private void writeSessionDocument() {
+    try {
+      boolean updated=cdocument.updateSessionDocument();
+      if (!updated) {
+        log.warn("Session document did not update properly for session directory "+_session.sessionDir);
+        // cdocument.archive.cancelArchive(); // LATER: could be done - would need to prevent updateSessionDocument closing the iohandler.
+        _session.slog.warn("Session Document updated but may not be valid (false return from org.vamsas.simpleclient.ClientDocument.updateSessionDocument()");
+      } else {
+        log.debug("Document update successful.");
+      }
+      _session.setUnsavedFlag();
+    }
+    catch (IOException e) {
+      log.warn("IO Problems when updating document!",e);
+      _session.slog.error("IO problems when attempting to update document.");
+    }
+  }
+
   /*
    * (non-Javadoc)
    * 
@@ -365,11 +381,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();
   }
   
@@ -448,7 +463,15 @@ public class SimpleClient implements IClient {
    */
   public void importDocument(File location) {
     // TODO LATER: implement SimpleClient.importDocument()
+    // if (numberOfClients<1 or document file is empty/new, then can import directly.)
+    // more complex if data is already present in document. Could have a 'clearSession' method, too, or dump and overwrite instead.
     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) {
@@ -494,4 +517,84 @@ public class SimpleClient implements IClient {
       pickmanager = new SimplePickManager(new uk.ac.vamsas.client.picking.SocketManager());
     }
   }
+  
+  
+  protected void releaseActiveClientFile() throws IOException
+  {
+   
+    log.debug("Releasing active client locks");
+    if( activeClientFilelock != null)
+    {// Release the lock
+      log.debug("Releasing lock on active client lock file");
+      activeClientFilelock.release();
+      log.debug("ReleaseActiveClientFile called when client has no lock on its clientLockFile");
+      activeClientFilelock = null;
+    } else {
+      log.debug("ReleaseActiveClientFile called when client has no lock on its clientLockFile");
+    }
+    if (this.clientlockFile != null)
+    {
+      log.debug("trying to delete active client lock file");
+      if (this.clientlockFile.exists())
+      {
+        this.clientlockFile.delete();
+        log.debug("deleted active client lock file");
+      }
+    } else {
+      log.debug("ReleaseActiveClientFile called when client has no clientLockFile");
+    }
+  }
+  
+  protected void  createActiveClientFile() throws IOException
+  {
+    if(this.clientlockFile != null )return; 
+   log.debug("createActiveClientFile");
+    //create, if need,  subdirectory to contain client files
+   File clientlockFileDir = new File ( this.get_session().sessionDir, this.get_session().clientFileDirectory);
+    if( !clientlockFileDir.exists())
+      {//the directory does not exist, create it
+        if (! clientlockFileDir.mkdirs())
+        {
+          throw new IOException("Failed to create sub directory to session directory  for client lock files'"+clientlockFileDir.getAbsolutePath()+"'");
+        }
+      }
+    else
+    {
+      if (!(clientlockFileDir.isDirectory() && clientlockFileDir.canWrite()))
+      {
+        throw new IOException("Directory  for client lock files is not a directory or is not accessibl: '"+clientlockFileDir.getAbsolutePath()+"'");
+       }
+    }
+    this.clientlockFile = new File (clientlockFileDir, this.getClientHandle().getClientUrn().replaceAll("[:;/\\\\]+",""));
+    
+    log.debug("Creating active client lock file "+ this.clientlockFile.getAbsolutePath());
+    Lock clientLock = uk.ac.vamsas.client.simpleclient.LockFactory.getLock(clientlockFile, false);
+    if (clientLock==null || !clientLock.isLocked())
+    {
+      log.fatal("IMPLEMENTATION ERROR: Couldn't get a lock for the client lock file "+clientlockFile);
+    }
+    activeClientFilelock = clientLock;
+  }
+  /**
+   * @return the clientlockFile
+   */
+  protected File getClientlockFile() {
+    return clientlockFile;
+  }
+  /**
+   * 
+   * @return the lock for the client in the session
+   */
+  protected Lock getClientLock() {
+    return activeClientFilelock;
+  }
+  SimpleClientConfig _config = null;
+  public SimpleClientConfig getSimpleClientConfig() {
+    if (_config==null)
+    {
+      _config = new SimpleClientConfig();
+    }
+    return _config;
+  }
+
 }