2 * This file is part of the Vamsas Client version 0.2.
\r
3 * Copyright 2010 by Jim Procter, Iain Milne, Pierre Marguerite,
\r
4 * Andrew Waterhouse and Dominik Lindner.
\r
6 * Earlier versions have also been incorporated into Jalview version 2.4
\r
7 * since 2008, and TOPALi version 2 since 2007.
\r
9 * The Vamsas Client is free software: you can redistribute it and/or modify
\r
10 * it under the terms of the GNU Lesser General Public License as published by
\r
11 * the Free Software Foundation, either version 3 of the License, or
\r
12 * (at your option) any later version.
\r
14 * The Vamsas Client is distributed in the hope that it will be useful,
\r
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
17 * GNU Lesser General Public License for more details.
\r
19 * You should have received a copy of the GNU Lesser General Public License
\r
20 * along with the Vamsas Client. If not, see <http://www.gnu.org/licenses/>.
\r
22 package uk.ac.vamsas.client.simpleclient;
\r
24 import java.io.File;
\r
25 import java.io.IOException;
\r
26 import java.io.RandomAccessFile;
\r
27 import java.nio.channels.FileChannel;
\r
28 import java.nio.channels.OverlappingFileLockException;
\r
30 import org.apache.commons.logging.Log;
\r
31 import org.apache.commons.logging.LogFactory;
\r
32 import org.apache.log4j.FileAppender;
\r
33 import org.apache.log4j.Logger;
\r
34 import org.apache.log4j.PatternLayout;
\r
36 import uk.ac.vamsas.client.ClientHandle;
\r
37 import uk.ac.vamsas.client.Events;
\r
38 import uk.ac.vamsas.client.InvalidSessionUrnException;
\r
39 import uk.ac.vamsas.client.UserHandle;
\r
42 * Does all the IO operations for a SimpleClient instance accessing a
\r
43 * SimpleClient vamsas session.
\r
45 * Basically, it defines the various standard names for the files in the session
\r
46 * directory (that maps to the sessionUrn), provides constructors for the file
\r
47 * handlers and watchers of those file entities, and some higher level methods
\r
48 * to check and change the state flags for the session.
\r
50 * TODO: move the stuff below to the SimpleClientFactory documentation. much may
\r
51 * not be valid now : Vamsas client is intialised with a path to create live
\r
52 * session directories. This path may contain a vamsas.properties file that sets
\r
53 * additional parameters (otherwise client just uses the one on the classpath).
\r
55 * A vamsas session consists of : SessionDir - translates to urn of a live
\r
56 * session. Contains: Vamsas Document (as a jar), Session client list file, both
\r
57 * of which may be locked, and additional temporary versions of these files when
\r
58 * write operations are taking place.
\r
60 * Zip file entries - vamsasdocument.xml : core info one or more: -
\r
61 * <applicationname>.version.sessionnumber.raw (string given in
\r
62 * vamsasdocument.xml applicationData entry)
\r
64 * Lockfile - filename given in the vamsasdocument.xml. Should be checked for
\r
65 * validity by any client and rewritten if necessary. The lockfile can point to
\r
66 * the jar itself. Mode of operation. Initially - documentHandler either: -
\r
67 * creates a zip for a new session for the client - connect to an existing
\r
68 * session zip 1. reads session urn file 2. waits for lock 3. examines session -
\r
69 * decide whether to create new application data slice or connect to one stored
\r
70 * in session. 4. writes info into session file 5. releases lock and generates
\r
71 * local client events. 6. Creates Watcher thread to generate events.
\r
73 * During the session - Update watcher checks for file change -
\r
75 * Procedures for file based session message exchange - session document
\r
80 public class VamsasSession {
\r
82 * indicator file for informing other processes that they should finalise
\r
83 * their vamsas datasets for storing into a vamsas archive.
\r
85 public static final String CLOSEANDSAVE_FILE = "stored.log";
\r
88 * session file storing the last_stored_stat data
\r
90 public static final String MODIFIEDDOC_FILE = "modified";
\r
92 private SimpleSessionManager sessionManager = null;
\r
95 * Count of cycles before considering the current client as the last one of
\r
96 * the session (if no other client registered as active )
\r
98 private final int watchCycleCountBeforeLastClient = 1220;
\r
101 * time between checking
\r
103 public int WATCH_SLEEP = 30;
\r
105 protected String clientFileDirectory = "clients";
\r
108 * called to clear update flag after a successful offline storage event
\r
110 protected void clearUnsavedFlag() {
\r
111 SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir,
\r
112 MODIFIEDDOC_FILE));
\r
113 if (!laststored.clearFlag())
\r
114 log.warn("Unsaved flag was not cleared for " + sessionDir);
\r
118 * called to indicate session document has been modified.
\r
121 protected void setUnsavedFlag() {
\r
122 SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir,
\r
123 MODIFIEDDOC_FILE));
\r
124 if (!laststored.setFlag())
\r
125 log.warn("Couldn't set the Unsaved flag for " + sessionDir);
\r
130 * @return true if session document has been modified since last offline
\r
133 protected boolean getUnsavedFlag() {
\r
134 SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir,
\r
135 MODIFIEDDOC_FILE));
\r
136 return laststored.checkFlag();
\r
140 * log file location
\r
142 public static final String SESSION_LOG = "Log.txt";
\r
144 private static Log log = LogFactory.getLog(VamsasSession.class);
\r
146 protected Logger slog = Logger.getLogger("uk.ac.vamsas.client.SessionLog");
\r
149 * the appender that writes to the log file inside the session's directory.
\r
151 private FileAppender slogAppender = null;
\r
154 * setup the sessionLog using Log4j.
\r
156 * @throws IOException
\r
158 private void initLog() throws IOException {
\r
159 // TODO: fix session event logging
\r
160 // LATER: make dedicated appender format for session log.
\r
162 * Appender app = slog.getAppender("log4j.appender.SESSIONLOG"); //
\r
163 * slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir,
\r
164 * SESSION_LOG).getAbsolutePath())); // slog.addAppender(new
\r
165 * FileAppender(app.getLayout(), new File(sessionDir,
\r
166 * SESSION_LOG).getAbsolutePath())); for (Enumeration e =
\r
167 * slog.getAllAppenders() ; e.hasMoreElements() ;) {
\r
168 * System.out.println(e.nextElement()); }
\r
171 if (slog != null) {
\r
172 File sessionLogFile = new File(this.sessionDir, SESSION_LOG);
\r
173 slog.addAppender(slogAppender = new FileAppender(new PatternLayout(
\r
174 "%-4r [%t] %-5p %c %x - %m%n"), sessionLogFile.getAbsolutePath(),
\r
177 log.info("No appender for SessionLog");
\r
181 private void closeSessionLog() {
\r
182 if (slog != null) {
\r
183 if (slogAppender != null) {
\r
184 slog.removeAppender(slogAppender);
\r
185 slogAppender.close();
\r
186 slogAppender = null;
\r
192 * the sessionDir is given as the session location for new clients.
\r
194 protected File sessionDir;
\r
197 * holds the list of attached clients
\r
201 public static final String CLIENT_LIST = "Clients.obj";
\r
206 VamsasFile vamArchive;
\r
208 public static final String VAMSAS_OBJ = "VamDoc.jar";
\r
211 * sets up the vamsas session files and watchers in sessionDir1
\r
213 * @param sessionDir1
\r
215 protected VamsasSession(File sessionDir1) throws IOException {
\r
216 this(sessionDir1, null);
\r
218 private SimpleSessionHandle sessionHandle = null;
\r
220 * sets up the vamsas session files and watchers in sessionDir1
\r
222 * @param sessionDir1
\r
224 * null or an existing archive to initialise the session with
\r
226 * IOExceptions from creating session directory and files.
\r
228 * if both extVamDoc and sessionDir1 already exist (cannot import
\r
229 * new data into session in this way)
\r
231 protected VamsasSession(File sessionDir1, File extVamDoc) throws IOException {
\r
232 this(sessionDir1,extVamDoc,null);
\r
235 * sets up the vamsas session files and watchers in sessionDir1
\r
237 * @param sessionDir1
\r
239 * null or an existing archive to initialise the session with
\r
240 * @param preferredName - optional string to use to generate a new session URI
\r
242 * IOExceptions from creating session directory and files.
\r
244 * if both extVamDoc and sessionDir1 already exist (cannot import
\r
245 * new data into session in this way)
\r
246 * MalformedURI if preferredName cannot be used to derive a URI of the form simpleclient::preferredName
\r
248 public VamsasSession(File sessionDir1, File extVamDoc, String preferredName) throws IOException {
\r
249 // TODO: refactor to separate extVamDoc path from session URN - enables non-local URLs to be locally bound to sessions.
\r
250 if (sessionDir1 == null)
\r
251 throw new Error("Null directory for VamsasSession.");
\r
252 if (!sessionDir1.exists() && !sessionDir1.mkdir()) {
\r
253 throw new IOException("Failed to make VamsasSession directory in "
\r
256 if (!sessionDir1.isDirectory() || !sessionDir1.canWrite()
\r
257 || !sessionDir1.canRead()) {
\r
258 throw new IOException("Cannot access '" + sessionDir1
\r
259 + "' as a read/writable Directory.");
\r
261 boolean existingSession = checkSessionFiles(sessionDir1);
\r
262 if (existingSession) {
\r
263 if (extVamDoc != null) {
\r
265 "Client Initialisation Error: Cannot join an existing session directory with an existing vamsas document to import.");
\r
267 log.debug("Joining an existing session.");
\r
270 this.sessionDir = sessionDir1;
\r
271 // construct session URN
\r
272 SessionUrn sessUrn = new SessionUrn(sessionDir);
\r
273 if (extVamDoc!=null) {
\r
274 // Construct Session URN from the original vamsas document.
\r
275 sessUrn = new SessionUrn(extVamDoc);
\r
277 if (preferredName!=null) {
\r
279 sessUrn = new SessionUrn(preferredName);
\r
280 } catch (InvalidSessionUrnException e) {
\r
281 throw new Error("Malformed URI : preferredName = "+preferredName,e);
\r
284 // create the session handle
\r
285 sessionHandle = new SimpleSessionHandle(sessUrn.getSessionUrn(),sessionDir);
\r
286 initSessionObjects();
\r
287 if (existingSession) {
\r
288 slog.debug("Initialising additional VamsasSession instance");
\r
290 slog.debug("Founding client has joined VamsasSession instance");
\r
293 log.debug("Attached to VamsasSession in " + sessionDir1);
\r
294 if (extVamDoc != null) {
\r
295 setVamsasDocument(extVamDoc);
\r
297 slog.debug("Session directory created.");
\r
298 log.debug("Initialised VamsasSession in " + sessionDir1);
\r
303 * tests presence of existing sessionfiles files in dir
\r
308 private boolean checkSessionFiles(File dir) throws IOException {
\r
309 File c_file = new File(dir, CLIENT_LIST);
\r
310 File v_doc = new File(dir, VAMSAS_OBJ);
\r
311 if (c_file.exists() && v_doc.exists())
\r
317 * create new empty files in dir
\r
320 private void createSessionFiles() throws IOException {
\r
321 if (sessionDir == null)
\r
322 throw new IOException(
\r
323 "Invalid call to createSessionFiles() with null sessionDir");
\r
324 File c_file = new File(sessionDir, CLIENT_LIST);
\r
325 File v_doc = new File(sessionDir, VAMSAS_OBJ);
\r
326 if (!c_file.exists() && c_file.createNewFile())
\r
327 log.debug("Created new ClientFile " + c_file); // don't care if this
\r
329 if (!v_doc.exists()) {
\r
330 if (v_doc.createNewFile()) {
\r
331 log.debug("Created new Vamsas Session Document File " + v_doc);
\r
333 log.warn("Didn't create Vamsas Session Document file in " + v_doc);
\r
339 * construct SessionFile objects and watchers for each
\r
341 private void initSessionObjects() throws IOException {
\r
342 createSessionFiles();
\r
343 if (clist != null || vamArchive != null)
\r
344 throw new IOException(
\r
345 "initSessionObjects called for initialised VamsasSession object.");
\r
346 clist = new ClientsFile(new File(sessionDir, CLIENT_LIST));
\r
347 vamArchive = new VamsasFile(new File(sessionDir, VAMSAS_OBJ));
\r
348 storedocfile = new ClientsFile(new File(sessionDir, CLOSEANDSAVE_FILE));
\r
353 * make a new watcher object for the clientFile
\r
355 * @return new ClientFile watcher instance
\r
357 public FileWatcher getClientWatcher() {
\r
358 return new FileWatcher(clist.sessionFile);
\r
362 * make a new watcher object for the vamsas Document
\r
364 * @return new ClientFile watcher instance
\r
366 public FileWatcher getDocWatcher() {
\r
367 return new FileWatcher(vamArchive.sessionFile);
\r
370 FileWatcher store_doc_file = null;
\r
372 public ClientsFile storedocfile = null;
\r
375 * make a new watcher object for the messages file
\r
377 * @return new watcher instance
\r
379 public FileWatcher getStoreWatcher() {
\r
380 return new FileWatcher(new File(sessionDir, CLOSEANDSAVE_FILE));
\r
385 * write to the StoreWatcher file to indicate that a storeDocumentRequest has
\r
386 * been made. The local client's storeWatcher FileWatcher object is updated so
\r
387 * the initial change is not registered.
\r
393 public void addStoreDocumentRequest(ClientHandle client, UserHandle user)
\r
394 throws IOException {
\r
395 // TODO: replace this with clientsFile mechanism
\r
396 SessionFile sfw = new SessionFile(new File(sessionDir, CLOSEANDSAVE_FILE));
\r
397 while (!sfw.lockFile())
\r
398 log.debug("Trying to get lock for " + CLOSEANDSAVE_FILE);
\r
399 RandomAccessFile sfwfile = sfw.fileLock.getRaFile();
\r
400 sfwfile.setLength(0); // wipe out any old info.
\r
401 // TODO: rationalise what gets written to this file (ie do we want other
\r
402 // clients to read the id of the requestor?)
\r
403 sfwfile.writeUTF(client.getClientUrn() + ":" + user.getFullName() + "@"
\r
404 + user.getOrganization());
\r
406 if (store_doc_file != null)
\r
407 store_doc_file.setState();
\r
408 slog.info("FinalizeAppData request from " + user.getFullName() + " using "
\r
409 + client.getClientUrn() + "");
\r
413 * create a new session with an existing vamsas Document - by copying it into
\r
418 public void setVamsasDocument(File archive) throws IOException {
\r
419 log.debug("Transferring vamsas data from " + archive + " to session:"
\r
420 + vamArchive.sessionFile);
\r
421 SessionFile xtantdoc = new SessionFile(archive);
\r
422 while (!vamArchive.lockFile())
\r
423 log.info("Trying to get lock for " + vamArchive.sessionFile);
\r
424 vamArchive.updateFrom(null, xtantdoc);
\r
425 xtantdoc.unlockFile();
\r
426 unlockVamsasDocument();
\r
427 // TODO: session archive provenance should be updated to reflect import from
\r
429 log.debug("Transfer complete.");
\r
433 * write session as a new vamsas Document (this will overwrite any existing
\r
434 * file without warning) TODO: test TODO: verify that lock should be released
\r
435 * for vamsas document.
\r
437 * @param destarchive
\r
439 protected void writeVamsasDocument(File destarchive, Lock extlock)
\r
440 throws IOException {
\r
441 log.debug("Transferring vamsas data from " + vamArchive.sessionFile
\r
442 + " to session:" + destarchive);
\r
443 SessionFile newdoc = new SessionFile(destarchive);
\r
444 if (extlock == null && !vamArchive.lockFile())
\r
445 while (!vamArchive.lockFile())
\r
446 log.info("Trying to get lock for " + vamArchive.sessionFile);
\r
447 // TODO: LATER: decide if a provenance entry should be written in the
\r
448 // exported document recording the export from the session
\r
449 newdoc.updateFrom(null, vamArchive);
\r
450 // LATER: LATER: fix use of updateFrom for file systems where locks cannot
\r
451 // be made (because they don't have a lockManager, ie NFS/Unix, etc).
\r
452 vamArchive.unLock();
\r
453 newdoc.unlockFile();
\r
454 log.debug("Transfer complete.");
\r
458 * extant archive IO handler
\r
460 VamsasArchive _va = null;
\r
463 * Creates a VamsasArchive Vobject for accessing and updating document Note:
\r
464 * this will lock the Vamsas Document for exclusive access to the client.
\r
466 * @return session vamsas document
\r
467 * @throws IOException
\r
468 * if locks fail or vamsas document read fails.
\r
470 protected VamsasArchive getVamsasDocument() throws IOException {
\r
471 // check we haven't already done this once - probably should be done by
\r
475 // patiently wait for a lock on the document. (from
\r
476 // ArchiveClient.getUpdateable())
\r
478 while (vamArchive.getLock() == null && --tries > 0) {
\r
479 // Thread.sleep(1);
\r
480 log.debug("Trying to get a document lock for the " + tries + "'th time.");
\r
483 throw new IOException("Failed to get lock for vamsas archive.");
\r
485 VamsasArchive va = new VamsasArchive(vamArchive.sessionFile, false, true,
\r
492 * Unlocks the vamsas archive session document after it has been closed.
\r
494 * @throws IOException
\r
496 protected void unlockVamsasDocument() throws IOException {
\r
498 _va.closeArchive();
\r
500 if (vamArchive != null)
\r
501 vamArchive.unLock();
\r
506 * create a uniquely named
\r
507 * uk.ac.vamsas.client.simpleclient.ClientsFile.addClient(ClientHandle)ile in
\r
508 * the session Directory
\r
510 * @see java.io.File.createTempFile
\r
515 * @return SessionFile object configured for the new file (of length zero)
\r
516 * @throws IOException
\r
518 protected SessionFile getTempSessionFile(String pref, String suff)
\r
519 throws IOException {
\r
520 File tfile = File.createTempFile(pref, suff, sessionDir);
\r
521 SessionFile tempFile = new SessionFile(tfile);
\r
526 * add a IClient to the session
\r
528 * add the client to the client list file
\r
531 * client to add to the session
\r
533 protected void addClient(SimpleClient client) {
\r
534 if (client == null)
\r
535 slog.error("Try to add a null client to the session ");
\r
537 log.debug("Adding client " + client.getClientHandle().getClientUrn());
\r
538 getClientWatcherElement().haltWatch();
\r
539 clist.addClient(client.getClientHandle());
\r
541 log.debug("Added.");
\r
542 log.debug("Register Client as Active.");
\r
544 client.createActiveClientFile();
\r
545 } catch (IOException e) {
\r
546 log.debug("Error during active client file creation.");
\r
548 // tracks modification to the client list and readds client to the list
\r
549 getClientWatcherElement().setHandler(new AddClientWatchCallBack(client));
\r
550 getClientWatcherElement().enableWatch();
\r
556 * Handler for the client watcher.
\r
558 * If (the current client is not in the client list, it is added again;)
\r
560 private class AddClientWatchCallBack implements WatcherCallBack {
\r
562 private SimpleClient client;
\r
565 * Inits the handler with the client to check in the list
\r
568 * client to monitor in the client list
\r
570 protected AddClientWatchCallBack(SimpleClient client) {
\r
571 this.client = client;
\r
575 * If the client list is modified, checks if the current is still in the
\r
576 * list. otherwise, readds ti.
\r
578 * @return true to enable watcher, or false to disable it in future
\r
579 * WatcherThread cycles.
\r
581 public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {
\r
582 boolean isWatchEnable = watcher.isWatchEnabled();
\r
583 if (lock == null)// no update on the list
\r
584 return isWatchEnable;
\r
585 log.debug("change on the client list ");
\r
586 if (client != null) {
\r
588 // checks if the client is not already in the lists
\r
589 ClientHandle[] cl = clist.retrieveClientList(lock);// clist.retrieveClientList();
\r
590 boolean found = false;
\r
592 for (int chi = cl.length - 1; !found && chi > -1; chi--) {
\r
593 found = cl[chi].equals(this.client.getClientHandle());
\r
598 log.debug("client not in the list ");
\r
599 if (log.isDebugEnabled())
\r
601 .debug("the client has not been found in the list. Adding it again :"
\r
605 log.debug("client is in the list");
\r
608 log.debug("isWatchEnable " + isWatchEnable);
\r
609 return isWatchEnable;
\r
615 * removes a client from the current session removes the client from the
\r
616 * session client list if the client is the last one from the session
\r
617 * (ClientList), the current session is removed from active session list.
\r
619 * The active should add them self to the client list. To insure to close the
\r
620 * session,when the current client is the lact active client, clears the list
\r
621 * of clients and when two cycles to insure there is no more active client,
\r
622 * that otherwise would have readd themself to the list
\r
627 protected void removeClient(SimpleClient client)// IClient client)
\r
629 if (client == null) {
\r
630 log.error("Null client passed to removeClient");
\r
633 // ClientSessionFileWatcherElement cwe=getClientWatcherElement();
\r
634 // if (cwe!=null && cwe.isWatchEnabled()) {
\r
635 // cwe.haltWatch();
\r
637 // set handler to check is the the last active client of the session
\r
638 // Wait for several watchers cycle to see if the current client was the last
\r
639 // client active in the session.
\r
640 // if yes, close the session
\r
642 // getClientWatcherElement().setHandler(new RemoveClientWatchCallBack
\r
644 // getClientWatcherElement().setTimeoutBeforeLastCycle(this.watchCycleCountBeforeLastClient);
\r
645 log.info("remove client from list");
\r
646 if (clistWatchElement != null) {
\r
647 clistWatchElement.haltWatch();
\r
648 clistWatchElement.watched.unlockFile();
\r
650 // clist.clearList();
\r
651 // clist.unlockFile();
\r
652 log.info("list cleared");
\r
653 // if (cwe!=null) {
\r
654 // cwe.enableWatch();
\r
656 log.debug("Stopping EventGenerator..");
\r
657 client.evgen.stopWatching();
\r
658 // cwe.setHandler(null);
\r
659 // ask to the client to copy application data into the document
\r
660 client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null, client, null);
\r
661 boolean closeSession = isLastActiveClient(client);
\r
662 if (closeSession) {
\r
663 if (client.get_session().getUnsavedFlag()) {
\r
664 log.debug("Raising request-to-save event");
\r
665 client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client, null);
\r
667 log.debug("Raising session shutdown event");
\r
668 client.evgen._raise(Events.SESSION_SHUTDOWN, null, client
\r
669 .getSessionHandle(), null);
\r
670 log.debug("All events raised for finalising session "
\r
671 + client.getSessionHandle().toString());
\r
673 // cwe.haltWatch();
\r
674 client.evgen.stopWatching();
\r
676 log.debug("Attempting to release active client locks");
\r
677 client.releaseActiveClientFile();
\r
678 } catch (IOException e) {
\r
679 log.error("error during active file client release");
\r
682 if (closeSession) {
\r
683 log.debug("Last active client: closing session");
\r
684 log.info("Closing session");
\r
685 closeSession(client.getSessionHandle());
\r
690 * close every file and stop.
\r
692 private void tidyUp() {
\r
694 clist.unlockFile();
\r
696 storedocfile.unlockFile();
\r
697 storedocfile = null;
\r
701 private boolean isLastActiveClient(SimpleClient client) {
\r
702 log.debug("Testing if current client is the last one.");
\r
704 .debug("current client lockfile is '" + client.getClientlockFile()
\r
706 boolean noOtherActiveClient = true;
\r
707 // create, if need, subdirectory to contain client files
\r
708 File clientlockFileDir = new File(this.sessionDir, clientFileDirectory);
\r
709 if (!clientlockFileDir.exists()) {
\r
711 .error("Something wrong the active client file does not exits... should not happen");
\r
717 // no check every file in the directory and try to get lock on it.
\r
718 File[] clientFiles = clientlockFileDir.listFiles();
\r
719 if (clientFiles == null || clientFiles.length == 0) {// there is not file
\r
720 // on the directory.
\r
722 // client should be
\r
727 for (int i = clientFiles.length - 1; i > -1 && noOtherActiveClient; i--) {
\r
728 File clientFile = clientFiles[i];
\r
729 log.debug("testing file for lock: " + clientFile.getAbsolutePath());
\r
730 if (client.getClientLock().isTargetLockFile(clientFile)) {
\r
731 log.debug("current client file found");
\r
734 if (clientFile != null && clientFile.exists()) {
\r
736 log.debug("Try to acquire a lock on the file");
\r
737 // Get a file channel for the file
\r
738 FileChannel channel = new RandomAccessFile(clientFile, "rw")
\r
741 // Use the file channel to create a lock on the file.
\r
742 // This method blocks until it can retrieve the lock.
\r
743 // java.nio.channels.FileLock activeClientFilelock = channel.lock();
\r
745 // Try acquiring the lock without blocking. This method returns
\r
746 // null or throws an exception if the file is already locked.
\r
748 java.nio.channels.FileLock activeClientFilelock = channel
\r
751 // the lock has been acquired.
\r
752 // the file was not lock and so the corresponding application
\r
753 // seems to have die
\r
754 if (activeClientFilelock != null) {
\r
756 .debug("lock obtained : file must be from a crashed application");
\r
758 activeClientFilelock.release();
\r
759 log.debug("lock released");
\r
762 log.debug("channel closed");
\r
765 clientFile.delete();
\r
766 log.debug("crashed application file deleted");
\r
769 noOtherActiveClient = false;
\r
770 log.debug("lock not obtained : another application is active");
\r
772 } catch (OverlappingFileLockException e) {
\r
773 // File is already locked in this thread or virtual machine
\r
774 // that the expected behaviour
\r
775 log.debug("lock not accessible ", e);
\r
777 } catch (Exception e) {
\r
778 log.debug("error during lock testing ", e);
\r
783 } catch (Exception e) {
\r
784 log.error("error during counting active clients");
\r
786 return noOtherActiveClient;
\r
790 * Handler for the client watcher. after a client have been removed
\r
792 * Checks if the client is not the last active one.
\r
794 * If (the current client is not in the client list readd it;)
\r
796 private class RemoveClientWatchCallBack implements WatcherCallBack {
\r
798 private SimpleClient client;
\r
800 private boolean manualCheckOfClientCount = false;
\r
803 * Inits the handler with the client to check in the list
\r
806 * client to monitor in the client list
\r
808 protected RemoveClientWatchCallBack(SimpleClient client) {
\r
809 this.client = client;
\r
813 * If the client list is modified, checks if the current is still in the
\r
814 * list. otherwise, readds ti.
\r
816 * @return true to enable watcher, or false to disable it in future
\r
817 * WatcherThread cycles.
\r
819 public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {
\r
820 // if lock is null, no client has been added since last, clear.
\r
821 // the client is then the last client
\r
822 if (client != null) {
\r
824 if (lock == null) {
\r
826 // checks if the client is not already in the lists
\r
827 // ClientHandle[] cl =
\r
828 // clist.retrieveClientList();//lock);//clist.retrieveClientList();
\r
830 boolean islastClient = true;
\r
831 if (manualCheckOfClientCount) {
\r
832 log.debug("manual checking of count of client");
\r
833 // checks if the client is not already in the lists
\r
834 ClientHandle[] cl = clist.retrieveClientList();// lock);//clist.retrieveClientList();
\r
835 if (cl == null || cl.length < 1)
\r
836 // {//no client has registered as active
\r
838 islastClient = true;
\r
839 log.debug("list is empty");
\r
841 islastClient = false;
\r
842 log.debug("list is not empty");
\r
844 // if(cl == null || cl.length<1 )
\r
845 // {//no client has registered as active
\r
846 if (islastClient) {
\r
847 // the client is the last one, so close current session
\r
849 .info("FROMCLIENTLIST WATCHER: last client removed: closing session");
\r
850 closeSession(client);
\r
853 log.debug("not the last client found ");
\r
854 // ask to the client to cpoy application data into the document
\r
855 // client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null,
\r
861 log.debug("Stopping EventGenerator..");
\r
862 // TODO: ensure ClientsFile lock is really released!!
\r
863 // clist.unlockFile();
\r
864 client.evgen.stopWatching();
\r
866 watcher.setHandler(null);// Do not check if the client is the last
\r
867 // client. watcher will shutdown anyway
\r
868 // watcher.haltWatch();
\r
875 * closes the current session, and send an event to the last client to close
\r
879 * the last client of the client
\r
881 private void closeSession(SimpleClient client) {
\r
883 client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client, null);
\r
884 log.debug("close document request done");
\r
885 closeSession(client.getSessionHandle());
\r
889 * CLoses the current session
\r
891 * @param sessionHandle
\r
892 * sessionHandle of the session to remove
\r
894 private void closeSession(uk.ac.vamsas.client.SessionHandle sessionHandle) {
\r
895 //if (sessionHandle instanceof SimpleSessionHandle)
\r
897 getSessionManager().removeSession(sessionHandle);
\r
898 log.debug("Session removed");
\r
900 // log.error("Cannot close a sessionHandle (URN="+sessionHandle.getSessionUrn()+") which is of type "+sessionHandle.getClass());
\r
901 // throw new Error("Tried to close a non-SimpleClient vamsas sessionHandle");
\r
906 * @return the sessionManager
\r
908 protected SimpleSessionManager getSessionManager() {
\r
909 return sessionManager;
\r
913 * @param sessionManager
\r
914 * the sessionManager to set
\r
916 protected void setSessionManager(SimpleSessionManager sessionManager) {
\r
917 this.sessionManager = sessionManager;
\r
920 public ClientsFile getStoreDocFile() {
\r
921 if (storedocfile == null) {
\r
924 return storedocfile;
\r
927 ClientSessionFileWatcherElement clistWatchElement = null;
\r
930 * get or create a watcher on clist.
\r
932 * @return the contents of clistWatchElement or initialise it
\r
934 public ClientSessionFileWatcherElement getClientWatcherElement() {
\r
935 if (clistWatchElement == null) {
\r
936 clistWatchElement = new ClientSessionFileWatcherElement(clist, null);
\r
938 return clistWatchElement;
\r
941 public uk.ac.vamsas.client.simpleclient.SimpleSessionHandle getSessionUrn() {
\r
942 return sessionHandle;
\r