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 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.
+ * 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
+ * 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).
+ * 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.
*
- * 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: -
+ * <applicationname>.version.sessionnumber.raw (string given in
+ * vamsasdocument.xml applicationData entry)
*
- * Zip file entries
- * - vamsasdocument.xml : core info
- * one or more:
- * - <applicationname>.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.
+ * 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 -
+ * During the session - Update watcher checks for file change -
+ *
+ * Procedures for file based session message exchange - session document
+ * modification flag
*
- * 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.
+ * 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";
+ public static final String CLOSEANDSAVE_FILE = "stored.log";
+
/**
- * session file storing the last_stored_stat data
+ * session file storing the last_stored_stat data
*/
- public static final String MODIFIEDDOC_FILE="modified";
+ 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 )
+ * 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 = 2 ;
-
+ private final int watchCycleCountBeforeLastClient = 1220;
+
/**
- * time between checking
+ * time between checking
*/
- public int WATCH_SLEEP=30;
-
+ 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));
+ SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir,
+ MODIFIEDDOC_FILE));
if (!laststored.clearFlag())
- log.warn("Unsaved flag was not cleared for "+sessionDir);
+ 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));
+ SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir,
+ MODIFIEDDOC_FILE));
if (!laststored.setFlag())
- log.warn("Couldn't set the Unsaved flag for "+sessionDir);
+ log.warn("Couldn't set the Unsaved flag for " + sessionDir);
}
+
/**
*
- * @return true if session document has been modified since last offline storage event
+ * @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));
+ SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir,
+ MODIFIEDDOC_FILE));
return laststored.checkFlag();
}
+
/**
* log file location
*/
- public static final String SESSION_LOG="Log.txt";
+ 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(new FileAppender(new PatternLayout("%-4r [%t] %-5p %c %x - %m%n"), sessionLogFile.getAbsolutePath(), true));
+ /*
+ * 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";
+
+ public static final String CLIENT_LIST = "Clients.obj";
+
/**
* holds the data
*/
- VamsasFile vamArchive;
- public static final String VAMSAS_OBJ="VamDoc.jar";
-
+ VamsasFile vamArchive;
+
+ public static final String VAMSAS_OBJ = "VamDoc.jar";
+
/**
- * sets up the vamsas session files and watchers in sessionDir
+ * sets up the vamsas session files and watchers in sessionDir1
+ *
* @param sessionDir1
*/
protected VamsasSession(File sessionDir1) throws IOException {
- if (sessionDir1==null)
+ 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()) {
- if (!sessionDir1.isDirectory() || !sessionDir1.canWrite() || !sessionDir1.canRead())
- throw new IOException("Cannot access '"+sessionDir1+"' as a read/writable Directory.");
- if (!checkSessionFiles(sessionDir1))
- log.warn("checkSessionFiles() returned false. Possible client implementation error");
- this.sessionDir = sessionDir1;
- initSessionObjects();
- slog.debug("Initialising additional VamsasSession instance");
- log.debug("Attached to VamsasSession in "+sessionDir1);
- //}
- } else {
- // start from scratch
- if (!sessionDir1.mkdir())
- throw new IOException("Failed to make VamsasSession directory in "+sessionDir1);
- createSessionFiles();
- initSessionObjects();
- slog.debug("Session directory created.");
- log.debug("Initialised VamsasSession in "+sessionDir1);
+ 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);
+ 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 (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() && v_doc.createNewFile())
- log.debug("Created new Vamsas Session Document File "+v_doc);
+ 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));
+ 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;
+
+ 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));
+ 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.
+ * 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 {
+ 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();
+ 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());
+ // 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)
+ if (store_doc_file != null)
store_doc_file.setState();
- slog.info("FinalizeAppData request from "+user.getFullName()+" using "+client.getClientUrn()+"");
+ 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.
+ * 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);
+ 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);
- // LATER: decide if session archive provenance should be updated to reflect access.
- // TODO: soon! do a proper import objects from external file
+ 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.
+ * 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);
+ 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())
+ 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(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).
+ 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;
+ * 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.
+ * 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.
+ * @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)
+ // 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)
+ // 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);
+
+ 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)
+ if (_va != null)
_va.closeArchive();
- _va=null;
- if (vamArchive!=null)
+ _va = null;
+ if (vamArchive != null)
vamArchive.unLock();
-
+
}
+
/**
- * create a uniquely named uk.ac.vamsas.client.simpleclient.ClientsFile.addClient(ClientHandle)ile in the session Directory
+ * 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
+ * @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);
+ 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
+ *
+ * @param client
+ * client to add to the session
*/
- protected void addClient(IClient client)
- {
+ 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());
+ log.debug("Adding client " + client.getClientHandle().getClientUrn());
getClientWatcherElement().haltWatch();
clist.addClient(client.getClientHandle());
- getClientWatcherElement().enableWatch();
+
log.debug("Added.");
log.debug("Register Client as Active.");
-
- //tracks modification to the client list and readds client to the list
+ 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 IClient client ;
-
+ */
+ 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
+ * Inits the handler with the client to check in the list
+ *
+ * @param client
+ * client to monitor in the client list
*/
- protected AddClientWatchCallBack (IClient 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 (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());
- }
- } else
- if (! found)
- {
- if( log.isDebugEnabled())
- log.debug("the client has not been found in the list. Adding it again :"+cl);
- addClient(client);
- }
+ 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());
+ }
- }
-
- return isWatchEnable;
}
+ 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");
+
}
-
-/**
- *
- * 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.
- *
- * @param client client to remove
- */
- protected void removeClient(SimpleClient client)//IClient client)
+ 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);
+ 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");
- clist.clearList();
- if (cwe!=null) {
- cwe.enableWatch();
+ 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) {
+ log.debug("Raising request-to-save event");
+ client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client, null);
+ client.evgen._raise(Events.SESSION_SHUTDOWN, null, client
+ .getSessionHandle(), null);
+ }
+ // 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;
}
-
-
-
- /*clist.removeClient(client.getClientHandle(),null);
- if (this.clist.retrieveClientList() == null|| this.clist.retrieveClientList().length<1)
- {//assume it is the last client has been removed shutting down session
- slog.info("last client removed: removing session");
- log.debug("last client removed: removing session");
- this.getSessionManager().removeSession(client.getSessionHandle());
+
+ 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;
}
- else
- {
- int active=clist.retrieveClientList().length;
- log.debug("Still "+active+" active clients");
- slog.info("Still "+active+" active clients");
- }*/
-
+
+ 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 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
+ * 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)
- {
- // boolean isWatchEnable = watcher.isWatchEnabled();
- if (client != null)
+ 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
{
-
- //checks if the client is not already in the lists
- ClientHandle[] cl = clist.retrieveClientList(lock);//clist.retrieveClientList();
-// ask to the client to cpoy application data into the document
- client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null, client,null);
-
- if(cl == null || cl.length<1 )
- {//no client has registered as active
- //the client is the last one, so close current session
- log.info("last client removed: closing session");
-
-// close document
- client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client,null);
- log.debug("close document request done");
-
- getSessionManager().removeSession(client.getSessionHandle());
- log.debug("Session removed");
- }
- 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);
-
- }
- // watcher.haltWatch();
- log.debug("Stopping EventGenerator..");
- client.evgen.stopWatching();
-
+ 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("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);
+
+ // / }
- }
- watcher.setHandler(null);//Do not check if the client is the last client. watcher will shutdown anyway
-
- return false;
}
+ 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;
+ }
+ }
-/**
- * @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) {
-
+ /**
+ * 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 storedocfile;
-}
-ClientSessionFileWatcherElement clistWatchElement=null;
-public ClientSessionFileWatcherElement getClientWatcherElement() {
- if (clistWatchElement==null) {
- clistWatchElement=new ClientSessionFileWatcherElement(clist,null);
+ /**
+ * @return the sessionManager
+ */
+ protected SimpleSessionManager getSessionManager() {
+ return sessionManager;
}
- return clistWatchElement;
-}
-/**
- * writes a vector of vorba Ids to the session.
- * @param modObjects
-public void setModObjectList(Vector modObjects) {
- log.debug("Writing "+modObjects.size()+" ids to ModObjectList");
- // TODO Auto-generated method stub
-}
-**
- * get current list of modified objects.
- * @return null or Vector of objects
- *
-public Vector getModObjectList() {
- log.debug("Reading modObjectList");
- return null;
-}
-*/
-}
+ /**
+ * @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;
+ }
+}