finished the Appdata/ClientDocument update mechanism. Untested!
[vamsas.git] / src / org / vamsas / client / simpleclient / SimpleClient.java
index 37595b4..970bcfa 100644 (file)
@@ -13,6 +13,7 @@ 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;
 
@@ -22,9 +23,16 @@ import org.vamsas.client.ClientHandle;
 import org.vamsas.client.Events;
 import org.vamsas.client.IClient;
 import org.vamsas.client.IClientDocument;
+import org.vamsas.client.InvalidSessionUrnException;
 import org.vamsas.client.SessionHandle;
 import org.vamsas.client.UserHandle;
+import org.vamsas.objects.core.ApplicationData;
+import org.vamsas.objects.core.Entry;
+import org.vamsas.objects.core.LockFile;
 import org.vamsas.objects.core.VamsasDocument;
+import org.vamsas.objects.utils.AppDataReference;
+import org.vamsas.objects.utils.ProvenanceStuff;
+import org.vamsas.objects.utils.document.VersionEntries;
 
 /**
  * @author jimp
@@ -39,12 +47,14 @@ public class SimpleClient implements IClient {
   protected VamsasSession _session;
   protected ClientHandle client = null;
   protected EventGeneratorThread evgen = null;
-  IdFactory vorba = null;
-  private void makeVorbaIdFactory() {
-    if (vorba==null) {
-      vorba = new IdFactory(getSessionHandle(), client, user);
-    } else
-      log.debug("Vorba Id factory exists already.");
+  protected ClientDocument cdocument = null;
+  /**
+   * construct a transient IdFactory instance - this should last only as long as the 
+   * SimpleClient object holds the lock on the vamsas document being created/manipulated.
+   * @return
+   */
+  private IdFactory makeVorbaIdFactory() {
+    return new IdFactory(getSessionHandle(), client, user);
   }
   
   /**
@@ -54,12 +64,17 @@ public class SimpleClient implements IClient {
    * @param client
    * @param sess
    */
-  protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess) {
+  protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess) throws InvalidSessionUrnException {
     // TODO: validate user/client/session
     _session = sess;
     this.user = user;
     this.client = client;
-    session = new SessionUrn(_session);
+    try {
+      session = new SessionUrn(_session);
+    } catch (MalformedURLException e) {
+      log.error("Couldn't form a valid SessionUrn object!",e);
+      throw new InvalidSessionUrnException(_session.toString());
+    }
   }
   /**
    * construct new session by importing objects from an existing vamsas document
@@ -71,23 +86,24 @@ public class SimpleClient implements IClient {
    */
   protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess, File importingArchive) throws Exception {
     this(user, client, sess);
-    makeVorbaIdFactory();
     VamsasArchive sessdoc = _session.getVamsasDocument();
     try {
       VamsasArchiveReader odoc = new VamsasArchiveReader(importingArchive);
-      SimpleDocument sdoc = new SimpleDocument(vorba);
+      SimpleDocument sdoc = new SimpleDocument(makeVorbaIdFactory());
       VamsasDocument doc = sdoc.getVamsasDocument(odoc);
-      sessdoc.putVamsasDocument(doc, vorba);
+      sessdoc.putVamsasDocument(doc, sdoc.vorba);
       sessdoc.closeArchive();
     } catch (Exception e) {
       sessdoc.cancelArchive();
       // write a dummy archive
+      _session.slog.info("Exception when importing document data from "+importingArchive);
       throw new Exception("Failed to import data from "+importingArchive, e);
     }
   }
