X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fuk%2Fac%2Fvamsas%2Fclient%2Fsimpleclient%2FVamsasSession.java;h=af5ab37b25821d5db58cf4f73c5d280722cff5c0;hb=844ccad5a3fcbedec17b2af66d460f31abc7cff1;hp=87e904a8b890457786492d32020c506671a0a8bd;hpb=6f33f705957d674dc2ab6c994a6ea87f7a91f40f;p=vamsas.git diff --git a/src/uk/ac/vamsas/client/simpleclient/VamsasSession.java b/src/uk/ac/vamsas/client/simpleclient/VamsasSession.java index 87e904a..af5ab37 100644 --- a/src/uk/ac/vamsas/client/simpleclient/VamsasSession.java +++ b/src/uk/ac/vamsas/client/simpleclient/VamsasSession.java @@ -1,884 +1,902 @@ -package uk.ac.vamsas.client.simpleclient; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; -import java.nio.channels.OverlappingFileLockException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.FileAppender; -import org.apache.log4j.Logger; -import org.apache.log4j.PatternLayout; - -import uk.ac.vamsas.client.ClientHandle; -import uk.ac.vamsas.client.Events; -import uk.ac.vamsas.client.IClient; -import uk.ac.vamsas.client.SessionHandle; -import uk.ac.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"; - - private SimpleSessionManager sessionManager = null; - - /** - * Count of cycles before considering the current client as the last one of - * the session (if no other client registered as active ) - */ - private final int watchCycleCountBeforeLastClient = 1220; - - /** - * time between checking - */ - public int WATCH_SLEEP = 30; - - protected String clientFileDirectory = "clients"; - - /** - * 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("uk.ac.vamsas.client.SessionLog"); - - /** - * the appender that writes to the log file inside the session's directory. - */ - private FileAppender slogAppender = null; - - /** - * setup the sessionLog using Log4j. - * - * @throws IOException - */ - private void initLog() throws IOException { - // TODO: fix session event logging - // LATER: make dedicated appender format for session log. - /* - * Appender app = slog.getAppender("log4j.appender.SESSIONLOG"); // - * slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, - * SESSION_LOG).getAbsolutePath())); // slog.addAppender(new - * FileAppender(app.getLayout(), new File(sessionDir, - * SESSION_LOG).getAbsolutePath())); for (Enumeration e = - * slog.getAllAppenders() ; e.hasMoreElements() ;) { - * System.out.println(e.nextElement()); - * } - */ - - if (slog != null) { - File sessionLogFile = new File(this.sessionDir, SESSION_LOG); - slog.addAppender(slogAppender = new FileAppender(new PatternLayout( - "%-4r [%t] %-5p %c %x - %m%n"), sessionLogFile.getAbsolutePath(), - true)); - } else { - log.info("No appender for SessionLog"); - } - } - - private void closeSessionLog() { - if (slog != null) { - if (slogAppender != null) { - slog.removeAppender(slogAppender); - slogAppender.close(); - slogAppender = null; - } - } - } - - /** - * 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 sessionDir1 - * - * @param sessionDir1 - */ - protected VamsasSession(File sessionDir1) throws IOException { - this(sessionDir1, null); - } - - /** - * sets up the vamsas session files and watchers in sessionDir1 - * - * @param sessionDir1 - * @param extVamDoc - * null or an existing archive to initialise the session with - * @throws any - * IOExceptions from creating session directory and files. - * @throws error - * if both extVamDoc and sessionDir1 already exist (cannot import - * new data into session in this way) - */ - protected VamsasSession(File sessionDir1, File extVamDoc) throws IOException { - if (sessionDir1 == null) - throw new Error("Null directory for VamsasSession."); - if (!sessionDir1.exists() && !sessionDir1.mkdir()) { - throw new IOException("Failed to make VamsasSession directory in " - + sessionDir1); - } - if (!sessionDir1.isDirectory() || !sessionDir1.canWrite() - || !sessionDir1.canRead()) { - throw new IOException("Cannot access '" + sessionDir1 - + "' as a read/writable Directory."); - } - boolean existingSession=checkSessionFiles(sessionDir1); - if (existingSession) - { - if (extVamDoc!=null) { - throw new Error( - "Client Initialisation Error: Cannot join an existing session directory with an existing vamsas document to import."); - } else { - log - .debug("Joining an existing session."); - } - } - this.sessionDir = sessionDir1; - initSessionObjects(); - if (existingSession) - { slog.debug("Initialising additional VamsasSession instance"); - } else - { - slog.debug("Founding client has joined VamsasSession instance"); - } - - log.debug("Attached to VamsasSession in " + sessionDir1); - if (extVamDoc!=null) - { - setVamsasDocument(extVamDoc); - } - slog.debug("Session directory created."); - log.debug("Initialised VamsasSession in " + sessionDir1); - } - - /** - * 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.exists() && c_file.createNewFile()) - log.debug("Created new ClientFile " + c_file); // don't care if this - // works or not - if (!v_doc.exists()) { - if (v_doc.createNewFile()) { - log.debug("Created new Vamsas Session Document File " + v_doc); - } else { - log.warn("Didn't create Vamsas Session Document file in " + v_doc); - } - } - } - - /** - * construct SessionFile objects and watchers for each - */ - private void initSessionObjects() throws IOException { - createSessionFiles(); - 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)); - storedocfile = new ClientsFile(new File(sessionDir, CLOSEANDSAVE_FILE)); - initLog(); - } - - /** - * make a new watcher object for the clientFile - * - * @return new ClientFile watcher instance - */ - public FileWatcher getClientWatcher() { - return new FileWatcher(clist.sessionFile); - } - - /** - * make a new watcher object for the vamsas Document - * - * @return new ClientFile watcher instance - */ - public FileWatcher getDocWatcher() { - return new FileWatcher(vamArchive.sessionFile); - } - - FileWatcher store_doc_file = null; - - public ClientsFile storedocfile = null; - - /** - * make a new watcher object for the messages file - * - * @return new watcher instance - */ - public FileWatcher getStoreWatcher() { - return new FileWatcher(new File(sessionDir, 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 { - // TODO: replace this with clientsFile mechanism - 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); - while (!vamArchive.lockFile()) - log.info("Trying to get lock for " + vamArchive.sessionFile); - vamArchive.updateFrom(null, xtantdoc); - xtantdoc.unlockFile(); - unlockVamsasDocument(); - // TODO: session archive provenance should be updated to reflect import from external source - 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 a provenance entry should be written in the - // exported document recording the export from the session - newdoc.updateFrom(null, 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."); - } - - /** - * extant archive IO handler - */ - VamsasArchive _va = null; - - /** - * 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 { - // check we haven't already done this once - probably should be done by - // caller - if (_va != null) - return _va; - // patiently wait for a lock on the document. (from - // ArchiveClient.getUpdateable()) - long tries = 5000; - while (vamArchive.getLock() == null && --tries > 0) { - // Thread.sleep(1); - log.debug("Trying to get a document lock for the " + tries + "'th time."); - } - if (tries == 0) - throw new IOException("Failed to get lock for vamsas archive."); - - VamsasArchive va = new VamsasArchive(vamArchive.sessionFile, false, true, - vamArchive); - - return va; - } - - /** - * Unlocks the vamsas archive session document after it has been closed. - * - * @throws IOException - */ - protected void unlockVamsasDocument() throws IOException { - if (_va != null) - _va.closeArchive(); - _va = null; - if (vamArchive != null) - vamArchive.unLock(); - - } - - /** - * create a uniquely named - * uk.ac.vamsas.client.simpleclient.ClientsFile.addClient(ClientHandle)ile 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; - } - - /** - * add a IClient to the session - * - * add the client to the client list file - * - * @param client - * client to add to the session - */ - protected void addClient(SimpleClient client) { - if (client == null) - slog.error("Try to add a null client to the session "); - else { - log.debug("Adding client " + client.getClientHandle().getClientUrn()); - getClientWatcherElement().haltWatch(); - clist.addClient(client.getClientHandle()); - - log.debug("Added."); - log.debug("Register Client as Active."); - try { - client.createActiveClientFile(); - } catch (IOException e) { - log.debug("Error during active client file creation."); - } - // tracks modification to the client list and readds client to the list - getClientWatcherElement().setHandler(new AddClientWatchCallBack(client)); - getClientWatcherElement().enableWatch(); - - } - } - - /** - * Handler for the client watcher. - * - * If (the current client is not in the client list, it is added again;) - */ - private class AddClientWatchCallBack implements WatcherCallBack { - - private SimpleClient client; - - /** - * Inits the handler with the client to check in the list - * - * @param client - * client to monitor in the client list - */ - protected AddClientWatchCallBack(SimpleClient client) { - this.client = client; - } - - /** - * If the client list is modified, checks if the current is still in the - * list. otherwise, readds ti. - * - * @return true to enable watcher, or false to disable it in future - * WatcherThread cycles. - */ - public boolean handleWatchEvent(WatcherElement watcher, Lock lock) { - boolean isWatchEnable = watcher.isWatchEnabled(); - if (lock == null)// no update on the list - return isWatchEnable; - log.debug("change on the client list "); - if (client != null) { - - // checks if the client is not already in the lists - ClientHandle[] cl = clist.retrieveClientList(lock);// clist.retrieveClientList(); - boolean found = false; - if (cl != null) { - for (int chi = cl.length - 1; !found && chi > -1; chi--) { - found = cl[chi].equals(this.client.getClientHandle()); - } - - } - if (!found) { - log.debug("client not in the list "); - if (log.isDebugEnabled()) - log - .debug("the client has not been found in the list. Adding it again :" - + cl); - addClient(client); - } else - log.debug("client is in the list"); - - } - log.debug("isWatchEnable " + isWatchEnable); - return isWatchEnable; - } - } - - /** - * - * removes a client from the current session removes the client from the - * session client list if the client is the last one from the session - * (ClientList), the current session is removed from active session list. - * - * The active should add them self to the client list. To insure to close the - * session,when the current client is the lact active client, clears the list - * of clients and when two cycles to insure there is no more active client, - * that otherwise would have readd themself to the list - * - * @param client - * client to remove - */ - protected void removeClient(SimpleClient client)// IClient client) - { - if (client == null) { - log.error("Null client passed to removeClient"); - return; - } - // ClientSessionFileWatcherElement cwe=getClientWatcherElement(); - // if (cwe!=null && cwe.isWatchEnabled()) { - // cwe.haltWatch(); - // }; - // set handler to check is the the last active client of the session - // Wait for several watchers cycle to see if the current client was the last - // client active in the session. - // if yes, close the session - - // getClientWatcherElement().setHandler(new RemoveClientWatchCallBack - // (client)); - // getClientWatcherElement().setTimeoutBeforeLastCycle(this.watchCycleCountBeforeLastClient); - log.info("remove client from list"); - if (clistWatchElement != null) { - clistWatchElement.haltWatch(); - clistWatchElement.watched.unlockFile(); - } - // clist.clearList(); - // clist.unlockFile(); - log.info("list cleared"); - // if (cwe!=null) { - // cwe.enableWatch(); - - log.debug("Stopping EventGenerator.."); - client.evgen.stopWatching(); - // cwe.setHandler(null); - // ask to the client to copy application data into the document - client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null, client, null); - boolean closeSession = isLastActiveClient(client); - if (closeSession) { - if (client.get_session().getUnsavedFlag()) - { - log.debug("Raising request-to-save event"); - client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client, null); - } - log.debug("Raising session shutdown event"); - client.evgen._raise(Events.SESSION_SHUTDOWN, null, client - .getSessionHandle(), null); - log.debug("All events raised for finalising session "+client.getSessionHandle().toString()); - } - // cwe.haltWatch(); - client.evgen.stopWatching(); - try { - log.debug("Attempting to release active client locks"); - client.releaseActiveClientFile(); - } catch (IOException e) { - log.error("error during active file client release"); - } - tidyUp(); - if (closeSession) { - log.debug("Last active client: closing session"); - log.info("Closing session"); - getSessionManager().removeSession(client.getSessionHandle()); - } - } - - /** - * close every file and stop. - */ - private void tidyUp() { - if (clist != null) - clist.unlockFile(); - clist = null; - storedocfile.unlockFile(); - storedocfile = null; - closeSessionLog(); - } - - private boolean isLastActiveClient(SimpleClient client) { - log.debug("Testing if current client is the last one."); - log - .debug("current client lockfile is '" + client.getClientlockFile() - + "'"); - boolean noOtherActiveClient = true; - // create, if need, subdirectory to contain client files - File clientlockFileDir = new File(this.sessionDir, clientFileDirectory); - if (!clientlockFileDir.exists()) { - log - .error("Something wrong the active client file does not exits... should not happen"); - return false; - } - - try { - - // no check every file in the directory and try to get lock on it. - File[] clientFiles = clientlockFileDir.listFiles(); - if (clientFiles == null || clientFiles.length == 0) {// there is not file - // on the directory. - // the current - // client should be - // the last one. - return true; - } - - for (int i = clientFiles.length - 1; i > -1 && noOtherActiveClient; i--) { - File clientFile = clientFiles[i]; - log.debug("testing file for lock: " + clientFile.getAbsolutePath()); - if (client.getClientLock().isTargetLockFile(clientFile)) { - log.debug("current client file found"); - continue; - } - if (clientFile != null && clientFile.exists()) { - try { - log.debug("Try to acquire a lock on the file"); - // Get a file channel for the file - FileChannel channel = new RandomAccessFile(clientFile, "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 { - java.nio.channels.FileLock activeClientFilelock = channel - .tryLock(); - - // the lock has been acquired. - // the file was not lock and so the corresponding application - // seems to have die - if (activeClientFilelock != null) { - log - .debug("lock obtained : file must be from a crashed application"); - - activeClientFilelock.release(); - log.debug("lock released"); - - channel.close(); - log.debug("channel closed"); - - // delete file - clientFile.delete(); - log.debug("crashed application file deleted"); - - } else { - noOtherActiveClient = false; - log.debug("lock not obtained : another application is active"); - } - } catch (OverlappingFileLockException e) { - // File is already locked in this thread or virtual machine - // that the expected behaviour - log.debug("lock not accessible ", e); - } - } catch (Exception e) { - log.debug("error during lock testing ", e); - } - } - } - - } catch (Exception e) { - log.error("error during counting active clients"); - } - return noOtherActiveClient; - } - - /** - * Handler for the client watcher. after a client have been removed - * - * Checks if the client is not the last active one. - * - * If (the current client is not in the client list readd it;) - */ - private class RemoveClientWatchCallBack implements WatcherCallBack { - - private SimpleClient client; - - private boolean manualCheckOfClientCount = false; - - /** - * Inits the handler with the client to check in the list - * - * @param client - * client to monitor in the client list - */ - protected RemoveClientWatchCallBack(SimpleClient client) { - this.client = client; - } - - /** - * If the client list is modified, checks if the current is still in the - * list. otherwise, readds ti. - * - * @return true to enable watcher, or false to disable it in future - * WatcherThread cycles. - */ - public boolean handleWatchEvent(WatcherElement watcher, Lock lock) { - // if lock is null, no client has been added since last, clear. - // the client is then the last client - if (client != null) { - - if (lock == null) { - - // checks if the client is not already in the lists - // ClientHandle[] cl = - // clist.retrieveClientList();//lock);//clist.retrieveClientList(); - - boolean islastClient = true; - if (manualCheckOfClientCount) { - log.debug("manual checking of count of client"); - // checks if the client is not already in the lists - ClientHandle[] cl = clist.retrieveClientList();// lock);//clist.retrieveClientList(); - if (cl == null || cl.length < 1) - // {//no client has registered as active - { - islastClient = true; - log.debug("list is empty"); - } else - islastClient = false; - log.debug("list is not empty"); - } - // if(cl == null || cl.length<1 ) - // {//no client has registered as active - if (islastClient) { - // the client is the last one, so close current session - log.info("FROMCLIENTLIST WATCHER: last client removed: closing session"); - closeSession(client); - } - } else { - log.debug("not the last client found "); - // ask to the client to cpoy application data into the document - // client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null, - // client,null); - - // / } - - } - log.debug("Stopping EventGenerator.."); - // TODO: ensure ClientsFile lock is really released!! - // clist.unlockFile(); - client.evgen.stopWatching(); - } - watcher.setHandler(null);// Do not check if the client is the last - // client. watcher will shutdown anyway - // watcher.haltWatch(); - // watcher. - return false; - } - } - - /** - * closes the current session, and send an event to the last client to close - * the document - * - * @param client - * the last client of the client - */ - private void closeSession(SimpleClient client) { - // close document - client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client, null); - log.debug("close document request done"); - closeSession(client.getSessionHandle()); - } - - /** - * CLoses the current session - * - * @param sessionHandle - * sessionHandle of the session to remove - */ - private void closeSession(SessionHandle sessionHandle) { - getSessionManager().removeSession(sessionHandle); - log.debug("Session removed"); - } - - /** - * @return the sessionManager - */ - protected SimpleSessionManager getSessionManager() { - return sessionManager; - } - - /** - * @param sessionManager - * the sessionManager to set - */ - protected void setSessionManager(SimpleSessionManager sessionManager) { - this.sessionManager = sessionManager; - } - - public ClientsFile getStoreDocFile() { - if (storedocfile == null) { - - } - return storedocfile; - } - - ClientSessionFileWatcherElement clistWatchElement = null; - - /** - * get or create a watcher on clist. - * - * @return the contents of clistWatchElement or initialise it - */ - public ClientSessionFileWatcherElement getClientWatcherElement() { - if (clistWatchElement == null) { - clistWatchElement = new ClientSessionFileWatcherElement(clist, null); - } - return clistWatchElement; - } -} +/* + * This file is part of the Vamsas Client version 0.1. + * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, + * Andrew Waterhouse and Dominik Lindner. + * + * Earlier versions have also been incorporated into Jalview version 2.4 + * since 2008, and TOPALi version 2 since 2007. + * + * The Vamsas Client is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Vamsas Client is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the Vamsas Client. If not, see . + */ +package uk.ac.vamsas.client.simpleclient; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.channels.OverlappingFileLockException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.FileAppender; +import org.apache.log4j.Logger; +import org.apache.log4j.PatternLayout; + +import uk.ac.vamsas.client.ClientHandle; +import uk.ac.vamsas.client.Events; +import uk.ac.vamsas.client.IClient; +import uk.ac.vamsas.client.SessionHandle; +import uk.ac.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"; + + private SimpleSessionManager sessionManager = null; + + /** + * Count of cycles before considering the current client as the last one of + * the session (if no other client registered as active ) + */ + private final int watchCycleCountBeforeLastClient = 1220; + + /** + * time between checking + */ + public int WATCH_SLEEP = 30; + + protected String clientFileDirectory = "clients"; + + /** + * 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("uk.ac.vamsas.client.SessionLog"); + + /** + * the appender that writes to the log file inside the session's directory. + */ + private FileAppender slogAppender = null; + + /** + * setup the sessionLog using Log4j. + * + * @throws IOException + */ + private void initLog() throws IOException { + // TODO: fix session event logging + // LATER: make dedicated appender format for session log. + /* + * Appender app = slog.getAppender("log4j.appender.SESSIONLOG"); // + * slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, + * SESSION_LOG).getAbsolutePath())); // slog.addAppender(new + * FileAppender(app.getLayout(), new File(sessionDir, + * SESSION_LOG).getAbsolutePath())); for (Enumeration e = + * slog.getAllAppenders() ; e.hasMoreElements() ;) { + * System.out.println(e.nextElement()); } + */ + + if (slog != null) { + File sessionLogFile = new File(this.sessionDir, SESSION_LOG); + slog.addAppender(slogAppender = new FileAppender(new PatternLayout( + "%-4r [%t] %-5p %c %x - %m%n"), sessionLogFile.getAbsolutePath(), + true)); + } else { + log.info("No appender for SessionLog"); + } + } + + private void closeSessionLog() { + if (slog != null) { + if (slogAppender != null) { + slog.removeAppender(slogAppender); + slogAppender.close(); + slogAppender = null; + } + } + } + + /** + * 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 sessionDir1 + * + * @param sessionDir1 + */ + protected VamsasSession(File sessionDir1) throws IOException { + this(sessionDir1, null); + } + + /** + * sets up the vamsas session files and watchers in sessionDir1 + * + * @param sessionDir1 + * @param extVamDoc + * null or an existing archive to initialise the session with + * @throws any + * IOExceptions from creating session directory and files. + * @throws error + * if both extVamDoc and sessionDir1 already exist (cannot import + * new data into session in this way) + */ + protected VamsasSession(File sessionDir1, File extVamDoc) throws IOException { + if (sessionDir1 == null) + throw new Error("Null directory for VamsasSession."); + if (!sessionDir1.exists() && !sessionDir1.mkdir()) { + throw new IOException("Failed to make VamsasSession directory in " + + sessionDir1); + } + if (!sessionDir1.isDirectory() || !sessionDir1.canWrite() + || !sessionDir1.canRead()) { + throw new IOException("Cannot access '" + sessionDir1 + + "' as a read/writable Directory."); + } + boolean existingSession = checkSessionFiles(sessionDir1); + if (existingSession) { + if (extVamDoc != null) { + throw new Error( + "Client Initialisation Error: Cannot join an existing session directory with an existing vamsas document to import."); + } else { + log.debug("Joining an existing session."); + } + } + this.sessionDir = sessionDir1; + initSessionObjects(); + if (existingSession) { + slog.debug("Initialising additional VamsasSession instance"); + } else { + slog.debug("Founding client has joined VamsasSession instance"); + } + + log.debug("Attached to VamsasSession in " + sessionDir1); + if (extVamDoc != null) { + setVamsasDocument(extVamDoc); + } + slog.debug("Session directory created."); + log.debug("Initialised VamsasSession in " + sessionDir1); + } + + /** + * 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.exists() && c_file.createNewFile()) + log.debug("Created new ClientFile " + c_file); // don't care if this + // works or not + if (!v_doc.exists()) { + if (v_doc.createNewFile()) { + log.debug("Created new Vamsas Session Document File " + v_doc); + } else { + log.warn("Didn't create Vamsas Session Document file in " + v_doc); + } + } + } + + /** + * construct SessionFile objects and watchers for each + */ + private void initSessionObjects() throws IOException { + createSessionFiles(); + 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)); + storedocfile = new ClientsFile(new File(sessionDir, CLOSEANDSAVE_FILE)); + initLog(); + } + + /** + * make a new watcher object for the clientFile + * + * @return new ClientFile watcher instance + */ + public FileWatcher getClientWatcher() { + return new FileWatcher(clist.sessionFile); + } + + /** + * make a new watcher object for the vamsas Document + * + * @return new ClientFile watcher instance + */ + public FileWatcher getDocWatcher() { + return new FileWatcher(vamArchive.sessionFile); + } + + FileWatcher store_doc_file = null; + + public ClientsFile storedocfile = null; + + /** + * make a new watcher object for the messages file + * + * @return new watcher instance + */ + public FileWatcher getStoreWatcher() { + return new FileWatcher(new File(sessionDir, 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 { + // TODO: replace this with clientsFile mechanism + 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); + while (!vamArchive.lockFile()) + log.info("Trying to get lock for " + vamArchive.sessionFile); + vamArchive.updateFrom(null, xtantdoc); + xtantdoc.unlockFile(); + unlockVamsasDocument(); + // TODO: session archive provenance should be updated to reflect import from + // external source + 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 a provenance entry should be written in the + // exported document recording the export from the session + newdoc.updateFrom(null, 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."); + } + + /** + * extant archive IO handler + */ + VamsasArchive _va = null; + + /** + * 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 { + // check we haven't already done this once - probably should be done by + // caller + if (_va != null) + return _va; + // patiently wait for a lock on the document. (from + // ArchiveClient.getUpdateable()) + long tries = 5000; + while (vamArchive.getLock() == null && --tries > 0) { + // Thread.sleep(1); + log.debug("Trying to get a document lock for the " + tries + "'th time."); + } + if (tries == 0) + throw new IOException("Failed to get lock for vamsas archive."); + + VamsasArchive va = new VamsasArchive(vamArchive.sessionFile, false, true, + vamArchive); + + return va; + } + + /** + * Unlocks the vamsas archive session document after it has been closed. + * + * @throws IOException + */ + protected void unlockVamsasDocument() throws IOException { + if (_va != null) + _va.closeArchive(); + _va = null; + if (vamArchive != null) + vamArchive.unLock(); + + } + + /** + * create a uniquely named + * uk.ac.vamsas.client.simpleclient.ClientsFile.addClient(ClientHandle)ile 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; + } + + /** + * add a IClient to the session + * + * add the client to the client list file + * + * @param client + * client to add to the session + */ + protected void addClient(SimpleClient client) { + if (client == null) + slog.error("Try to add a null client to the session "); + else { + log.debug("Adding client " + client.getClientHandle().getClientUrn()); + getClientWatcherElement().haltWatch(); + clist.addClient(client.getClientHandle()); + + log.debug("Added."); + log.debug("Register Client as Active."); + try { + client.createActiveClientFile(); + } catch (IOException e) { + log.debug("Error during active client file creation."); + } + // tracks modification to the client list and readds client to the list + getClientWatcherElement().setHandler(new AddClientWatchCallBack(client)); + getClientWatcherElement().enableWatch(); + + } + } + + /** + * Handler for the client watcher. + * + * If (the current client is not in the client list, it is added again;) + */ + private class AddClientWatchCallBack implements WatcherCallBack { + + private SimpleClient client; + + /** + * Inits the handler with the client to check in the list + * + * @param client + * client to monitor in the client list + */ + protected AddClientWatchCallBack(SimpleClient client) { + this.client = client; + } + + /** + * If the client list is modified, checks if the current is still in the + * list. otherwise, readds ti. + * + * @return true to enable watcher, or false to disable it in future + * WatcherThread cycles. + */ + public boolean handleWatchEvent(WatcherElement watcher, Lock lock) { + boolean isWatchEnable = watcher.isWatchEnabled(); + if (lock == null)// no update on the list + return isWatchEnable; + log.debug("change on the client list "); + if (client != null) { + + // checks if the client is not already in the lists + ClientHandle[] cl = clist.retrieveClientList(lock);// clist.retrieveClientList(); + boolean found = false; + if (cl != null) { + for (int chi = cl.length - 1; !found && chi > -1; chi--) { + found = cl[chi].equals(this.client.getClientHandle()); + } + + } + if (!found) { + log.debug("client not in the list "); + if (log.isDebugEnabled()) + log + .debug("the client has not been found in the list. Adding it again :" + + cl); + addClient(client); + } else + log.debug("client is in the list"); + + } + log.debug("isWatchEnable " + isWatchEnable); + return isWatchEnable; + } + } + + /** + * + * removes a client from the current session removes the client from the + * session client list if the client is the last one from the session + * (ClientList), the current session is removed from active session list. + * + * The active should add them self to the client list. To insure to close the + * session,when the current client is the lact active client, clears the list + * of clients and when two cycles to insure there is no more active client, + * that otherwise would have readd themself to the list + * + * @param client + * client to remove + */ + protected void removeClient(SimpleClient client)// IClient client) + { + if (client == null) { + log.error("Null client passed to removeClient"); + return; + } + // ClientSessionFileWatcherElement cwe=getClientWatcherElement(); + // if (cwe!=null && cwe.isWatchEnabled()) { + // cwe.haltWatch(); + // }; + // set handler to check is the the last active client of the session + // Wait for several watchers cycle to see if the current client was the last + // client active in the session. + // if yes, close the session + + // getClientWatcherElement().setHandler(new RemoveClientWatchCallBack + // (client)); + // getClientWatcherElement().setTimeoutBeforeLastCycle(this.watchCycleCountBeforeLastClient); + log.info("remove client from list"); + if (clistWatchElement != null) { + clistWatchElement.haltWatch(); + clistWatchElement.watched.unlockFile(); + } + // clist.clearList(); + // clist.unlockFile(); + log.info("list cleared"); + // if (cwe!=null) { + // cwe.enableWatch(); + + log.debug("Stopping EventGenerator.."); + client.evgen.stopWatching(); + // cwe.setHandler(null); + // ask to the client to copy application data into the document + client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null, client, null); + boolean closeSession = isLastActiveClient(client); + if (closeSession) { + if (client.get_session().getUnsavedFlag()) { + log.debug("Raising request-to-save event"); + client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client, null); + } + log.debug("Raising session shutdown event"); + client.evgen._raise(Events.SESSION_SHUTDOWN, null, client + .getSessionHandle(), null); + log.debug("All events raised for finalising session " + + client.getSessionHandle().toString()); + } + // cwe.haltWatch(); + client.evgen.stopWatching(); + try { + log.debug("Attempting to release active client locks"); + client.releaseActiveClientFile(); + } catch (IOException e) { + log.error("error during active file client release"); + } + tidyUp(); + if (closeSession) { + log.debug("Last active client: closing session"); + log.info("Closing session"); + getSessionManager().removeSession(client.getSessionHandle()); + } + } + + /** + * close every file and stop. + */ + private void tidyUp() { + if (clist != null) + clist.unlockFile(); + clist = null; + storedocfile.unlockFile(); + storedocfile = null; + closeSessionLog(); + } + + private boolean isLastActiveClient(SimpleClient client) { + log.debug("Testing if current client is the last one."); + log + .debug("current client lockfile is '" + client.getClientlockFile() + + "'"); + boolean noOtherActiveClient = true; + // create, if need, subdirectory to contain client files + File clientlockFileDir = new File(this.sessionDir, clientFileDirectory); + if (!clientlockFileDir.exists()) { + log + .error("Something wrong the active client file does not exits... should not happen"); + return false; + } + + try { + + // no check every file in the directory and try to get lock on it. + File[] clientFiles = clientlockFileDir.listFiles(); + if (clientFiles == null || clientFiles.length == 0) {// there is not file + // on the directory. + // the current + // client should be + // the last one. + return true; + } + + for (int i = clientFiles.length - 1; i > -1 && noOtherActiveClient; i--) { + File clientFile = clientFiles[i]; + log.debug("testing file for lock: " + clientFile.getAbsolutePath()); + if (client.getClientLock().isTargetLockFile(clientFile)) { + log.debug("current client file found"); + continue; + } + if (clientFile != null && clientFile.exists()) { + try { + log.debug("Try to acquire a lock on the file"); + // Get a file channel for the file + FileChannel channel = new RandomAccessFile(clientFile, "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 { + java.nio.channels.FileLock activeClientFilelock = channel + .tryLock(); + + // the lock has been acquired. + // the file was not lock and so the corresponding application + // seems to have die + if (activeClientFilelock != null) { + log + .debug("lock obtained : file must be from a crashed application"); + + activeClientFilelock.release(); + log.debug("lock released"); + + channel.close(); + log.debug("channel closed"); + + // delete file + clientFile.delete(); + log.debug("crashed application file deleted"); + + } else { + noOtherActiveClient = false; + log.debug("lock not obtained : another application is active"); + } + } catch (OverlappingFileLockException e) { + // File is already locked in this thread or virtual machine + // that the expected behaviour + log.debug("lock not accessible ", e); + } + } catch (Exception e) { + log.debug("error during lock testing ", e); + } + } + } + + } catch (Exception e) { + log.error("error during counting active clients"); + } + return noOtherActiveClient; + } + + /** + * Handler for the client watcher. after a client have been removed + * + * Checks if the client is not the last active one. + * + * If (the current client is not in the client list readd it;) + */ + private class RemoveClientWatchCallBack implements WatcherCallBack { + + private SimpleClient client; + + private boolean manualCheckOfClientCount = false; + + /** + * Inits the handler with the client to check in the list + * + * @param client + * client to monitor in the client list + */ + protected RemoveClientWatchCallBack(SimpleClient client) { + this.client = client; + } + + /** + * If the client list is modified, checks if the current is still in the + * list. otherwise, readds ti. + * + * @return true to enable watcher, or false to disable it in future + * WatcherThread cycles. + */ + public boolean handleWatchEvent(WatcherElement watcher, Lock lock) { + // if lock is null, no client has been added since last, clear. + // the client is then the last client + if (client != null) { + + if (lock == null) { + + // checks if the client is not already in the lists + // ClientHandle[] cl = + // clist.retrieveClientList();//lock);//clist.retrieveClientList(); + + boolean islastClient = true; + if (manualCheckOfClientCount) { + log.debug("manual checking of count of client"); + // checks if the client is not already in the lists + ClientHandle[] cl = clist.retrieveClientList();// lock);//clist.retrieveClientList(); + if (cl == null || cl.length < 1) + // {//no client has registered as active + { + islastClient = true; + log.debug("list is empty"); + } else + islastClient = false; + log.debug("list is not empty"); + } + // if(cl == null || cl.length<1 ) + // {//no client has registered as active + if (islastClient) { + // the client is the last one, so close current session + log + .info("FROMCLIENTLIST WATCHER: last client removed: closing session"); + closeSession(client); + } + } else { + log.debug("not the last client found "); + // ask to the client to cpoy application data into the document + // client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null, + // client,null); + + // / } + + } + log.debug("Stopping EventGenerator.."); + // TODO: ensure ClientsFile lock is really released!! + // clist.unlockFile(); + client.evgen.stopWatching(); + } + watcher.setHandler(null);// Do not check if the client is the last + // client. watcher will shutdown anyway + // watcher.haltWatch(); + // watcher. + return false; + } + } + + /** + * closes the current session, and send an event to the last client to close + * the document + * + * @param client + * the last client of the client + */ + private void closeSession(SimpleClient client) { + // close document + client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client, null); + log.debug("close document request done"); + closeSession(client.getSessionHandle()); + } + + /** + * CLoses the current session + * + * @param sessionHandle + * sessionHandle of the session to remove + */ + private void closeSession(SessionHandle sessionHandle) { + getSessionManager().removeSession(sessionHandle); + log.debug("Session removed"); + } + + /** + * @return the sessionManager + */ + protected SimpleSessionManager getSessionManager() { + return sessionManager; + } + + /** + * @param sessionManager + * the sessionManager to set + */ + protected void setSessionManager(SimpleSessionManager sessionManager) { + this.sessionManager = sessionManager; + } + + public ClientsFile getStoreDocFile() { + if (storedocfile == null) { + + } + return storedocfile; + } + + ClientSessionFileWatcherElement clistWatchElement = null; + + /** + * get or create a watcher on clist. + * + * @return the contents of clistWatchElement or initialise it + */ + public ClientSessionFileWatcherElement getClientWatcherElement() { + if (clistWatchElement == null) { + clistWatchElement = new ClientSessionFileWatcherElement(clist, null); + } + return clistWatchElement; + } +}