package uk.ac.vamsas.client.simpleclient; import uk.ac.vamsas.client.*; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.Vector; /** * Handler for the clientsFile within a vamsas session thread. * @author jim */ public class ClientsFile extends ListFile { private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(ClientsFile.class); /** * number of my client in list - passed back when a client * is added to list, and used (if valid) for quickly * looking up presence of client handle in the list. */ private int syncnum = 1; public ClientsFile(File filelist) throws IOException { super(filelist); } /** * internal method for getting clientList - ensures a lock has been made but * does not release it. * * @return list of clients */ private ClientHandle[] retrieveClientHandles() { if (lockFile()) { try { ClientHandle[] clients=null; if (fileLock.length()>0) { ObjectInputStream is = new ObjectInputStream(fileLock.getBufferedInputStream(true)); Object o; o=is.readObject(); if (o!=null) { try { clients = (ClientHandle[]) o; } catch (Exception e) { System.err.println("Garbage in the clientHandle list "+this.sessionFile); } } } return clients; } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(System.err); } catch (Exception e) { e.printStackTrace(System.err); } } return null; } /** * get the clientList from the file. May return null if lock failed! * @return clientList */ public ClientHandle[] retrieveClientList() { if (lockFile()) { ClientHandle[] clients = retrieveClientHandles(); 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 ClientHandle[] retrieveClientList(Lock extantlock) { if (lockFile(extantlock)) { ClientHandle[] clients = retrieveClientHandles(); 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 addClient(ClientHandle me, Lock extantLock) { return addClient(me, true, extantLock); } /** * adds clientHandle me to the clientList under an existing lock. * @param me - clientHandle * @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 addClient(ClientHandle me, boolean disambig, Lock extantLock) { if (lockFile(extantLock)) { syncnum = addClient(me, disambig); unlockFile(); return syncnum; } return 0; } /** * adds the ClientHandle to the list - if it is not unique, then the * ClientHandle object is modified to make it unique in the list. returns the * clientNumber for the client in the session. * * @param me * @return */ public int addClient(ClientHandle me) { syncnum = addClient(me, true); unlockFile(); return syncnum; } /** * removes 'me' from the session ClientList without complaint if 'me' isn't in the clientList already. * @param me client handle to be removed * @param clientlock existing lock passed from watcher. */ public void removeClient(ClientHandle me, Lock clientlock) { int mynum=-1; if (lockFile(clientlock)) { ClientHandle[] clients = retrieveClientHandles(); if (clients != null) { if ((syncnum<=0 || syncnum>clients.length) || clients[syncnum-1]!=me) { for (int i = 0, j = clients.length; i < j; i++) if (clients[i].equals(me)) { mynum=i; break; } } else { mynum=syncnum-1; } if (mynum>-1) { ClientHandle[] newlist = new ClientHandle[clients.length - 1]; for (int k=0,i = 0, j = clients.length; i < j; i++) if (i!=mynum) newlist[k++] = clients[i]; if (!putClientList(newlist)) throw new Error("Failed to write new clientList!"); // failed to put the clientList to disk. } } unlockFile(); } else { throw new Error("Couldn't get lock for "+((sessionFile==null) ? "Unitialised sessionFile in ClientsFile" : sessionFile.getAbsolutePath())); } } /** * Adds a ClientHandle to the ClientList file - optionally disambiguating * the ClientHandle (modifes the URN). * Note: Caller is left to release the lock on the ClientList. * @param me * @param disambiguate - * flag indicating if the URN for me should be disambiguated to * differentiate between sessions. * @return index of clientHandle in new list, or -1-position of existing * clientHandle (if disambiguate is true) */ protected int addClient(ClientHandle me, boolean disambiguate) { int newclient = 0; int tries=5; while (tries-->0 && !lockFile()) try { Thread.sleep(1); } catch (Exception e){}; if (lockFile()) { ClientHandle[] clients = retrieveClientHandles(); if (me.getClientUrn()==null) { // TODO: move this into ClientUrn as a standard form method. me.setClientUrn("vamsas://"+me.getClientName()+":"+me.getVersion()+"/"); } if (clients == null) { clients = new ClientHandle[1]; clients[0] = me; newclient = 1; } else { int k = 0; for (int i = 0, j = clients.length; i < j; i++) { if (clients[i].equals(me)) { if (disambiguate) { while (clients[i].equals(me)) { 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; ClientHandle[] newlist = new ClientHandle[clients.length + 1]; for (i = 0, j = clients.length; i < j; i++) newlist[i] = clients[i]; newlist[j] = me; clients = newlist; newclient = j+1; } if (!putClientList(clients)) return 0; // failed to put the clientList to disk. } return newclient; } /** * 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; /** * safely writes clients array to the file referred to by sessionFile. * * @param clients * @return true if successful write. Throws Errors otherwise. */ protected boolean putClientList(ClientHandle[] clients) { if (lockFile()) { File templist=null; if (backup) { templist = backupSessionFile(); } if (!backup || (templist != null)) { int retries=3; while (retries-->0) { try { ObjectOutputStream os = new ObjectOutputStream(fileLock.getBufferedOutputStream(true)); log.debug("About to write "+clients.length+" clientHandles to output stream."); os.writeObject(clients); os.close(); // All done - remove the backup. if (backup) templist.delete(); templist = null; retries=-1; } catch (Exception e) { System.err .println("Serious - problems writing to sessionFile."); if (retries>0 && templist != null) { System.err.println("Recovering from Backup in " + templist.getAbsolutePath()); templist.renameTo(fileLock.target); } e.printStackTrace(System.err); } } if (retries>-2) { System.err .println("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: " + ((sessionFile == null) ? "Unitialized ClientsFile" : " failed to get lock on " + sessionFile.getAbsolutePath())); } // successful! return true; } public void clearList() { if (lockFile()) { try { FileOutputStream fout = fileLock.getFileOutputStream(true); fout.flush(); fout.close(); } catch (Exception e) { throw new Error("Problems trying to clear clientlist!",e); } } } }