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)
*
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)
*
public ClientHandle getClientHandle() {
return client;
}
-
+
/*
* (non-Javadoc)
*
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
}
return events;
}
-
+
/*
* (non-Javadoc)
*
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)
*
// 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)
*
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.");
}
}