X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fuk%2Fac%2Fvamsas%2Fclient%2Fsimpleclient%2FSimpleClient.java;h=9cbdcb79a76fadd44d4c83c45e4ec9b96ceb7096;hb=273a27e050e41178ed2fc45777796d7741e02173;hp=b2b1757f95e6be32031c72e483a429ba907529a2;hpb=815d45c0c23f2309d93c3ca073d307f391927b11;p=vamsas.git diff --git a/src/uk/ac/vamsas/client/simpleclient/SimpleClient.java b/src/uk/ac/vamsas/client/simpleclient/SimpleClient.java index b2b1757..9cbdcb7 100644 --- a/src/uk/ac/vamsas/client/simpleclient/SimpleClient.java +++ b/src/uk/ac/vamsas/client/simpleclient/SimpleClient.java @@ -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; @@ -28,13 +28,10 @@ import uk.ac.vamsas.client.IObjectUpdate; import uk.ac.vamsas.client.InvalidSessionUrnException; import uk.ac.vamsas.client.SessionHandle; import uk.ac.vamsas.client.UserHandle; -import uk.ac.vamsas.objects.core.ApplicationData; +import uk.ac.vamsas.client.picking.IPickManager; 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 @@ -50,6 +47,15 @@ public class SimpleClient implements IClient { protected ClientHandle client = null; protected EventGeneratorThread evgen = null; protected ClientDocument cdocument = null; + + + + + private java.nio.channels.FileLock activeClientFilelock = null; + + private FileChannel activeClientFileChannel = null; + private File clientlockFile = null; + /** * object hash table that persists in each client holding vorbaIds and hash values after a document write */ @@ -60,7 +66,9 @@ public class SimpleClient implements IClient { * @return */ private IdFactory makeVorbaIdFactory() { - return new IdFactory(getSessionHandle(), client, user); + if (extantobjects==null) + extantobjects=new Hashtable(); + return new IdFactory(getSessionHandle(), client, user, extantobjects); } /** @@ -75,12 +83,17 @@ public class SimpleClient implements IClient { _session = sess; this.user = user; this.client = client; - try { + //try { + log.debug("Creating new session for "+_session); session = new SessionUrn(_session); - } catch (MalformedURLException e) { + log.debug("Creating new Event Generator"); + evgen = new EventGeneratorThread(_session, this, handlers); + /*} catch (MalformedURLException e) { log.error("Couldn't form a valid SessionUrn object!",e); throw new InvalidSessionUrnException(_session.toString()); - } + }*/ + log.debug("SimpleClient constructed for session "+session.getSessionUrn()); + } /** * construct new session by importing objects from an existing vamsas document @@ -99,10 +112,12 @@ public class SimpleClient implements IClient { 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 archive + // 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); } } @@ -194,28 +209,50 @@ public class SimpleClient implements IClient { * @see uk.ac.vamsas.client.IClient#addDocumentUpdateHandler(java.util.EventListener) */ public void addDocumentUpdateHandler(PropertyChangeListener evt) { - if (handlers.containsKey(Events.DOCUMENT_UPDATE)) { - Object handler; - ((PropertyChangeSupport) (handler = handlers.get(Events.DOCUMENT_UPDATE))) - .addPropertyChangeListener(evt); - listeners.add(handler); - listeners.add((Object) evt); - } + this.addVorbaEventHandler(Events.DOCUMENT_UPDATE, evt); } boolean finalized=false; + private void haltPickmanager() { + if (pickmanager!=null) { + final SimpleClient dying=this; + new Thread() { + public void run() { + SimpleClient.log.debug("Stopping pickManager.."); + dying.pickmanager.shutdown(); + SimpleClient.log.debug("pickManager halted."); + } + }.start(); + } + } /* * (non-Javadoc) * * @see uk.ac.vamsas.client.IClient#finalizeClient() */ public void finalizeClient() { + if (finalized) + throw new Error("VAMSAS Client Implementation Error: Finalized called twice for same client instance."); + + // mark this instance as finalized + finalized=true; + // TODO: determine if this is last client in session - // TODO: raise events like : ((lst_client && document.request.to.close), (client_finalization), ( + // TODO: raise events like : ((lst_client && document.request.to.close), (client_finalization), ( + evgen._raise(Events.CLIENT_FINALIZATION, null, this,null); // if (handlers.containsKey(Events.)) // if (handlers.containsKey(Events.CLIENT_FINALIZATION)) // deregister listeners. - // mark this instance as finalized + log.debug("Stopping pickManager"); + haltPickmanager(); + + log.debug("Deregistering Client"); + _session.removeClient(this); + //log.debug("Stopping EventGenerator.."); + //evgen.stopWatching(); + SimpleClient.log.debug("EventGenerator halted."); + this.cdocument = null; + log.debug("finalization Complete."); } /* @@ -224,10 +261,12 @@ public class SimpleClient implements IClient { * @see uk.ac.vamsas.client.IClient#getClientDocument() */ public IClientDocument getClientDocument() throws IOException { + log.debug("getClientDocument"); if (cdocument!=null) { // cdocument is non-nill if the ClientDocument.finalise() method hasn't been called. return cdocument; } + evgen.disableDocumentWatch(); VamsasArchive va = null; try { // LATER: bail out if it takes too long to get the lock ? @@ -238,16 +277,16 @@ public class SimpleClient implements IClient { } VamsasDocument doc=null; IdFactory vorba = null; - // TODO: reduce size of vorba ids generated from these parameters to IdFactory (mainly sessionHandle rationalization ?) + // TODO: LATER: 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"); + log.debug("Accessing document"); doc = va.getVamsasDocument(getProvenanceUser(), "created new session document.", null); if (doc!=null) - _session.slog.debug("Successfully retrieved document."); + log.debug("Successfully retrieved document."); else log.error("Unexpectedly retrieved null document!"); } @@ -257,8 +296,8 @@ public class SimpleClient implements IClient { } // Construct the IClientDocument instance - ClientDocument cdoc = new ClientDocument(doc, va, vorba, this); - return cdoc; + cdocument = new ClientDocument(doc, va, vorba, this); + return cdocument; } /* @@ -267,23 +306,34 @@ public class SimpleClient implements IClient { * @see uk.ac.vamsas.client.IClient#updateDocument(uk.ac.vamsas.client.IClientDocument) */ public void updateDocument(IClientDocument newdoc) { + log.debug("updateDocument:"); + // Check validity of simpleclient instance and that it holds a lock on the session's document if (!(newdoc instanceof ClientDocument)) { - throw new Error("Invalid IClientDocument instance for SimpleClient."); + throw new Error("Invalid IClientDocument passsed to SimpleClient."); } if (cdocument==null) - throw new Error("Client Error - updateDocument() called before getClientDocument()."); + 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."); + log.debug("updateDocument for "+session.getSessionUrn()+" with unmodified IClientDocument (skipping the write)"); } else { try { - if (!cdocument.updateSessionDocument()) { + 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 archive. + // 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); @@ -292,12 +342,20 @@ public class SimpleClient implements IClient { } // garbage collect the ClientDocument instance. try { + log.debug("Finalizing ClientDocument instance."); cdocument.finalize(); - } catch (Throwable e) { log.error("Exception when trying to garbage collect ClientDocument for "+session.getSessionUrn(), e); } cdocument = null; // this is probably done by finalize + + try { + _session.unlockVamsasDocument(); + evgen.enableDocumentWatch(); + } catch (IOException e) { + log.warn("IO Problems when releasing lock on session document!",e); + _session.slog.error("IO problems when attempting to release lock on session document."); + } } /* @@ -306,17 +364,18 @@ public class SimpleClient implements IClient { * @see uk.ac.vamsas.client.IClient#storeDocument(java.io.File) */ public void storeDocument(File location) { - + if (location==null) + throw new Error("Vamsas Client API Usage Error: storeDocument called with null location."); + log.debug("StoreDocument to "+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(); + _session.clearUnsavedFlag(); } catch (Exception e) { log.warn("Exception whilst trying to store document in "+location,e); } - vamlock.release(); } @@ -328,6 +387,7 @@ public class SimpleClient implements IClient { */ public void addVorbaEventHandler(String EventChain, PropertyChangeListener evt) { if (handlers.containsKey(EventChain)) { + log.debug("Adding new handler for "+EventChain); Object handler; ((PropertyChangeSupport) (handler = handlers.get(EventChain))) .addPropertyChangeListener(evt); @@ -340,13 +400,13 @@ public class SimpleClient implements IClient { * @see uk.ac.vamsas.client.IClient#pollUpdate() */ public void pollUpdate() { - + log.debug("pollUpdate"); if (evgen==null) { log.warn("pollUpdate called on incomplete SimpleClient object."); return; } - if (!evgen.isAlive()) { + if (!evgen.isWatcherAlive()) { log.warn("pollUpdate called before joinSession() - trying to do this."); try { joinSession(); @@ -357,7 +417,7 @@ public class SimpleClient implements IClient { //TODO ensure event generator robustly handles these interrupts. log.debug("interrrupting event generator."); - evgen.interrupt(); + evgen.interruptWatching(); log.debug("interrrupted event generator."); } @@ -365,24 +425,26 @@ public class SimpleClient implements IClient { * @see uk.ac.vamsas.client.IClient#joinSession() */ public void joinSession() throws Exception { + log.debug("Joining Session."); // start the EventGenerator thread. if (evgen==null) { log.warn("joinSession called on incomplete SimpleClient object."); return; } - if (evgen.isAlive()) + if (evgen.isWatcherAlive()) throw new Error("Join session called twice for the same SimpleClient (IClient instance)."); - evgen.start(); - if (evgen.isAlive()) + evgen.startWatching(); + if (evgen.isWatcherAlive()) 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 ? + //TODO: LATER: is this application connecting to a newly created session document ? //evgen.raise(Events.DOCUMENT_CREATE); } + } @@ -392,7 +454,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) { @@ -414,4 +484,126 @@ public class SimpleClient implements IClient { // TODO Auto-generated method stub } + + /** + * retrieves the current VamsasSession to which belong the client + * @return the _session + */ + protected VamsasSession get_session() { + return this._session; + } + SimplePickManager pickmanager=null; + /* (non-Javadoc) + * @see uk.ac.vamsas.client.IClient#getPickManager() + */ + public IPickManager getPickManager() { + createPickManager(); + return pickmanager; + } + + private void createPickManager() { + if (pickmanager==null){ + // TODO: Construct PickManager for session using details from sessionURN! + log.debug("Creating PickManager (not from sessionURN yet)"); + pickmanager = new SimplePickManager(new uk.ac.vamsas.client.picking.SocketManager()); + } + } + + + protected void releaseActiveClientFile() throws IOException + { + + log.debug("Releasing active client file"); + if( activeClientFilelock != null) + // Release the lock + activeClientFilelock.release(); + + if (activeClientFileChannel != null) + // Close the file + activeClientFileChannel.close(); + if (this.clientlockFile != null) + { + this.clientlockFile.delete(); + log.debug("deleted active client lock file"); + } + + } + + 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()); + if (clientlockFile.exists()) + {//should not happen, file should be deleted when the application closes or when a crashed application has been detected + log.error("client lock file already exits"); + } + try { + //create the empty file + if(! clientlockFile.createNewFile()) + { + log.error("Unable to create active client lock file"); + + return; + } + else + log.debug("file created"); + // Get a file channel for the file + FileChannel channel = new RandomAccessFile(clientlockFile, "rw").getChannel(); + + // Use the file channel to create a lock on the file. + // This method blocks until it can retrieve the lock. + java.nio.channels.FileLock activeClientFilelock ;// = channel.lock(); + + // Try acquiring the lock without blocking. This method returns + // null or throws an exception if the file is already locked. + try + { + activeClientFilelock = channel.tryLock(); + log.debug("Got lock"); + } + catch (OverlappingFileLockException e) + { + // File is already locked in this thread or virtual machine + log.error("Oups the file is already locked",e); + + } + + // Release the lock + /* lock.release(); + + // Close the file + channel.close();*/ + } catch (Exception e) { + log.error("Error during lock file creation",e); + } + + + + } + + /** + * @return the clientlockFile + */ + protected File getClientlockFile() { + return clientlockFile; + } }