From 30801ef84cde704404d052f00c70ae129820bb0b Mon Sep 17 00:00:00 2001 From: jprocter Date: Fri, 1 Sep 2006 17:02:21 +0000 Subject: [PATCH] updated test cases for spring 2006 schema, and major debug of locking system git-svn-id: https://svn.lifesci.dundee.ac.uk/svn/repository/trunk@234 be28352e-c001-0410-b1a7-c7978e42abec --- .../vamsas/client/simpleclient/ClientsFile.java | 63 ++++++++--- src/org/vamsas/client/simpleclient/FileLock.java | 116 ++++++++++++++++---- .../vamsas/client/simpleclient/FileWatcher.java | 2 +- src/org/vamsas/client/simpleclient/Lock.java | 11 ++ .../vamsas/client/simpleclient/LockFactory.java | 26 ++++- .../simpleclient/LockedFileOutputStream.java | 100 ++++++++++++++++- src/org/vamsas/client/simpleclient/NativeLock.java | 54 ++++++++- .../vamsas/client/simpleclient/SessionFile.java | 109 +++++++++++------- .../client/simpleclient/SimpleClientAppdata.java | 2 +- .../vamsas/client/simpleclient/VamsasArchive.java | 27 +++-- .../client/simpleclient/VamsasArchiveReader.java | 26 +++-- .../vamsas/client/simpleclient/VamsasSession.java | 6 +- .../vamsas/test/simpleclient/ArchiveClient.java | 28 +++-- src/org/vamsas/test/simpleclient/ClientDoc.java | 54 ++++++++- .../vamsas/test/simpleclient/ClientsFileTest.java | 9 +- .../vamsas/test/simpleclient/VamsasArchive.java | 14 ++- 16 files changed, 508 insertions(+), 139 deletions(-) diff --git a/src/org/vamsas/client/simpleclient/ClientsFile.java b/src/org/vamsas/client/simpleclient/ClientsFile.java index cab03cd..96fa871 100644 --- a/src/org/vamsas/client/simpleclient/ClientsFile.java +++ b/src/org/vamsas/client/simpleclient/ClientsFile.java @@ -21,6 +21,7 @@ import java.util.Vector; * @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 @@ -42,7 +43,7 @@ public class ClientsFile extends ListFile { if (lockFile()) { try { ClientHandle[] clients=null; - if (this.fileLock.rafile.length()>0) { + if (fileLock.length()>0) { ObjectInputStream is = new ObjectInputStream(fileLock.getBufferedInputStream(true)); Object o; @@ -222,7 +223,10 @@ public class ClientsFile extends ListFile { } 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. * @@ -231,28 +235,37 @@ public class ClientsFile extends ListFile { */ protected boolean putClientList(ClientHandle[] clients) { if (lockFile()) { - File templist = backupSessionFile(); - if (templist != null) { - try { - // fileLock.rafile.setLength(0); - ObjectOutputStream os = - new ObjectOutputStream(fileLock.getBufferedOutputStream(true)); - -// new BufferedOutputStream(new FileOutputStream(this.sessionFile))); - // new BufferedOutputStream(new FileOutputStream(this.fileLock.rafile.getFD()))); - os.writeObject(clients); - os.close(); // close destroys lock! + File templist=null; + if (!backup || (templist = backupSessionFile()) != 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. - templist.delete(); + if (backup) + templist.delete(); templist = null; - } catch (Exception e) { - if (templist != null) { + retries=-1; + } catch (Exception e) { System.err - .println("Serious - problems writing to sessionFile. Backup in " - + templist.getAbsolutePath()); + .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!"); @@ -265,4 +278,18 @@ public class ClientsFile extends ListFile { // 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); + + } + } + + } } diff --git a/src/org/vamsas/client/simpleclient/FileLock.java b/src/org/vamsas/client/simpleclient/FileLock.java index 8cb6808..53688ac 100644 --- a/src/org/vamsas/client/simpleclient/FileLock.java +++ b/src/org/vamsas/client/simpleclient/FileLock.java @@ -7,41 +7,71 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; - +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +/** + * File based Locking mechanism to get around some bizarre limitations of JarEntry seeking. + * Abstract locks have a target file, to which access is controlled when a lock is held. Native locks on WindowsXP seem to conflict with Jar seek operations, so a file lock creates an advisory lock. + * Method: + * A lock file is created, if it doesn't already exist - the naming convention is TargetFile+suffixSeparator+_LockSuffix. + * A lock is obtained by locking the lock file with a native lock. The NativeLock is used for this. + * @author JimP + * + */ public class FileLock extends Lock { - File _lock = null; + private File _lock = null; protected static String _LockSuffix="lck"; + private NativeLock advisory=null; + /** + * ensure that the _lock file exists + * and create a lock + */ + private boolean ensureLockFile(boolean block) { + if (_lock==null) + return false; + if (advisory!=null && advisory.isLocked()) + return true; + try { + advisory=new NativeLock(_lock, block); + } catch (Exception e) { + log.fatal("Failed to create advisory lock file "+_lock,e); + throw new Error("Failed to create advisory lock file "+_lock); + } + return advisory.isLocked(); + } /** * call to clear up a filelock file after its been made * */ private void tidy() { - if (_lock!=null) { - _lock.delete(); + if (_lock!=null) { + if ( advisory!=null) + advisory.release(true); + advisory=null; _lock=null; } } /** * @param lockfile + * @param block true means thread blocks until FileLock is obtained. */ - public FileLock(File lockfile) { + public FileLock(File lockfile, boolean block) { super(lockfile); // try and get a lock. try { _lock = new File(lockfile.getParentFile(), lockfile.getName()+"."+_LockSuffix); - if (_lock.exists() || !_lock.createNewFile()) { - log.debug("Failed to get lock for "+lockfile+" using lockfile "+_lock); - _lock=null; + if (!ensureLockFile(block)) { + log.debug("Couldn't get lock on "+_lock); + tidy(); return; } - _lock.deleteOnExit(); // safe - all locks should be removed on finalization. // create target file ready to be written to if necessary. if (!lockfile.exists()) if (!lockfile.createNewFile()) { log.warn("Failed to create locked file "+lockfile); return; } - openRaFile(); + //openRaFile(); } catch (FileNotFoundException e) { // log.debug("FileLock failed with target="+lockfile+" and lockfile suffix of "+_LockSuffix); @@ -56,18 +86,22 @@ public class FileLock extends Lock { private boolean openRaFile() throws IOException { if (target==null) return false; - if (_lock==null || !_lock.exists()) + if (advisory==null || !advisory.isLocked()) return false; - if (rafile==null) + if (rafile==null || rafile.getFD()==null || !rafile.getFD().valid()) { rafile=new RandomAccessFile(target,"rw"); + } else { + if (log.isDebugEnabled()) + log.debug("Reusing existing RandomAccessFile on "+target); + } return (rafile.getChannel()!=null) && rafile.getChannel().isOpen(); } public boolean isLocked() { - if (_lock != null) { - if (_lock.exists()) + if (advisory != null) { + if (advisory.isLocked()) return true; - _lock=null; + advisory=null; if (log.isDebugEnabled()) log.debug("Lockfile "+_lock+" unexpectedly deleted ?"); } @@ -79,8 +113,10 @@ public class FileLock extends Lock { } public void release(boolean closeChannel) { - if (_lock==null) + if (!isLocked()) return; + if (log.isDebugEnabled()) + log.debug("Releasing advisory lock on "+target); if (closeChannel) { if (rafile!=null) try { @@ -94,8 +130,10 @@ public class FileLock extends Lock { } public FileInputStream getFileInputStream(boolean atStart) throws IOException { - if (!isLocked()) + if (!isLocked()) { + log.debug("Don't hold lock on "+target+" to get locked FileInputStream."); return null; + } openRaFile(); if (atStart) rafile.seek(0); @@ -104,27 +142,63 @@ public class FileLock extends Lock { public FileOutputStream getFileOutputStream(boolean clear) throws IOException { - if (!isLocked()) + if (!isLocked()) { + log.debug("Don't hold lock on "+target+" to get locked FileOutputStream."); return null; + } openRaFile(); - if (clear) + if (clear) { + rafile.seek(0); rafile.setLength(0); - else + } else rafile.seek(rafile.length()); return new LockedFileOutputStream(rafile.getFD()); } public BufferedOutputStream getBufferedOutputStream(boolean clear) throws IOException { + log.debug("Getting BufferedOutputStream (clear="+clear+")"); FileOutputStream fos = getFileOutputStream(clear); if (fos!=null) return new BufferedOutputStream(fos); return null; } + /* (non-Javadoc) + * @see org.vamsas.client.simpleclient.Lock#getLength() + */ + public long length() { + if (isLocked()) { + if (!target.exists()) { + try { + target.createNewFile(); + } catch (Exception e) { + log.error("Invalid lock:Failed to create target file "+target); + tidy(); + return -1; + } + return 0; + } + return target.length(); + } + return -1; + } protected void finalize() throws Throwable { release(true); // we explicitly lose the lock here. super.finalize(); } - + public RandomAccessFile getRaFile() throws IOException { + if (isLocked() && openRaFile()) { + return rafile; + } + log.debug("Failed to getRaFile on target "+target); + return null; + } + public FileChannel getRaChannel() throws IOException { + if (isLocked() && openRaFile()) { + return rafile.getChannel(); + } + log.debug("Failed to getRaChannel on target "+target); + return null; + } } diff --git a/src/org/vamsas/client/simpleclient/FileWatcher.java b/src/org/vamsas/client/simpleclient/FileWatcher.java index 29d5979..fe72381 100644 --- a/src/org/vamsas/client/simpleclient/FileWatcher.java +++ b/src/org/vamsas/client/simpleclient/FileWatcher.java @@ -42,7 +42,7 @@ public class FileWatcher { if (subjectLock!=null) { subjectLock.release(); } - subjectLock = LockFactory.getLock(subject); + subjectLock = LockFactory.tryLock(subject); if (subjectLock.isLocked()) { return false; } diff --git a/src/org/vamsas/client/simpleclient/Lock.java b/src/org/vamsas/client/simpleclient/Lock.java index 9dd472f..d3a5889 100644 --- a/src/org/vamsas/client/simpleclient/Lock.java +++ b/src/org/vamsas/client/simpleclient/Lock.java @@ -6,7 +6,11 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.io.RandomAccessFile; +import java.nio.channels.ByteChannel; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; import org.apache.commons.logging.LogFactory; @@ -82,4 +86,11 @@ public abstract class Lock { return new BufferedInputStream(fis); return null; } + /** + * safe lock target length() function. + * @return -1 for non-lockable target, otherwise target's file length + */ + public abstract long length(); + public abstract RandomAccessFile getRaFile() throws IOException; + public abstract FileChannel getRaChannel() throws IOException; } diff --git a/src/org/vamsas/client/simpleclient/LockFactory.java b/src/org/vamsas/client/simpleclient/LockFactory.java index 72cbe66..5fdde6c 100644 --- a/src/org/vamsas/client/simpleclient/LockFactory.java +++ b/src/org/vamsas/client/simpleclient/LockFactory.java @@ -1,5 +1,7 @@ package org.vamsas.client.simpleclient; +import java.io.File; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -20,15 +22,31 @@ public class LockFactory { log.warn("System property vamsas.locktype takes one of "+lt); log.warn("Defaulting to Locktype of "+locktypes[locktype]); } - } + } else + log.debug("Defaulting to Locktype of "+locktypes[locktype]); } - + /** + * lock target (blocks until lock is obtained) + * @param target + * @return lock + */ public static Lock getLock(java.io.File target) { + return getLock(target, true); + } + public static Lock getLock(java.io.File target, boolean block) { if (locktype==0) - return new FileLock(target); + return new FileLock(target, block); if (locktype==1) - return new NativeLock(target); + return new NativeLock(target, block); log.fatal("Implementation Error! No valid Locktype value"); return null; } + /** + * try to lock target + * @param target + * @return null if lock was not possible + */ + public static Lock tryLock(File target) { + return getLock(target, false); + } } diff --git a/src/org/vamsas/client/simpleclient/LockedFileOutputStream.java b/src/org/vamsas/client/simpleclient/LockedFileOutputStream.java index 92d9d0c..1d59c8a 100644 --- a/src/org/vamsas/client/simpleclient/LockedFileOutputStream.java +++ b/src/org/vamsas/client/simpleclient/LockedFileOutputStream.java @@ -8,19 +8,36 @@ import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; +import java.nio.channels.FileChannel; + +import org.apache.commons.logging.LogFactory; /** * @author Jim * */ public class LockedFileOutputStream extends FileOutputStream { - + private static org.apache.commons.logging.Log log = LogFactory.getLog(LockedFileOutputStream.class); + //FileOutputStream ostream=null; + boolean closed=true; + private void init() { + FileChannel ch = super.getChannel(); + if (ch!=null) { + try { closed = !ch.isOpen(); + } catch (Exception e) { + closed=true; + log.debug("Invalid LockedOutputStream marked closed.",e); + } + } + } /** * @param file * @throws FileNotFoundException */ public LockedFileOutputStream(File file) throws FileNotFoundException { - super(file); + super(file); // super(file); + init(); } /** @@ -31,6 +48,7 @@ public class LockedFileOutputStream extends FileOutputStream { public LockedFileOutputStream(File file, boolean append) throws FileNotFoundException { super(file, append); + init(); } /** @@ -38,6 +56,9 @@ public class LockedFileOutputStream extends FileOutputStream { */ public LockedFileOutputStream(FileDescriptor fdObj) { super(fdObj); + init(); + if (fdObj.valid()) + closed=false; } /** @@ -46,6 +67,7 @@ public class LockedFileOutputStream extends FileOutputStream { */ public LockedFileOutputStream(String name) throws FileNotFoundException { super(name); + init(); } /** @@ -56,14 +78,82 @@ public class LockedFileOutputStream extends FileOutputStream { public LockedFileOutputStream(String name, boolean append) throws FileNotFoundException { super(name, append); - // TODO Auto-generated constructor stub + init(); } /** * closes - actually just flushes the stream instead. */ public void close() throws IOException { - // TODO Auto-generated method stub - super.flush(); + if (!closed) { + super.flush(); + super.getChannel().force(true); + log.debug("Marking Lockedoutputstream closed."); + } else + throw new IOException("Close on already closed FileOutputStream."); + closed=true; + } + + + /** + * @throws IOException + * @see java.io.OutputStream#flush() + */ + public void flush() throws IOException { + if (!closed) + super.flush(); + else + throw new IOException("flush on closed FileOutputStream"); + } + + /** + * @return + * @see java.io.FileOutputStream#getChannel() + */ + public FileChannel getChannel() { + if (!closed) + return super.getChannel(); + else + return null; + } + + + + /** + * @param b + * @param off + * @param len + * @throws IOException + * @see java.io.FileOutputStream#write(byte[], int, int) + */ + public void write(byte[] b, int off, int len) throws IOException { + if (!closed) + super.write(b, off, len); + else + throw new IOException("write on Closed FileOutputStream"); + } + + /** + * @param b + * @throws IOException + * @see java.io.FileOutputStream#write(byte[]) + */ + public void write(byte[] b) throws IOException { + if (!closed) + super.write(b); + else + throw new IOException("write on Closed FileOutputStream"); + } + + /** + * @param b + * @throws IOException + * @see java.io.FileOutputStream#write(int) + */ + public void write(int b) throws IOException { + if (!closed) + super.write(b); + else + throw new IOException("write on Closed FileOutputStream"); } } diff --git a/src/org/vamsas/client/simpleclient/NativeLock.java b/src/org/vamsas/client/simpleclient/NativeLock.java index 96d9b6b..6a624f1 100644 --- a/src/org/vamsas/client/simpleclient/NativeLock.java +++ b/src/org/vamsas/client/simpleclient/NativeLock.java @@ -7,17 +7,25 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; import java.nio.channels.FileLock; +import java.nio.channels.ReadableByteChannel; +/** + * @author JimP + * + */ public class NativeLock extends Lock { protected FileLock lock = null; /** * @param lockfile + * @param block true means thread will block until a lock is obtained. */ - public NativeLock(File lockfile) { + public NativeLock(File lockfile, boolean block) { super(lockfile); // try and get a lock. lock = null; @@ -29,7 +37,11 @@ public class NativeLock extends Lock { return; } - lock = (rafile=new RandomAccessFile(lockfile,"rw")).getChannel().tryLock(); + rafile=new RandomAccessFile(lockfile,"rw"); + if (block) + lock = rafile.getChannel().lock(); + else + lock = rafile.getChannel().tryLock(); if (lock==null || !lock.isValid()) { // failed to get lock. Close the file channel log.debug("failed to get lock for "+lockfile); @@ -100,9 +112,11 @@ public class NativeLock extends Lock { public FileOutputStream getFileOutputStream(boolean clear) throws IOException { if (!isLocked()) return null; - if (clear) + if (clear) { + rafile.seek(0); rafile.setLength(0); - rafile.seek(rafile.length()); + } else + rafile.seek(rafile.length()); return new LockedFileOutputStream(rafile.getFD()); } @@ -112,7 +126,7 @@ public class NativeLock extends Lock { * @return */ public BufferedOutputStream getBufferedOutputStream(boolean clear) throws IOException { - FileOutputStream fos = getFileOutputStream(clear); + OutputStream fos = getFileOutputStream(clear); if (fos!=null) return new BufferedOutputStream(fos); return null; @@ -126,4 +140,34 @@ public class NativeLock extends Lock { super.finalize(); } + /* (non-Javadoc) + * @see org.vamsas.client.simpleclient.Lock#getLength() + */ + public long length() { + if (isLocked()){ + try { + return rafile.length(); + } catch (Exception e) { + log.debug("getLength exception:",e); + } + } + return -1; + } + + public RandomAccessFile getRaFile() throws IOException { + if (isLocked()) + return rafile; + else + log.debug("Failed to getRaFile on "+target); + return null; + } + + public FileChannel getRaChannel() throws IOException { + if (isLocked()) + return rafile.getChannel(); + else + log.debug("Failed to getRaChannel on "+target); + return null; + } + } diff --git a/src/org/vamsas/client/simpleclient/SessionFile.java b/src/org/vamsas/client/simpleclient/SessionFile.java index bd2dc44..c9f9e67 100644 --- a/src/org/vamsas/client/simpleclient/SessionFile.java +++ b/src/org/vamsas/client/simpleclient/SessionFile.java @@ -7,6 +7,8 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.ReadableByteChannel; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -32,23 +34,11 @@ public class SessionFile { fileLock.release();// tidy up invalid lock fileLock=null; } - if (extantlock!=null) + if (extantlock!=null && extantlock.isLocked()) fileLock=extantlock; // THIS IS BROKEN - lockFile then nulls the lock. return lockFile(); } - - /** - * Get a lock for the SessionFile - * - * @return true if lock was made - */ - protected boolean lockFile() { - if (fileLock != null) - if (fileLock.isLocked()) - return true; - else - // lock failed for some reason. - fileLock.release(); + private boolean ensureSessionFile() { if (sessionFile != null) { if (!sessionFile.exists()) { // create new file @@ -62,27 +52,46 @@ public class SessionFile { return false; } } - // TODO: see if we need to loop-wait for locks or they just block until - // lock is made... - long tries=500; - do { - tries--; - if (fileLock!=null && !fileLock.isLocked()) { - fileLock.release(); - try { Thread.sleep(5); } catch (Exception e) {}; - } - fileLock = LockFactory.getLock(sessionFile); // TODO: wait around if we can't get the lock. - } while (tries>0 && !fileLock.isLocked()); - if (!fileLock.isLocked()) - log.error("Failed to get lock for "+sessionFile); - // fileLock = new Lock(sessionFile); - return fileLock.isLocked(); - } else - log.error("lockFile called for non-initialised SessionFile!"); - - // no lock possible + return true; + } + log.error("ensureSessionFile called for non-initialised SessionFile!"); return false; } + /** + * Get a lock for the SessionFile + * + * @return true if lock was made + */ + protected boolean lockFile() { + if (fileLock != null) { + if (fileLock.isLocked()) { + if (!ensureSessionFile()) + return false; + return true; + } else { + // lock failed for some reason. + fileLock.release(); + log.info("Unexpected session file lock failure. Trying to get it again."); + fileLock=null; + } + } + if (!ensureSessionFile()) + return false; + // TODO: see if we need to loop-wait for locks or they just block until + // lock is made... + long tries=5000; + do { + tries--; + if (fileLock==null || !fileLock.isLocked()) { + //try { Thread.sleep(1); } catch (Exception e) {}; + fileLock = LockFactory.getLock(sessionFile,true); // TODO: wait around if we can't get the lock. + } + } while (tries>0 && !fileLock.isLocked()); + if (!fileLock.isLocked()) + log.error("Failed to get lock for "+sessionFile); + // fileLock = new Lock(sessionFile); + return fileLock.isLocked(); + } /** * Explicitly release the SessionFile's lock. @@ -109,10 +118,18 @@ public class SessionFile { if (lockFile(extantLock)) { try { tempfile = File.createTempFile(backupPrefix, backupSuffix, backupDir); - FileOutputStream tos = new FileOutputStream(tempfile); - tos.getChannel().transferFrom(fileLock.rafile.getChannel(), 0, - fileLock.rafile.length()); - tos.close(); + if (fileLock.length()>0) { + FileOutputStream tos = new FileOutputStream(tempfile); + ReadableByteChannel channel; + tos.getChannel().transferFrom(channel=fileLock.getRaChannel(), 0, + fileLock.length()); + tos.close(); + if (!channel.isOpen()) + throw new Error(tos.getChannel().getClass()+".transferFrom closes source channel!"); + if (!lockFile(extantLock)) + throw new Error("Lost lock for "+sessionFile.getName()+" after backup."); + + } } catch (FileNotFoundException e1) { log.warn("Can't create temp file for "+sessionFile.getName(),e1); tempfile=null; @@ -136,11 +153,21 @@ public class SessionFile { throw new IOException("Null SessionFile in newData."); if (!lockFile(extantLock)) - log.error("Failed to get write lock for "+sessionFile); + throw new IOException("Failed to get write lock for "+sessionFile); if (!newData.lockFile()) - log.warn("Failed to get lock for updateFrom "+newData.sessionFile); - fileLock.rafile.getChannel().transferFrom(newData.fileLock.rafile.getChannel(), 0, - newData.fileLock.rafile.length()); + throw new IOException("Failed to get lock for updateFrom "+newData.sessionFile); + RandomAccessFile nrafile = newData.fileLock.getRaFile(); + nrafile.seek(0); + RandomAccessFile trafile = fileLock.getRaFile(); + /*long tries=5000; + while (trafile==null && --tries>0) { + log.debug("Lost lock on "+sessionFile+"! Re-trying for a transfer."); + lockFile(); + trafile = fileLock.getRaFile(); + }*/ + trafile.seek(0); + trafile.getChannel().transferFrom(nrafile.getChannel(), 0, + nrafile.length()); } /** * remove all trace of the sessionFile file diff --git a/src/org/vamsas/client/simpleclient/SimpleClientAppdata.java b/src/org/vamsas/client/simpleclient/SimpleClientAppdata.java index 91ad33d..84bc48a 100644 --- a/src/org/vamsas/client/simpleclient/SimpleClientAppdata.java +++ b/src/org/vamsas/client/simpleclient/SimpleClientAppdata.java @@ -327,7 +327,7 @@ public class SimpleClientAppdata implements IClientAppdata { log.debug("Successfully made temp appData file for "+apdname); } else { // truncate to remove existing data. - apdfile.fileLock.rafile.setLength(0); + apdfile.fileLock.getRaFile().setLength(0); log.debug("Successfully truncated existing temp appData for "+apdname); } } catch (Exception e) { diff --git a/src/org/vamsas/client/simpleclient/VamsasArchive.java b/src/org/vamsas/client/simpleclient/VamsasArchive.java index f065d04..e697861 100644 --- a/src/org/vamsas/client/simpleclient/VamsasArchive.java +++ b/src/org/vamsas/client/simpleclient/VamsasArchive.java @@ -154,7 +154,7 @@ public class VamsasArchive { */ File originalBackup = null; - boolean donotdeletebackup=false; + boolean donotdeletebackup=true; private final int _TRANSFER_BUFFER=4096*4; protected SimpleDocument vorba = null; /** @@ -216,7 +216,7 @@ public class VamsasArchive { */ public VamsasArchive(File archive, boolean overwrite, boolean vamsasdocument, SessionFile extantLock) throws IOException { super(); - if (archive==null || (archive!=null && !(archive.getParentFile().canWrite() && (!archive.exists() || archive.canWrite())))) { + if (archive==null || (archive!=null && !(archive.getAbsoluteFile().getParentFile().canWrite() && (!archive.exists() || archive.canWrite())))) { log.fatal("Expect Badness! -- Invalid parameters for VamsasArchive constructor:"+((archive!=null) ? "File cannot be overwritten." : "Null Object not valid constructor parameter")); return; @@ -248,9 +248,9 @@ public class VamsasArchive { else rchive = new SessionFile(archive); rchive.lockFile(); - if (rchive.fileLock==null || rchive.fileLock.rafile==null || !rchive.fileLock.isLocked()) + if (rchive.fileLock==null || !rchive.fileLock.isLocked()) throw new IOException("Lock failed for new archive"+archive); - rchive.fileLock.rafile.setLength(0); // empty the archive. + rchive.fileLock.getRaFile().setLength(0); // empty the archive. virginArchive = true; } this.openArchive(); // open archive @@ -383,7 +383,9 @@ public class VamsasArchive { newarchive.closeEntry(); if (!isDocumentWritten()) log.warn("Premature closure of archive '"+archive.getAbsolutePath()+"': No document has been written."); - newarchive.close(); // use newarchive.finish(); for a stream IO + newarchive.finish();// close(); // use newarchive.finish(); for a stream IO + newarchive.flush(); + // updateOriginal(); closeAndReset(); } else { @@ -563,9 +565,9 @@ public class VamsasArchive { if (!rchive.lockFile()) throw new IOException("Failed to get lock on file "+archive); // LATER: locked IO stream based access. - //Manifest newmanifest = new Manifest(); - //newarchive = new JarOutputStream(rchive.fileLock.getBufferedOutputStream(true), newmanifest); - newarchive = new JarOutputStream(new BufferedOutputStream(new java.io.FileOutputStream(archive))); + Manifest newmanifest = new Manifest(); + newarchive = new JarOutputStream(rchive.fileLock.getBufferedOutputStream(true), newmanifest); + //newarchive = new JarOutputStream(new BufferedOutputStream(new java.io.FileOutputStream(archive))); entries = new Hashtable(); } public void putVamsasDocument(VamsasDocument doc) throws IOException, @@ -781,14 +783,19 @@ public class VamsasArchive { odoclock.updateFrom(null, rchive); } catch (IOException e) { - // LATER: decide if leaving nastily named backup files around is necessary. - log.error("Problem updating archive from temporary file! - backup left in '" + // LATER: decide if leaving nastily named backup files around is necessary. + File backupFile=backupFile(); + if (backupFile!=null) + log.error("Problem updating archive from temporary file! - backup left in '" +backupFile().getAbsolutePath()+"'",e); + else + log.error("Problems updating, and failed to even make a backup file. Ooops!", e); } // Tidy up if necessary. removeBackup(); } else { + } } } diff --git a/src/org/vamsas/client/simpleclient/VamsasArchiveReader.java b/src/org/vamsas/client/simpleclient/VamsasArchiveReader.java index b4455ac..2df47f5 100644 --- a/src/org/vamsas/client/simpleclient/VamsasArchiveReader.java +++ b/src/org/vamsas/client/simpleclient/VamsasArchiveReader.java @@ -35,8 +35,8 @@ public class VamsasArchiveReader { ZipInputStream jstream=null; Hashtable strmentries = null; private void streamInit() { - throw new Error("VamsasArchiveReader(Stream) Not implemented!"); - /* if (!stream) { + //throw new Error("VamsasArchiveReader(Stream) Not implemented!"); + if (!stream) { log.debug("Skipping init for Jar Stream input."); return; } @@ -63,7 +63,7 @@ public class VamsasArchiveReader { catch (Exception e) { log.warn("Exceptions during init!",e); jstream=null; - }*/ + } } public VamsasArchiveReader(File vamsasfile) { @@ -87,12 +87,18 @@ public class VamsasArchiveReader { */ public VamsasArchiveReader(Lock vamsaslock) { // LATER: implement or remove - throw new Error("VamsasArchiveReading from locked IO stream not yet implemented."); - //rfile = vamsaslock.rafile; - //stream = true; - //streamInit(); - //if (jstream==null) - // throw new Error("Failed to open archive from Locked random access stream."); + if (vamsaslock==null || !vamsaslock.isLocked()) + throw new Error("IMPLEMENTATION ERROR: Cannot create a VamsasArchiveReader without a valid lock."); + // throw new Error("VamsasArchiveReading from locked IO stream not yet implemented."); + try { + rfile = vamsaslock.getRaFile(); + } catch (Exception e) { + log.warn("Unexpected IO Exception when accessing locked vamsas archive stream "+vamsaslock.target,e); + } + stream = true; + streamInit(); + if (jstream==null) + throw new Error("Failed to open archive from Locked random access stream."); } /** @@ -134,7 +140,7 @@ public class VamsasArchiveReader { long epos = entrypos.longValue(); do { entry = jstream.getNextEntry(); - } while (entry!=null && epos-->0); + } while (entry!=null && --epos>=0); // rfile.seek(entrypos.longValue()); // make a Jar entry from a zip entry. return new JarEntry(entry); diff --git a/src/org/vamsas/client/simpleclient/VamsasSession.java b/src/org/vamsas/client/simpleclient/VamsasSession.java index 887ca91..a5359ad 100644 --- a/src/org/vamsas/client/simpleclient/VamsasSession.java +++ b/src/org/vamsas/client/simpleclient/VamsasSession.java @@ -5,6 +5,7 @@ import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; +import java.io.RandomAccessFile; import java.io.Writer; import org.apache.commons.logging.Log; @@ -237,9 +238,10 @@ public class VamsasSession { SessionFile sfw = new SessionFile(new File(sessionDir, CLOSEANDSAVE_FILE)); while (!sfw.lockFile()) log.debug("Trying to get lock for "+CLOSEANDSAVE_FILE); - sfw.fileLock.rafile.setLength(0); // wipe out any old info. + 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?) - sfw.fileLock.rafile.writeUTF(client.getClientUrn()+":"+user.getFullName()+"@"+user.getOrganization()); + sfwfile.writeUTF(client.getClientUrn()+":"+user.getFullName()+"@"+user.getOrganization()); sfw.unlockFile(); if (store_doc_file!=null) store_doc_file.setState(); diff --git a/src/org/vamsas/test/simpleclient/ArchiveClient.java b/src/org/vamsas/test/simpleclient/ArchiveClient.java index d72b43e..da555af 100644 --- a/src/org/vamsas/test/simpleclient/ArchiveClient.java +++ b/src/org/vamsas/test/simpleclient/ArchiveClient.java @@ -83,7 +83,7 @@ public class ArchiveClient extends IdFactory { if (vsess==null) throw new Error("ArchiveClient instance is invalid!."); } - + public static int WATCH_SLEEP=300; /** * watch the document file for updates. * @param time - length of time to watch for. @@ -91,15 +91,17 @@ public class ArchiveClient extends IdFactory { */ public ClientDoc watch(long time) { valid(); + vsess.unLock(); // doh. FileWatcher watcher = new FileWatcher(vsess.getVamsasFile()); long endtime=System.currentTimeMillis()+time; try { org.vamsas.client.simpleclient.Lock doclock; - watcher.setState(); + //watcher.setState(); do { - Thread.sleep(50); // tuning. doclock=watcher.getChangedState(); - } while (doclock==null && (time==0 || endtime>System.currentTimeMillis())); + if (doclock==null) + Thread.sleep(WATCH_SLEEP); + } while (doclock==null && (time==0 || endtime>System.currentTimeMillis())); // tuning. if (doclock==null) return null; else { @@ -170,7 +172,13 @@ public class ArchiveClient extends IdFactory { public ClientDoc getUpdateable() { valid(); try { - vsess.getLock(); + // patiently wait for a lock on the document. + long tries=5000; + org.vamsas.client.simpleclient.Lock lock; + while (((lock=vsess.getLock())==null || !lock.isLocked()) && --tries>0) { +// Thread.sleep(1); + log.debug("Trying to get a document lock for the "+tries+"'th time."); + } VamsasArchive varc = new VamsasArchive(vsess, true, false); // read archive, write as vamsasDocument, don't erase original contents. varc.setVorba(this); VamsasDocument d = varc.getVamsasDocument(getProvenanceUser(), "Created new document.", VersionEntries.latestVersion()); // VAMSAS: provenance user and client combined @@ -211,6 +219,7 @@ public class ArchiveClient extends IdFactory { // do any appDatas first. if (cdoc.iohandler.transferRemainingAppDatas()) log.debug("Remaining appdatas were transfered."); + cdoc.updateDocumentRoots(); cdoc.iohandler.putVamsasDocument(cdoc.doc); cdoc.iohandler.closeArchive(); cdoc.iohandler=null; @@ -244,14 +253,17 @@ public class ArchiveClient extends IdFactory { cdoc.addVamsasRoot(Core.getDemoVamsas()); System.out.println("Doing update."); client.doUpdate(cdoc); + cdoc.closeDoc(); cdoc = null; int u=5; while (--u>0) { System.out.println("Watch for more... ("+u+" left)"); - cdoc = client.watch(0); - if (cdoc!=null) { + ClientDoc ucdoc = client.watch(50000); + if (ucdoc!=null) { System.out.println("****\nUpdate detected at "+new Date()); - ArchiveReports.reportDocument(cdoc.doc, cdoc.getReader(), true, System.out); + ArchiveReports.reportDocument(ucdoc.doc, ucdoc.getReader(), true, System.out); + ucdoc.closeDoc(); + ucdoc=null; } else { System.out.println("!!!! Null document update detected at "+new Date()); } diff --git a/src/org/vamsas/test/simpleclient/ClientDoc.java b/src/org/vamsas/test/simpleclient/ClientDoc.java index a560c38..cc56e8c 100644 --- a/src/org/vamsas/test/simpleclient/ClientDoc.java +++ b/src/org/vamsas/test/simpleclient/ClientDoc.java @@ -209,14 +209,32 @@ public class ClientDoc { public VamsasArchiveReader getReader() { return reader; } + private void _finalize() { + log.debug("finalizing clientDoc"); + if (doc!=null) { + doc = null; + } + if (_VamsasRoots!=null) { + for (int i=0; i<_VamsasRoots.length; i++) + _VamsasRoots[i]=null; + _VamsasRoots=null; + + } + + if (reader!=null) { + log.debug("Closing and removing reader reference"); + reader.close(); + reader=null; + } + if (iohandler!=null) { + log.debug("Removing ioHandler reference."); + iohandler.cancelArchive(); + iohandler=null; + } + } protected void finalize() throws Throwable { + _finalize(); super.finalize(); - //if (reader!=null) - // reader.close(); - //reader=null; -// if (iohandler!=null) { -// iohandler.cancelArchive(); // otherwise the original may be overwritten. -// } } private java.util.Hashtable objrefs=null; @@ -303,5 +321,29 @@ public class ClientDoc { log.debug("Returning null Vobject reference for id "+ids[i].getId()); return vo; } + protected void updateDocumentRoots() { + if (doc==null) { + log.error("updateDocumentRoots called on null document. Probably an implementation error."); + return; + } + if (isModified) { + if (_VamsasRoots!=null) { + doc.setVAMSAS(_VamsasRoots); + _VamsasRoots=null; + } + } + } + /** + * tell vamsas client to close the document and reset the object. Once closed, nothing can be done with the object. + * + */ + public void closeDoc() { + if (doc!=null) { + log.debug("Closing open document."); + _finalize(); + } else { + log.warn("Ignoring closeDoc on invalid document."); + } + } } \ No newline at end of file diff --git a/src/org/vamsas/test/simpleclient/ClientsFileTest.java b/src/org/vamsas/test/simpleclient/ClientsFileTest.java index 9ab68ac..46fbfd7 100644 --- a/src/org/vamsas/test/simpleclient/ClientsFileTest.java +++ b/src/org/vamsas/test/simpleclient/ClientsFileTest.java @@ -87,11 +87,12 @@ public class ClientsFileTest { break; case 3: // clear - cfhand = null; - cf.delete(); + //cfhand.get = null; + //cf.delete(); try { - cf.createNewFile(); - cfhand = new ClientsFile(cf); + + cfhand.clearList(); + } catch (Exception e) { System.err.println("Failed on new empty clientfile creation!"); e.printStackTrace(System.err); diff --git a/src/org/vamsas/test/simpleclient/VamsasArchive.java b/src/org/vamsas/test/simpleclient/VamsasArchive.java index 3ec7d4d..09ddf34 100644 --- a/src/org/vamsas/test/simpleclient/VamsasArchive.java +++ b/src/org/vamsas/test/simpleclient/VamsasArchive.java @@ -125,12 +125,18 @@ public class VamsasArchive { } while (newf.exists()); } */ + if (newf.exists()) { + log.info("Removing existing "+newf); + newf.delete(); + } + log.info("Now writing new Archive into "+newf.getAbsolutePath()); org.vamsas.client.simpleclient.VamsasArchive va=null; { // hold lock over deletion and write of new archive. //Lock wlock = sfile.getLock(); //newf.delete(); // clear out old file. - va = new org.vamsas.client.simpleclient.VamsasArchive(newf, true, true); // , sfile); + sfile.getLock(); + va = new org.vamsas.client.simpleclient.VamsasArchive(newf, true, true, sfile); // open another and... ApplicationData appdata = makeDemoAppdata(va, "org.vamsas.test.simpleclient.VamsasArchive", "arnold Bugger esq", "disOrganised"); @@ -146,9 +152,11 @@ public class VamsasArchive { { Lock lock=sfile.getLock(); if (lock==null) - while ((lock=sfile.getLock())==null) + while ((lock=sfile.getLock())==null) { log.info("Waiting for lock."); - VamsasArchiveReader vreader = new VamsasArchiveReader(newf); + Thread.sleep(100); + } + VamsasArchiveReader vreader = new VamsasArchiveReader(sfile.getVamsasFile());// lock); // cannot do new JarFile on a locked file. // newf); SimpleDocument sdoc = new SimpleDocument("testing new vamsas write"); ArchiveReports.reportDocument(sdoc.getVamsasDocument(vreader), vreader, true, System.out); sfile.unLock(); -- 1.7.10.2