X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Forg%2Fvamsas%2Fclient%2Fsimpleclient%2FVamsasSession.java;fp=src%2Forg%2Fvamsas%2Fclient%2Fsimpleclient%2FVamsasSession.java;h=a5359ad5114ac5347b833d333409705481f53190;hb=e15b38a5b95e0aa5b11707728abb539c073fe27c;hp=0000000000000000000000000000000000000000;hpb=bfd813ba38e1d432d49a311422ade5ba33203a43;p=vamsas.git diff --git a/src/org/vamsas/client/simpleclient/VamsasSession.java b/src/org/vamsas/client/simpleclient/VamsasSession.java new file mode 100644 index 0000000..a5359ad --- /dev/null +++ b/src/org/vamsas/client/simpleclient/VamsasSession.java @@ -0,0 +1,312 @@ +package org.vamsas.client.simpleclient; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.RandomAccessFile; +import java.io.Writer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Appender; +import org.apache.log4j.Logger; +import org.apache.log4j.FileAppender; +import org.vamsas.client.ClientHandle; +import org.vamsas.client.UserHandle; +/** + * Does all the IO operations for a SimpleClient instance accessing + * a SimpleClient vamsas session. + * + * Basically, it defines the various standard names for the files + * in the session directory (that maps to the sessionUrn), + * provides constructors for the file handlers and watchers of + * those file entities, and some higher level methods + * to check and change the state flags for the session. + * + * TODO: move the stuff below to the SimpleClientFactory documentation. + * much may not be valid now : + * Vamsas client is intialised with a path to create live session directories. + * This path may contain a vamsas.properties file + * that sets additional parameters (otherwise client + * just uses the one on the classpath). + * + * A vamsas session consists of : + * SessionDir - translates to urn of a live session. + * Contains: Vamsas Document (as a jar), Session client list file, + * both of which may be locked, and additional + * temporary versions of these files when write + * operations are taking place. + * + * Zip file entries + * - vamsasdocument.xml : core info + * one or more: + * - .version.sessionnumber.raw (string given in vamsasdocument.xml applicationData entry) + * + * Lockfile + * - filename given in the vamsasdocument.xml. Should be checked for validity by any client and rewritten if necessary. + * The lockfile can point to the jar itself. + * Mode of operation. + * Initially - documentHandler either: + * - creates a zip for a new session for the client + * - connect to an existing session zip + * 1. reads session urn file + * 2. waits for lock + * 3. examines session - decide whether to create new application data slice or connect to one stored in session. + * 4. writes info into session file + * 5. releases lock and generates local client events. + * 6. Creates Watcher thread to generate events. + * + * During the session + * - Update watcher checks for file change - + * + * Procedures for file based session message exchange + * - session document modification flag + * + */ + +public class VamsasSession { + /** + * indicator file for informing other processes that + * they should finalise their vamsas datasets for + * storing into a vamsas archive. + */ + public static final String CLOSEANDSAVE_FILE="stored.log"; + /** + * session file storing the last_stored_stat data + */ + public static final String MODIFIEDDOC_FILE="modified"; + + /** + * called to clear update flag after a successful offline storage event + */ + protected void clearUnsavedFlag() { + SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE)); + if (!laststored.clearFlag()) + log.warn("Unsaved flag was not cleared for "+sessionDir); + } + /** + * called to indicate session document has been modified. + * + */ + protected void setUnsavedFlag() { + SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE)); + if (!laststored.setFlag()) + log.warn("Couldn't set the Unsaved flag for "+sessionDir); + } + /** + * + * @return true if session document has been modified since last offline storage event + */ + protected boolean getUnsavedFlag() { + SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE)); + return laststored.checkFlag(); + } + /** + * log file location + */ + public static final String SESSION_LOG="Log.txt"; + private static Log log = LogFactory.getLog(VamsasSession.class); + protected Logger slog = Logger.getLogger("org.vamsas.client.SessionLog"); + /** + * setup the sessionLog using Log4j. + * @throws IOException + */ + private void initLog() throws IOException { + // LATER: make dedicated appender format for session log. + Appender app = slog.getAppender("SESSION_LOG"); + slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath())); + } + + /** + * the sessionDir is given as the session location for new clients. + */ + protected File sessionDir; + /** + * holds the list of attached clients + */ + ClientsFile clist; + public static final String CLIENT_LIST="Clients.obj"; + /** + * holds the data + */ + VamsasFile vamArchive; + public static final String VAMSAS_OBJ="VamDoc.jar"; + + /** + * sets up the vamsas session files and watchers in sessionDir + * @param sessionDir + */ + protected VamsasSession(File sessionDir) throws IOException { + if (sessionDir==null) + throw new Error("Null directory for VamsasSession."); + if (sessionDir.exists()) { + if (!sessionDir.isDirectory() || !sessionDir.canWrite() || !sessionDir.canRead()) + throw new IOException("Cannot access '"+sessionDir+"' as a read/writable Directory."); + if (checkSessionFiles(sessionDir)) { + // session files exist in the directory + this.sessionDir = sessionDir; + initSessionObjects(); + slog.debug("Initialising additional VamsasSession instance"); + log.debug("Attached to VamsasSession in "+sessionDir); + } + } else { + // start from scratch + if (!sessionDir.mkdir()) + throw new IOException("Failed to make VamsasSession directory in "+sessionDir); + this.sessionDir = sessionDir; + createSessionFiles(); + initSessionObjects(); + slog.debug("Session directory created."); + log.debug("Initialised VamsasSession in "+sessionDir); + } + } + /** + * tests presence of existing sessionfiles files in dir + * @param dir + * @return + */ + private boolean checkSessionFiles(File dir) throws IOException { + File c_file = new File(dir,CLIENT_LIST); + File v_doc = new File(dir,VAMSAS_OBJ); + if (c_file.exists() && v_doc.exists()) + return true; + return false; + } + /** + * create new empty files in dir + * + */ + private void createSessionFiles() throws IOException { + if (sessionDir==null) + throw new IOException("Invalid call to createSessionFiles() with null sessionDir"); + File c_file = new File(sessionDir,CLIENT_LIST); + File v_doc = new File(sessionDir,VAMSAS_OBJ); + if (c_file.createNewFile()) + log.debug("Created new ClientFile "+c_file); // don't care if this works or not + if (v_doc.createNewFile()) + log.debug("Created new Vamsas Session Document File "+v_doc); + } + /** + * construct SessionFile objects and watchers for each + */ + private void initSessionObjects() throws IOException { + if (clist!=null || vamArchive!=null) + throw new IOException("initSessionObjects called for initialised VamsasSession object."); + clist = new ClientsFile(new File(sessionDir,CLIENT_LIST)); + vamArchive = new VamsasFile(new File(sessionDir,VAMSAS_OBJ)); + initLog(); + } + /** + * make a new watcher object for the clientFile + * @return new ClientFile watcher instance + */ + public FileWatcher getClientWatcher() { + return new FileWatcher(clist.sessionFile); + } + FileWatcher session_doc_watcher=null; + /** + * make a new watcher object for the vamsas Document + * @return new ClientFile watcher instance + */ + public FileWatcher getDocWatcher() { + if (session_doc_watcher==null) + return session_doc_watcher = new FileWatcher(vamArchive.sessionFile); + return new FileWatcher(vamArchive.sessionFile); + } + FileWatcher store_doc_file=null; + /** + * make a new watcher object for the messages file + * (thread safe - keeps a reference to the first watcher) + * @return new watcher instance + */ + public FileWatcher getStoreWatcher() { + if (store_doc_file==null) + return store_doc_file = new FileWatcher(new File(CLOSEANDSAVE_FILE)); + return new FileWatcher(new File(CLOSEANDSAVE_FILE)); + + } + /** + * write to the StoreWatcher file to indicate that a storeDocumentRequest has been made. + * The local client's storeWatcher FileWatcher object is updated so the initial change is not registered. + * @param client + * @param user + * @return + */ + public void addStoreDocumentRequest(ClientHandle client, UserHandle user) throws IOException { + SessionFile sfw = new SessionFile(new File(sessionDir, CLOSEANDSAVE_FILE)); + while (!sfw.lockFile()) + log.debug("Trying to get lock for "+CLOSEANDSAVE_FILE); + RandomAccessFile sfwfile=sfw.fileLock.getRaFile(); + sfwfile.setLength(0); // wipe out any old info. + // TODO: rationalise what gets written to this file (ie do we want other clients to read the id of the requestor?) + sfwfile.writeUTF(client.getClientUrn()+":"+user.getFullName()+"@"+user.getOrganization()); + sfw.unlockFile(); + if (store_doc_file!=null) + store_doc_file.setState(); + slog.info("FinalizeAppData request from "+user.getFullName()+" using "+client.getClientUrn()+""); + } + /** + * create a new session with an existing vamsas Document - by copying it into the session. + * @param archive + */ + public void setVamsasDocument(File archive) throws IOException { + log.debug("Transferring vamsas data from "+archive+" to session:"+vamArchive.sessionFile); + SessionFile xtantdoc = new SessionFile(archive); + vamArchive.updateFrom(null, xtantdoc); + // LATER: decide if session archive provenance should be updated to reflect access. + // TODO: soon! do a proper import objects from external file + log.debug("Transfer complete."); + } + /** + * write session as a new vamsas Document (this will overwrite any existing file without warning) + * TODO: test + * TODO: verify that lock should be released for vamsas document. + * @param destarchive + */ + protected void writeVamsasDocument(File destarchive, Lock extlock) throws IOException { + log.debug("Transferring vamsas data from "+vamArchive.sessionFile+" to session:"+destarchive); + SessionFile newdoc = new SessionFile(destarchive); + if (extlock==null && !vamArchive.lockFile()) + while (!vamArchive.lockFile()) + log.info("Trying to get lock for "+vamArchive.sessionFile); + // TODO: LATER: decide if session archive provenance should be written in vamsasDocument file for this export. + newdoc.updateFrom(extlock, vamArchive); + // LATER: LATER: fix use of updateFrom for file systems where locks cannot be made (because they don't have a lockManager, ie NFS/Unix, etc). + vamArchive.unLock(); + newdoc.unlockFile(); + log.debug("Transfer complete."); + } + + /** + * Creates a VamsasArchive Vobject for accessing and updating document + * Note: this will lock the Vamsas Document for exclusive access to the client. + * @return session vamsas document + * @throws IOException if locks fail or vamsas document read fails. + */ + protected VamsasArchive getVamsasDocument() throws IOException { + // TODO: check we haven't already done this once + if (!vamArchive.lockFile()) + throw new IOException("Failed to get lock for vamsas archive."); + + VamsasArchive va = new VamsasArchive(vamArchive.sessionFile, false, true, vamArchive); + + return va; + } + /** + * create a uniquely named file in the session Directory + * @see java.io.File.createTempFile + * @param pref Prefix for name + * @param suff Suffix for name + * @return SessionFile object configured for the new file (of length zero) + * @throws IOException + */ + protected SessionFile getTempSessionFile(String pref, String suff) throws IOException { + File tfile = File.createTempFile(pref,suff,sessionDir); + SessionFile tempFile = new SessionFile(tfile); + return tempFile; + } +} + +