+  
   /*
    * (non-Javadoc)
-   * TODO: LATER: check that build substitution variables are correct
+   * LATER: check that build substitution variables are correct
    * @see org.vamsas.client.IClient#getAbout()
    */
   public String getAbout() {
@@ -131,7 +147,23 @@ public class SimpleClient implements IClient {
   public UserHandle getUserHandle() {
     return user;
   }
-  
+  /**
+   * 
+   * @return user field for a provenance entry
+   */
+  protected String getProvenanceUser() {
+    return new String(user.getFullName()+" ["+client.getClientUrn()+"]");
+  }
+  /**
+   * construct a provenance entry for this client with the specified action string.
+   * @param action
+   * @return properly completed provenance entry
+   */
+  protected Entry getProvenanceEntry(String action) {
+    // VAMSAS: modify schema to allow referencing of user field (plus other issues, ClientUrn field, machine readable action, input parameters, additional data generated notes
+    Entry prov = ProvenanceStuff.newProvenanceEntry(getProvenanceUser(), action);
+    return prov;
+  }
   private Hashtable handlers = initHandlers();
   
   private Vector listeners = new Vector();
@@ -181,56 +213,85 @@ public class SimpleClient implements IClient {
     // mark this instance as finalized
   }
   
-  /**
-   * extract data appropriate for client, session and user
-   * from vamsas document.
-   * @return application's byte array
-   */
-  private byte[] getApplicationData() {
-    // TODO: extract correct byte object from Jar and return it to application.
-    return null;
-  }
-
   /*
    * (non-Javadoc)
    * 
    * @see org.vamsas.client.IClient#getClientDocument()
    */
   public IClientDocument getClientDocument() throws IOException {
-    
-    makeVorbaIdFactory();
-    VamsasArchive va = _session.getVamsasDocument();
+    if (cdocument!=null) {
+      // cdocument is non-nill if the ClientDocument.finalise() method hasn't been called.
+      return cdocument;
+    }
+    VamsasArchive va = null;
+    try {
+      // LATER: bail out if it takes too long to get the lock ?
+      va = _session.getVamsasDocument();
+    }
+    catch (IOException e) {
+      throw new IOException("Failed to get lock on session document");
+    }
+    VamsasDocument doc=null;
+    IdFactory vorba = null;
     // TODO: reduce size of vorba ids generated from these parameters to IdFactory (mainly sessionHandle rationalization ?)
     try {
-      va.getOriginalVamsasDocument(va, vorba);
-      // if session currently holds data - read it in.
+      va.setVorba(vorba=makeVorbaIdFactory());
+      // if session currently holds data - read it in - or get a dummy
+      _session.slog.debug("Accessing document");
+      doc = 
+        va.getVamsasDocument(getProvenanceUser(),
+            "created new session document.", null);
+      if (doc!=null)
+        _session.slog.debug("Successfully retrieved document.");
+      else
+        log.error("Unexpectedly retrieved null document!");
     }
     catch (Exception e) {
-      
+      log.error("Failed to get session document for session directory '"+_session.sessionDir+"'", e);
+      throw new IOException("Failed to get session document for session directory '"+_session.sessionDir+"'");
     }
-    //ClientDocument cdoc = new ClientDocument();
+    // Construct the IClientDocument instance
     
-    Object[] vdoc;//  TODO: = getVamsasDocument(new Reader());
-    // ClientDocument cdoc = new ClientDocument(getApplicationData(),
-    // ((VamsasDocument) vdoc[0]).getVAMSAS(), (Hashtable) vdoc[1], this);
-    // 
-    return null;
+    ClientDocument cdoc = new ClientDocument(doc, va, vorba, this);
+    return cdoc;
   }
   
   /*
    * (non-Javadoc)
-   * 
+   * @throws Errors for invalid newdoc parameter
    * @see org.vamsas.client.IClient#updateDocument(org.vamsas.client.IClientDocument)
    */
   public void updateDocument(IClientDocument newdoc) {
     if (!(newdoc instanceof ClientDocument)) {
       throw new Error("Invalid IClientDocument instance for SimpleClient.");
     }
-    
-    // try to update the sessionFile
-    
-    // write the appHandle to the lastupdate file.
-    
+    if (cdocument==null)
+      throw new Error("Client Error - updateDocument() called before getClientDocument().");
+    if (newdoc!=cdocument)
+      throw new Error("Client Error - SimpleClient.updateDocument() can only take the IClientDocument instance returned from SimpleClient.getClientDocument()");
+    if (!cdocument.isModified()) {
+      if (log.isDebugEnabled())
+        log.debug("updateDocument for "+session.getSessionUrn()+" with unmodified IClientDocument.");
+    } else {
+      try {
+        if (!cdocument.updateSessionDocument()) {
+          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 archive.
+          _session.slog.warn("Session Document updated but may not be valid (false return from org.vamsas.simpleclient.ClientDocument.updateSessionDocument()");
+        }
+      }
+      catch (IOException e) {
+        log.warn("IO Problems when updating document!",e);
+        _session.slog.error("IO problems when attempting to update document.");
+      }
+    }
+    // garbage collect the ClientDocument instance.
+    try {
+      cdocument.finalize();
+    } catch (Throwable e) {
+      log.error("Exception when trying to garbage collect ClientDocument for "+session.getSessionUrn(), e);
+    }
+    cdocument = null;
   }
   
   /*
@@ -239,15 +300,17 @@ public class SimpleClient implements IClient {
    * @see org.vamsas.client.IClient#storeDocument(java.io.File)
    */
   public void storeDocument(File location) {
-
+    
     // write storeDocument file to inform other clients that they should raise
     Lock vamlock = evgen.want_to_store();
     // Events.DOCUMENT_FINALIZEAPPDATA
     try {
       _session.writeVamsasDocument(location, vamlock);
+      _session.clearUnsavedFlag();
     } catch (Exception e) {
       log.warn("Exception whilst trying to store document in "+location,e);
     }
+    
     vamlock.release();
   }
   
@@ -271,17 +334,27 @@ public class SimpleClient implements IClient {
    * @see org.vamsas.client.IClient#pollUpdate()
    */
   public void pollUpdate() {
-
+    
     if (evgen==null) {
       log.warn("pollUpdate called on incomplete SimpleClient object.");
       return;
     }
+    
+    if (!evgen.isAlive()) {
+      log.warn("pollUpdate called before joinSession()");
+      try {
+        joinSession();
+      } catch (Exception e) {
+        log.error("Unexpected exception on default call to joinSession",e);
+      }
+    }
+    
     //TODO ensure event generator robustly handles these interrupts.
     log.debug("interrrupting event generator.");
     evgen.interrupt();
     log.debug("interrrupted event generator.");
   }
-
+  
   /* (non-Javadoc)
    * @see org.vamsas.client.IClient#joinSession()
    */
@@ -300,7 +373,10 @@ public class SimpleClient implements IClient {
       log.warn("Failed to start EventGenerator thread.");
       throw new Exception("Failed to start event generator thread - client cannot be instantiated.");
     }
-    
+    if (evgen.countHandlersFor(Events.DOCUMENT_CREATE)>0) {
+      //TODO: is this application connecting to a newly created session document ?
+      //evgen.raise(Events.DOCUMENT_CREATE);
+    }
   }