updated test cases for spring 2006 schema, and major debug of locking system
authorjprocter <jprocter@compbio.dundee.ac.uk>
Fri, 1 Sep 2006 17:02:21 +0000 (17:02 +0000)
committerjprocter <jprocter@compbio.dundee.ac.uk>
Fri, 1 Sep 2006 17:02:21 +0000 (17:02 +0000)
git-svn-id: https://svn.lifesci.dundee.ac.uk/svn/repository/trunk@234 be28352e-c001-0410-b1a7-c7978e42abec

16 files changed:
src/org/vamsas/client/simpleclient/ClientsFile.java
src/org/vamsas/client/simpleclient/FileLock.java
src/org/vamsas/client/simpleclient/FileWatcher.java
src/org/vamsas/client/simpleclient/Lock.java
src/org/vamsas/client/simpleclient/LockFactory.java
src/org/vamsas/client/simpleclient/LockedFileOutputStream.java
src/org/vamsas/client/simpleclient/NativeLock.java
src/org/vamsas/client/simpleclient/SessionFile.java
src/org/vamsas/client/simpleclient/SimpleClientAppdata.java
src/org/vamsas/client/simpleclient/VamsasArchive.java
src/org/vamsas/client/simpleclient/VamsasArchiveReader.java
src/org/vamsas/client/simpleclient/VamsasSession.java
src/org/vamsas/test/simpleclient/ArchiveClient.java
src/org/vamsas/test/simpleclient/ClientDoc.java
src/org/vamsas/test/simpleclient/ClientsFileTest.java
src/org/vamsas/test/simpleclient/VamsasArchive.java

index cab03cd..96fa871 100644 (file)
@@ -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);
+        
+      }
+    }
+    
+  }
 }
index 8cb6808..53688ac 100644 (file)
@@ -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;
+  }
 }
index 29d5979..fe72381 100644 (file)
@@ -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;
       }
index 9dd472f..d3a5889 100644 (file)
@@ -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;
 }
index 72cbe66..5fdde6c 100644 (file)
@@ -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);
+  }
 }
index 92d9d0c..1d59c8a 100644 (file)
@@ -8,19 +8,36 @@ import java.io.FileDescriptor;
 import java.io.FileNotFoundException;\r
 import java.io.FileOutputStream;\r
 import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.nio.channels.FileChannel;\r
+\r
+import org.apache.commons.logging.LogFactory;\r
 \r
 /**\r
  * @author Jim\r
  *\r
  */\r
 public class LockedFileOutputStream extends FileOutputStream {\r
-  \r
+  private static org.apache.commons.logging.Log log = LogFactory.getLog(LockedFileOutputStream.class);\r
+  //FileOutputStream ostream=null;\r
+  boolean closed=true;\r
+  private void init() {\r
+    FileChannel ch = super.getChannel();\r
+    if (ch!=null) {\r
+      try { closed = !ch.isOpen();\r
+      } catch (Exception e) {\r
+        closed=true;\r
+        log.debug("Invalid LockedOutputStream marked closed.",e);\r
+      }\r
+    }\r
+  }\r
   /**\r
    * @param file\r
    * @throws FileNotFoundException\r
    */\r
   public LockedFileOutputStream(File file) throws FileNotFoundException {\r
-    super(file);\r
+    super(file); // super(file);\r
+    init();\r
   }\r
 \r
   /**\r
@@ -31,6 +48,7 @@ public class LockedFileOutputStream extends FileOutputStream {
   public LockedFileOutputStream(File file, boolean append)\r
       throws FileNotFoundException {\r
     super(file, append);\r
+    init();\r
   }\r
 \r
   /**\r
@@ -38,6 +56,9 @@ public class LockedFileOutputStream extends FileOutputStream {
    */\r
   public LockedFileOutputStream(FileDescriptor fdObj) {\r
     super(fdObj);\r
+    init();\r
+    if (fdObj.valid())\r
+      closed=false;\r
   }\r
 \r
   /**\r
@@ -46,6 +67,7 @@ public class LockedFileOutputStream extends FileOutputStream {
    */\r
   public LockedFileOutputStream(String name) throws FileNotFoundException {\r
     super(name);\r
+    init();\r
   }\r
 \r
   /**\r
@@ -56,14 +78,82 @@ public class LockedFileOutputStream extends FileOutputStream {
   public LockedFileOutputStream(String name, boolean append)\r
       throws FileNotFoundException {\r
     super(name, append);\r
-    // TODO Auto-generated constructor stub\r
+    init();\r
   }\r
   /**\r
    * closes - actually just flushes the stream instead.\r
    */\r
   public void close() throws IOException {\r
-    // TODO Auto-generated method stub\r
-    super.flush();\r
+    if (!closed) {\r
+      super.flush();\r
+      super.getChannel().force(true);\r
+      log.debug("Marking Lockedoutputstream closed.");\r
+    } else\r
+      throw new IOException("Close on already closed FileOutputStream.");\r
+    closed=true;\r
+  }\r
+\r
+\r
+  /**\r
+   * @throws IOException\r
+   * @see java.io.OutputStream#flush()\r
+   */\r
+  public void flush() throws IOException {\r
+    if (!closed)\r
+      super.flush();\r
+    else\r
+      throw new IOException("flush on closed FileOutputStream");\r
+  }\r
+\r
+  /**\r
+   * @return\r
+   * @see java.io.FileOutputStream#getChannel()\r
+   */\r
+  public FileChannel getChannel() {\r
+    if (!closed)\r
+      return super.getChannel();\r
+    else\r
+      return null;\r
+  }\r
+\r
+\r
+\r
+  /**\r
+   * @param b\r
+   * @param off\r
+   * @param len\r
+   * @throws IOException\r
+   * @see java.io.FileOutputStream#write(byte[], int, int)\r
+   */\r
+  public void write(byte[] b, int off, int len) throws IOException {\r
+    if (!closed)\r
+      super.write(b, off, len);\r
+    else\r
+      throw new IOException("write on Closed FileOutputStream");\r
+  }\r
+\r
+  /**\r
+   * @param b\r
+   * @throws IOException\r
+   * @see java.io.FileOutputStream#write(byte[])\r
+   */\r
+  public void write(byte[] b) throws IOException {\r
+    if (!closed)\r
+      super.write(b);\r
+    else\r
+      throw new IOException("write on Closed FileOutputStream");\r
+  }\r
+\r
+  /**\r
+   * @param b\r
+   * @throws IOException\r
+   * @see java.io.FileOutputStream#write(int)\r
+   */\r
+  public void write(int b) throws IOException {\r
+    if (!closed)\r
+      super.write(b);\r
+    else\r
+      throw new IOException("write on Closed FileOutputStream");\r
   }\r
   \r
 }\r
index 96d9b6b..6a624f1 100644 (file)
@@ -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;
+  }
+
 }
index bd2dc44..c9f9e67 100644 (file)
@@ -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
index 91ad33d..84bc48a 100644 (file)
@@ -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) {
index f065d04..e697861 100644 (file)
@@ -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 {
       
+      
     }
   }
 }
index b4455ac..2df47f5 100644 (file)
@@ -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);
index 887ca91..a5359ad 100644 (file)
@@ -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();
index d72b43e..da555af 100644 (file)
@@ -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());
         }
index a560c38..cc56e8c 100644 (file)
@@ -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
index 9ab68ac..46fbfd7 100644 (file)
@@ -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);
index 3ec7d4d..09ddf34 100644 (file)
@@ -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();