From: pmarguerite Date: Thu, 14 Dec 2006 16:19:27 +0000 (+0000) Subject: added SimpleClientFactory implementation and new class SessionsFile to deal with... X-Git-Tag: Release_0.2~262 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=4dc56e102f8959071f009385d75a89967e68a4f6;p=vamsas.git added SimpleClientFactory implementation and new class SessionsFile to deal with the list of active sessions git-svn-id: https://svn.lifesci.dundee.ac.uk/svn/repository/trunk@258 be28352e-c001-0410-b1a7-c7978e42abec --- diff --git a/src/org/vamsas/client/simpleclient/SessionsFile.java b/src/org/vamsas/client/simpleclient/SessionsFile.java new file mode 100644 index 0000000..eb80f45 --- /dev/null +++ b/src/org/vamsas/client/simpleclient/SessionsFile.java @@ -0,0 +1,342 @@ +/* EMBL - The European Bioinformatics institute +* MSD Group +* VAMSAS Project +* +* Copyright (c) 2005-2006 Thr European Bioinformatics Institute. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* +* 3. The name MSD must not be used to endorse or promote products +* derived from this software without prior written permission. For +* written permission, please contact msd-help@ebi.ac.uk +* +* 4. Products derived from this software may not be called "MSD" +* nor may "MSD" appear in their names without prior written +* permission of the MSD developers. +* +* 5. Redistributions of any form whatsoever must retain the following +* acknowledgment: +* "This product includes software developed by MSD +* (http://www.ebi.ac.uk/)" +* +* THIS SOFTWARE IS PROVIDED BY THE MSD GROUP ``AS IS'' AND ANY +* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ENSEMBL GROUP OR +* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The European Bioinformatics Institute may publish revised and/or new +* versions of this license with new releases of VAMSAS software. +*============================================================================== +* +* @author Pierre MARGUERITE +* +* Dec 13, 2006 - VamsasClientV4 +* +*/ + +package org.vamsas.client.simpleclient; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.vamsas.client.SessionHandle; + +/** + * @author Pierre MARGUERITE + * + * + */ +public class SessionsFile extends ListFile { + + private static Log log = LogFactory.getLog(SessionsFile.class); + /** + * when set true - get FileNotFoundExceptions on WinXP when writing to locked stream after the backup has been made (via the backupFile method) + */ + boolean backup=false; + /** + * number of my session in list - passed back when a session + * is added to list, and used (if valid) for quickly + * looking up presence of session handle in the list. + */ + private int syncnum = 1; + /** + * @param file + */ + public SessionsFile(File file) throws java.io.IOException { + super(file); + } + + + /** + * internal method for getting sessionsList - ensures a lock has been made but + * does not release it. + * + * @return list of clients + */ + private SessionHandle[] retrieveSessionHandles() { + if (lockFile()) { + try { + SessionHandle[] clients=null; + if (this.fileLock.length()>0) { + + ObjectInputStream is = new ObjectInputStream(this.fileLock.getBufferedInputStream(true)); + Object o; + o=is.readObject(); + if (o!=null) { + try { + clients = (SessionHandle[]) o; + } + catch (Exception e) { + log.error("Garbage in the clientHandle list "+this.sessionFile,e); + } + } + } + return clients; + } catch (FileNotFoundException e) { + // e.printStackTrace(System.err); + log.error(e); + } catch (Exception e) { + log.error(e); + //e.printStackTrace(System.err); + } + } + return null; + } + + /** + * get the SessionsList from the file. May return null if lock failed! + * @return sessionsList + */ + public SessionHandle[] retrieveSessionsList() { + if (lockFile()) { + SessionHandle[] clients = retrieveSessionHandles(); + unlockFile(); + return clients; + } + return null; + } + + /** + * get list from the locked ClientList. + * @param extantlock + * @return clientList or null if lock failed (or file was empty) + */ + public SessionHandle[] retrieveSessionsList(Lock extantlock) { + if (lockFile(extantlock)) { + SessionHandle[] clients = retrieveSessionHandles(); + unlockFile(); + return clients; + } + return null; + } + + + /** + * adds clientHandle me to the clientList under an existing lock extantLock. + * @param me + * @param extantLock + * @return client index in list or 0 if lock was invalid or addClient operation failed. + */ + public int addSession(SessionHandle me, Lock extantLock) { + return addSession(me, true, extantLock); + } + + /** + * adds SessionsHandle me to the sessionsList under an existing lock. + * @param me - sessionsHandle + * @param disambig - if true then add will fail if an identical clientHandle already exists + * @param extantLock - existing lock + * @return client index in list or 0 if addClient (or the lock) failed. + */ + + public int addSession(SessionHandle session, boolean disambig, Lock extantLock) { + if (lockFile(extantLock)) { + this.syncnum = addSession(session, disambig); + unlockFile(); + return this.syncnum; + } + return 0; + } + + /** + * removes the current session from the SessionsList without complaint if the session isn't in the sessionsList already. + * @param me client handle to be removed + * @param clientlock existing lock passed from watcher. + */ + public void removeSession(SessionHandle session, Lock clientlock) { + int mynum =-1; + if (lockFile(clientlock)) { + SessionHandle[] sessions = retrieveSessionHandles(); + if (sessions != null) { + if ((this.syncnum<=0 || this.syncnum>sessions.length) || sessions[this.syncnum-1]!=session) { + for (int i = 0, j = sessions.length; i < j; i++) + if (sessions[i].equals(session)) { + mynum=i; + break; + } + } else { + mynum=this.syncnum-1; + } + if (mynum>-1) { + SessionHandle[] newlist = new SessionHandle[sessions.length - 1]; + for (int k=0,i = 0, j = sessions.length; i < j; i++) + if (i!=mynum) + newlist[k++] = sessions[i]; + if (!putSessionsList(newlist)) + throw new Error("Failed to write new sessionsList!"); // failed to put the sessionList to disk. + } + } + unlockFile(); + } else { + throw new Error("Couldn't get lock for "+((sessionFile==null) ? "Unitialised sessionFile in SessionsFile" : this.sessionFile.getAbsolutePath())); + } + } + /** + * Adds a SessionHandle to the SessionList file - optionally disambiguating + * the SessionHandle (modifes the URN). + * Note: Caller is left to release the lock on the SessionList. + * @param me + * @param disambiguate - + * flag indicating if the URN for me should be disambiguated to + * differentiate between sessions. + * @return index of sessionHandle in new list, or -1-position of existing + * sessionHandle (if disambiguate is true) + */ + protected int addSession(SessionHandle session, boolean disambiguate) { + int newsession = 0; + int tries=5; + while (tries-->0 && !lockFile()) + try { Thread.sleep(1); } catch (Exception e){}; + if (lockFile()) { + SessionHandle[] sessions = retrieveSessionHandles(); + + if (sessions == null) { + sessions = new SessionHandle[1]; + sessions[0] = session; + newsession = 1; + } else { + int k = 0; + for (int i = 0, j = sessions.length; i < j; i++) { + if ( sessions[i].equals(session)) { + if (disambiguate) { + while (sessions[i].equals(session)) { + // me.setClientUrn(me.getClientUrn() + k++); // TODO: make a better + // disambiguation of + // urn. + } + } else { + // will not write the ambiguous clientHandle to disk, just return + // its index. + return -1 - i; + } + } + } + int i, j; + SessionHandle[] newlist = new SessionHandle[sessions.length + 1]; + for (i = 0, j = sessions.length; i < j; i++) + newlist[i] = sessions[i]; + newlist[j] = session; + sessions = newlist; + newsession = j+1; + } + if (!putSessionsList(sessions)) + return 0; // failed to put the clientList to disk. + } + return newsession; + } + + + /** + * safely writes sessions array to the file referred to by sessionFile. + * + * @param clients + * @return true if successful write. Throws Errors otherwise. + */ + protected boolean putSessionsList(SessionHandle[] clients) { + if (lockFile()) { + File templist=null; + if (!this.backup || (templist = backupSessionFile()) != null) { + int retries=3; + while (retries-->0) { + try { + ObjectOutputStream os = + new ObjectOutputStream(this.fileLock.getBufferedOutputStream(true)); + log.debug("About to write "+clients.length+" sessionHandles to output stream."); + os.writeObject(clients); + os.close(); + // All done - remove the backup. + if (this.backup) + templist.delete(); + templist = null; + retries=-1; + } catch (Exception e) { + // System.err + //.println("Serious - problems writing to sessionFile."); + log.error("Serious - problems writing to sessionFile.",e); + if (retries>0 && templist != null) { + // System.err.println("Recovering from Backup in " + // + templist.getAbsolutePath()); + log.error("Recovering from Backup in "+ templist.getAbsolutePath()); + templist.renameTo(this.fileLock.target); + } + //e.printStackTrace(System.err); + log.error(e); + } + } + if (retries>-2) { + // System.err + // .println("Serious - problems writing to sessionFile. Giving Up."); + log.error("Serious - problems writing to sessionFile. Giving Up."); + return false; + } + } else { + throw new Error( + "Couldn't create backup of the clientList before writing to it!"); + } + } else { + throw new Error("Could not lock the clientList: " + + ((this.sessionFile == null) ? "Unitialized ClientsFile" + : " failed to get lock on " + this.sessionFile.getAbsolutePath())); + } + // successful! + return true; + } + + public void clearList() { + if (lockFile()) { + try { + FileOutputStream fout = this.fileLock.getFileOutputStream(true); + fout.flush(); + fout.close(); + } catch (Exception e) { + throw new Error("Problems trying to clear clientlist!",e); + + } + } + + } +} diff --git a/src/org/vamsas/client/simpleclient/SimpleClientFactory.java b/src/org/vamsas/client/simpleclient/SimpleClientFactory.java index 640ad78..6f5509a 100644 --- a/src/org/vamsas/client/simpleclient/SimpleClientFactory.java +++ b/src/org/vamsas/client/simpleclient/SimpleClientFactory.java @@ -1,135 +1,262 @@ +/* +* VAMSAS Project +* + +* +* Dec 13, 2006 +* +*/ + package org.vamsas.client.simpleclient; import java.io.File; + import java.io.IOException; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.vamsas.client.ClientHandle; import org.vamsas.client.IClient; import org.vamsas.client.IClientFactory; +import org.vamsas.client.InvalidSessionUrnException; import org.vamsas.client.NoDefaultSessionException; +import org.vamsas.client.SessionHandle; import org.vamsas.client.UserHandle; /** - * TODO document type SimpleClientFactory - * @author jimp - * + * + * */ public class SimpleClientFactory implements IClientFactory { + private static Log log = LogFactory.getLog(SimpleClientFactory.class); - File sessionArena; + private File sessionArena = null; + + private String vamsasSubdirectoryName = ".vamsas"; + + private SessionsFile sessionFile = null; + private static final String SESSION_LIST="sessions.obj"; + + //private String[] currentlyAvailableDessions = null; /** * default constructor - called by CreateClientFactory only. * + *Inits the sessionarena to the directory .vamsas of the user home directory. + * */ - public SimpleClientFactory() { - sessionArena = null; + public SimpleClientFactory() throws IOException + { + // sessionArena + + //retrieves user home directory + String userHomeDirectory = System.getProperty("user.home"); + if (userHomeDirectory == null || userHomeDirectory.length()<1) + { + new IOException("Unable to detect user home directory"); + } + String sessionArenaPath = userHomeDirectory.concat(File.separator.concat(this.vamsasSubdirectoryName)); + + this.initSessionArena(sessionArenaPath); + this.initFactoryObjects(); } + + /** * Create a client factory that works with sessions at the given * path. - * @param path + * @param path path to directory called session arena, where will be created session directories and session files. */ - public SimpleClientFactory(String path) throws IOException { + public SimpleClientFactory(String path) throws IOException + { + this.initSessionArena(path); + } + /** + * Inits sessionArena to a given path. + * checks if path is valid. + * + * @param path path to a directory to use + * @throws IOException if the path is incorrect + */ + private void initSessionArena (String path) throws IOException + { // Check path is valid and read/writeable. - File newarena = new File(path); - if (newarena.isDirectory() && newarena.canRead() && newarena.canWrite()) { - sessionArena = newarena; - } else { - sessionArena = null; - throw(new IOException("Cannot read and write to a directory called "+path)); + File arenaFile = new File (path); + if (!arenaFile.exists()) + { + if (! arenaFile.mkdirs()) + { + this.sessionArena = null; + throw(new IOException("Unable to create a directory called "+path)); + } } + if (arenaFile.exists() && arenaFile.isDirectory() && arenaFile.canRead() && arenaFile.canWrite()) + { + this.sessionArena = arenaFile; + } + else + { + this.sessionArena = null; + throw(new IOException("Cannot read and write to a directory called "+path)); + } } - /* (non-Javadoc) + /** + * construct SessionFile objects and watchers for each + */ + private void initFactoryObjects() throws IOException { + if (this.sessionFile!=null ) + throw new IOException("initFactoryObjects called for initialised ClientFactory object."); + this.sessionFile = new SessionsFile(new File(this.sessionArena,SESSION_LIST)); + + } + /** * @see org.vamsas.client.IClientFactory#getCurrentSessions() */ - public String[] getCurrentSessions() { - // TODO look in the arena and enumerate session handles for return. - return new String[] {}; + public String[] getCurrentSessions() + { + String[] sessions = null; + if (this.sessionFile!=null ) + { + SessionHandle[] sessionHandles = this.sessionFile.retrieveSessionsList(); + if (sessionHandles != null) + { + sessions = new String[sessionHandles.length]; + for (int i = sessionHandles.length -1; i > 0; i--) + { + SessionHandle sessionHandle = sessionHandles[i]; + sessions [i] = sessionHandle.getSessionUrn(); + } + } + } + return sessions; } - /* (non-Javadoc) + + + private void discoverSession() + { + + } + + /** * @see org.vamsas.client.IClientFactory#getIClient(org.vamsas.client.ClientHandle) + * + * Creates a IClient object, using default UserHandle with system variables:"user.name" or "USERNAME")), + "host.name" or "HOSTNAME" */ - public IClient getIClient(ClientHandle applicationHandle) throws NoDefaultSessionException { - // create a new session - // register new ClientHandle in session - // create SimpleClient instance - return null; + public IClient getIClient(ClientHandle applicationHandle) + throws NoDefaultSessionException { + + return this.getIClient(applicationHandle, (UserHandle) null); } - /* (non-Javadoc) + /** * @see org.vamsas.client.IClientFactory#getIClient(org.vamsas.client.ClientHandle, java.lang.String) */ public IClient getIClient(ClientHandle applicationHandle, String sessionUrn) { - // locate session from Urn - // check that clientHandle is unique (with default user) - if not update the clientHandle urn to make it unique. - // wait for lock and attach to session - // create SimpleClient instance + // TODO Auto-generated method stub return null; } - /* (non-Javadoc) + /** * @see org.vamsas.client.IClientFactory#getIClient(org.vamsas.client.ClientHandle, org.vamsas.client.UserHandle, java.lang.String) */ public IClient getIClient(ClientHandle applicationHandle, UserHandle userId, String sessionUrn) { - // locate session from Urn - // check Uniqueness of user + ClientHandle in the session. Update clientHandle urn accordingly. - // wait for lock, attach to session - // create client instance + // TODO Auto-generated method stub return null; } - /* (non-Javadoc) + /** * @see org.vamsas.client.IClientFactory#getIClient(org.vamsas.client.ClientHandle, org.vamsas.client.UserHandle) */ - public IClient getIClient(ClientHandle applicationHandle, UserHandle userId) throws NoDefaultSessionException { - // create new session - // register SimpleClient and UserHandles in session - // create client instance - return null; + public IClient getIClient(ClientHandle applicationHandle, UserHandle userId) + throws NoDefaultSessionException { + SimpleClient client = null; + if (this.sessionArena==null) + throw new Error("Improperly initialised SimpleClientFactory object - null sessionArena."); + + ClientHandle clientHandle =applicationHandle; + //create default clientHandle with "SimpleVamsasClientApp","0.1", + if (clientHandle == null) + clientHandle = new ClientHandle("SimpleVamsasClientApp","0.1"); + + //check if any available session(s) + String[] availableSessions = this.getCurrentSessions(); + if (availableSessions != null) + {//there are available sessions + if (availableSessions.length>1) + {//more than one session if available... can not choose + + //represents list of session as String + StringBuffer sessionURNs = new StringBuffer(""); + for (int i = 0; i< availableSessions.length ; i++) + { + sessionURNs.append(availableSessions[i]+" "); + } + throw new NoDefaultSessionException("Several sessions available, please pick one: "+sessionURNs); + } + + //check if only one session available. if yes, open it + if (availableSessions.length == 1) + { + //only one session available, open it. + return this.getIClient(clientHandle, availableSessions[0]); + } + } + //no session available - create a new one + + + try + { + //create sessionDirectory + File sessdir = File.createTempFile("sess", ".simpleclient", this.sessionArena); + log.debug("Creating new session directory"); + if (!(sessdir.delete() && sessdir.mkdir())) + throw new IOException("Could not make session directory "+sessdir); + //create session + VamsasSession vamsasSession = new VamsasSession(sessdir); + + this.getSessionFile().addSession(new SessionHandle(new SessionUrn(vamsasSession).getSessionUrn()), false); + if (userId == null) + { + //create a default userHandle + //with current OS user and hostname + userId = new UserHandle(System.getProperty("user.name", System.getProperty("USERNAME","Joe Doe")), + System.getProperty("host.name",System.getProperty("HOSTNAME", "Unknown") ));// clientName, clientVersion, sessionPath); + } + + + //create simple client + client = new SimpleClient(userId, clientHandle, vamsasSession); + } + catch (IOException e) + { + log.error("error while creating new IClient",e); + } + catch (InvalidSessionUrnException e) + { + log.error("Unable to create new IClient. The session urn is incorrect ",e); + } + + return client; } + + /** - * make a new vamsas session from the data in the archive vamsasdocument - * @param applicationHandle - * @param userId - * @param vamsasdocument - * @return + * @return the sessionFile */ - public IClient openSession(ClientHandle applicationHandle, UserHandle userId, ArchiveUrn vamsasdocument) throws IOException { - // verify applicationHandle and userId - // TODO: verify applicationHandle and userId - // verify vamsasdocument is a valid document. - File vamdoc = vamsasdocument.asFile(); - log.debug("Starting new session with data from "+vamsasdocument.getSessionUrn()+"(File is : "+vamdoc+")"); - VamsasArchiveReader archive = new VamsasArchiveReader(vamdoc); - // TODO: a real validity test. The below just checks it can be read. - if (!archive.isValid()) - throw new IOException(vamsasdocument.getSessionUrn()+" is not a valid vamsasDocument archive."); - // create new session directory - if (sessionArena==null) - throw new Error("Improperly initialised SimpleClientFactory object - null sessionArena."); - File sessdir = File.createTempFile("sess", ".simpleclient", sessionArena); - if (!(sessdir.delete() && sessdir.mkdir())) - throw new IOException("Could not make session directory "+sessdir); - VamsasSession sess = new VamsasSession(sessdir); - // copy document into session directory - sess.setVamsasDocument(vamsasdocument.asFile()); - // create client instance and return. - SimpleClient client=null; - try { - client = new SimpleClient(userId, applicationHandle, sess); - } catch (Exception e) { - log.error("Couldn't make a new SimpleClient instance.",e); - throw new IOException(e.getMessage()); + private SessionsFile getSessionFile() throws IOException + { + if (this.sessionFile == null) + { + this.sessionFile = new SessionsFile( new File (this.sessionArena, SESSION_LIST)); + } + return this.sessionFile; } - return client; - } + - public static void main(String[] args) { - } + }