import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+
+import java.nio.channels.OverlappingFileLockException;
import java.util.Hashtable;
import java.util.Vector;
protected ClientHandle client = null;
protected EventGeneratorThread evgen = null;
protected ClientDocument cdocument = null;
+
+
+
+
+ private java.nio.channels.FileLock activeClientFilelock = null;
+
+ private FileChannel activeClientFileChannel = null;
+ private File clientlockFile = null;
+
/**
* object hash table that persists in each client holding vorbaIds and hash values after a document write
*/
// more complex if data is already present in document. Could have a 'clearSession' method, too, or dump and overwrite instead.
log.error("importDocument is not yet implemented for a SimpleClient Session.");
- /* try {
+ /*try {
this._session.setVamsasDocument(location);
} catch (IOException e) {
log.error("importDocument failed.");
pickmanager = new SimplePickManager(new uk.ac.vamsas.client.picking.SocketManager());
}
}
+
+
+ protected void releaseActiveClientFile() throws IOException
+ {
+
+ log.debug("Releasing active client file");
+ if( activeClientFilelock != null)
+ // Release the lock
+ activeClientFilelock.release();
+
+ if (activeClientFileChannel != null)
+ // Close the file
+ activeClientFileChannel.close();
+ if (this.clientlockFile != null)
+ {
+ this.clientlockFile.delete();
+ log.debug("deleted active client lock file");
+ }
+
+ }
+
+ protected void createActiveClientFile() throws IOException
+ {
+ if(this.clientlockFile != null )return;
+ log.debug("createActiveClientFile");
+ //create, if need, subdirectory to contain client files
+ File clientlockFileDir = new File ( this.get_session().sessionDir, this.get_session().clientFileDirectory);
+ if( !clientlockFileDir.exists())
+ {//the directory does not exist, create it
+ if (! clientlockFileDir.mkdirs())
+ {
+ throw new IOException("Failed to create sub directory to session directory for client lock files'"+clientlockFileDir.getAbsolutePath()+"'");
+ }
+ }
+ else
+ {
+ if (!(clientlockFileDir.isDirectory() && clientlockFileDir.canWrite()))
+ {
+ throw new IOException("Directory for client lock files is not a directory or is not accessibl: '"+clientlockFileDir.getAbsolutePath()+"'");
+ }
+ }
+ this.clientlockFile = new File (clientlockFileDir, this.getClientHandle().getClientUrn().replaceAll(File.separator, "").replaceAll(":", "").replaceAll(";", ""));
+
+ log.debug("Creating active client lock file "+ this.clientlockFile.getAbsolutePath());
+ if (clientlockFile.exists())
+ {//should not happen, file should be deleted when the application closes or when a crashed application has been detected
+ log.error("client lock file already exits");
+ }
+ try {
+ //create the empty file
+ if(! clientlockFile.createNewFile())
+ {
+ log.error("Unable to create active client lock file");
+
+ return;
+ }
+ else
+ log.debug("file created");
+ // Get a file channel for the file
+ FileChannel channel = new RandomAccessFile(clientlockFile, "rw").getChannel();
+
+ // Use the file channel to create a lock on the file.
+ // This method blocks until it can retrieve the lock.
+ java.nio.channels.FileLock activeClientFilelock ;// = channel.lock();
+
+ // Try acquiring the lock without blocking. This method returns
+ // null or throws an exception if the file is already locked.
+ try
+ {
+ activeClientFilelock = channel.tryLock();
+ log.debug("Got lock");
+ }
+ catch (OverlappingFileLockException e)
+ {
+ // File is already locked in this thread or virtual machine
+ log.error("Oups the file is already locked",e);
+
+ }
+
+ // Release the lock
+ /* lock.release();
+
+ // Close the file
+ channel.close();*/
+ } catch (Exception e) {
+ log.error("Error during lock file creation",e);
+ }
+
+
+
+ }
+
+ /**
+ * @return the clientlockFile
+ */
+ protected File getClientlockFile() {
+ return clientlockFile;
+ }
}
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
/**
* 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 = 1100 ;
+ private final int watchCycleCountBeforeLastClient = 1220 ;
/**
* time between checking
*/
public int WATCH_SLEEP=30;
- //protected String clientFileDirectory = "clients";
+ protected String clientFileDirectory = "clients";
/**
* called to clear update flag after a successful offline storage event
* add the client to the client list file
* @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 ");
log.debug("Added.");
log.debug("Register Client as Active.");
- /* try {
+ 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();
+
}
}
private class AddClientWatchCallBack implements WatcherCallBack
{
- private IClient client ;
+ 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 (IClient client)
+ protected AddClientWatchCallBack (SimpleClient client)
{
this.client = client;
}
boolean isWatchEnable = watcher.isWatchEnabled();
if (lock== null)//no update on the list
return isWatchEnable;
- // log.debug("change on the client list ");
+ log.debug("change on the client list ");
if (client != null)
{
log.debug("client is in the list");
}
- //log.debug("isWatchEnable "+isWatchEnable);
+ log.debug("isWatchEnable "+isWatchEnable);
return isWatchEnable;
}
}
//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().setHandler(new RemoveClientWatchCallBack (client));
getClientWatcherElement().setTimeoutBeforeLastCycle(this.watchCycleCountBeforeLastClient);
log.info("remove client from list");
clist.clearList();
- log.info("client list cleared");
+ log.info("list cleared");
if (cwe!=null) {
cwe.enableWatch();
- /* try {
- log.debug("Releasing active client file");
- client.releaseActiveClientFile();
- } catch (IOException e) {
- log.error("error during active file client release");
- }*/
+ 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);
+
+ if ( this.isLastActiveClient(client))
+ {
+ log.debug("Last active client: closing session");
+ log.info("Closing session");
+ getSessionManager().removeSession(client.getSessionHandle());
+ }
+
+ try
+ {
+ log.debug("Releasing active client file");
+ client.releaseActiveClientFile();
+ }
+ catch (IOException e)
+ {
+ log.error("error during active file client release");
+ }
}
}
+
+ private boolean isLastActiveClient(SimpleClient client)
+ {
+ log.debug("Testing if current client is the last one.");
+ 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.getClientlockFile().equals(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 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);
+
boolean islastClient = true;
if (manualCheckOfClientCount)
{
{
//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");
+ closeSession(client);
}
}
else
}
}
+ /**
+ * 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");
+ this.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
*/