finished the Appdata/ClientDocument update mechanism. Untested!
[vamsas.git] / src / org / vamsas / client / simpleclient / SimpleClient.java
index cbf5c0d..970bcfa 100644 (file)
@@ -12,36 +12,104 @@ 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;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 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
  */
 public class SimpleClient implements IClient {
-
-  UserHandle user = null;
-
-  SessionHandle session = null;
-
-  ClientHandle client = null;
+  
+  private static Log log = LogFactory.getLog(SimpleClient.class);
+  
+  protected UserHandle user = null;
+  
+  protected SessionUrn session = null;
+  protected VamsasSession _session;
+  protected ClientHandle client = null;
+  protected EventGeneratorThread evgen = null;
+  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);
+  }
+  
+  /**
+   * construct SimpleClient for user, client and VamsasSession directory
+   * use the SimpleClientFactory rather than this constructor directly. 
+   * @param user
+   * @param client
+   * @param sess
+   */
+  protected SimpleClient(UserHandle user, ClientHandle client, VamsasSession sess) throws InvalidSessionUrnException {
+    // TODO: validate user/client/session
+    _session = sess;
+    this.user = user;
+    this.client = client;
+    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
+   * @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();
+    } 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: check that build substitution variables are correct
+   * LATER: check that build substitution variables are correct
    * @see org.vamsas.client.IClient#getAbout()
    */
   public String getAbout() {
     return new String("VORBA SimpleClient version $version$ build $build$");
   }
-
+  
   /*
    * (non-Javadoc)
    * 
@@ -50,16 +118,18 @@ public class SimpleClient implements IClient {
   public String getSessionUrn() {
     return session.getSessionUrn();
   }
-
+  
   /*
    * (non-Javadoc)
    * 
    * @see org.vamsas.client.IClient#getSessionHandle()
    */
   public SessionHandle getSessionHandle() {
-    return session;
+    // TODO: eliminate SessionHandle ? need to refactor interfaces.
+    SessionHandle sh = new SessionHandle(session.getSessionUrn());
+    return sh;
   }
-
+  
   /*
    * (non-Javadoc)
    * 
@@ -68,7 +138,7 @@ public class SimpleClient implements IClient {
   public ClientHandle getClientHandle() {
     return client;
   }
-
+  
   /*
    * (non-Javadoc)
    * 
@@ -77,11 +147,27 @@ 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();
-
+  
   /**
    * make all the PropertyChangeSupport objects for the
    * events described in org.vamsas.client.Event
@@ -96,7 +182,7 @@ public class SimpleClient implements IClient {
     }
     return events;
   }
-
+  
   /*
    * (non-Javadoc)
    * 
@@ -106,12 +192,12 @@ public class SimpleClient implements IClient {
     if (handlers.containsKey(Events.DOCUMENT_UPDATE)) {
       Object handler;
       ((PropertyChangeSupport) (handler = handlers.get(Events.DOCUMENT_UPDATE)))
-          .addPropertyChangeListener(evt);
+      .addPropertyChangeListener(evt);
       listeners.add(handler);
       listeners.add((Object) evt);
     }
   }
-
+  boolean finalized=false;
   /*
    * (non-Javadoc)
    * 
@@ -126,50 +212,108 @@ public class SimpleClient implements IClient {
     // deregister listeners.
     // 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() {
-    Object[] vdoc;//  TODO: = getVamsasDocument(new Reader());
-    // ClientDocument cdoc = new ClientDocument(getApplicationData(),
-        // ((VamsasDocument) vdoc[0]).getVAMSAS(), (Hashtable) vdoc[1], this);
-    // 
-    return null;
+  public IClientDocument getClientDocument() throws IOException {
+    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.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+"'");
+    }
+    // Construct the IClientDocument instance
+    
+    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) {
-    // TODO Auto-generated method stub
-    // 
+    if (!(newdoc instanceof ClientDocument)) {
+      throw new Error("Invalid IClientDocument instance for SimpleClient.");
+    }
+    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;
   }
-
+  
   /*
    * (non-Javadoc)
    * 
    * @see org.vamsas.client.IClient#storeDocument(java.io.File)
    */
   public void storeDocument(File location) {
-    // TODO Auto-generated method stub
-
+    
+    // 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();
   }
-
+  
   /*
    * (non-Javadoc)
    * 
@@ -180,28 +324,68 @@ public class SimpleClient implements IClient {
     if (handlers.containsKey(EventChain)) {
       Object handler;
       ((PropertyChangeSupport) (handler = handlers.get(EventChain)))
-          .addPropertyChangeListener(evt);
+      .addPropertyChangeListener(evt);
       listeners.add(handler);
       listeners.add((Object) evt);
     }
   }
-
+  
   /* (non-Javadoc)
    * @see org.vamsas.client.IClient#pollUpdate()
    */
   public void pollUpdate() {
-    // TODO wake up UpdateWatcher thread to check for updates.
-
-  }
-
-  public static void main(String[] args) {
+    
+    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()
    */
   public void joinSession() throws Exception {
-    // TODO Auto-generated method stub
-    
+    // start the EventGenerator thread.
+    if (evgen==null) {
+      log.warn("joinSession called on incomplete SimpleClient object.");
+      return;
+    }
+    if (evgen.isAlive())
+      throw new Error("Join session called twice for the same SimpleClient (IClient instance).");
+    evgen.start();
+    if (evgen.isAlive())
+      log.debug("Started EventGenerator thread.");
+    else {
+      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);
+    }
+  }
+  
+  
+  
+  /* (non-Javadoc)
+   * @see org.vamsas.client.IClient#importDocument(java.io.File)
+   */
+  public void importDocument(File location) {
+    // TODO LATER: implement SimpleClient.importDocument()
+    log.error("importDocument is not implemented for a SimpleClient Session.");
   }
 }