backtrack
[vamsas.git] / src / org / vamsas / client / simpleclient / SessionFile.java
diff --git a/src/org/vamsas/client/simpleclient/SessionFile.java b/src/org/vamsas/client/simpleclient/SessionFile.java
new file mode 100644 (file)
index 0000000..c9f9e67
--- /dev/null
@@ -0,0 +1,215 @@
+package org.vamsas.client.simpleclient;
+
+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.RandomAccessFile;
+import java.nio.channels.ReadableByteChannel;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Basic methods for classes handling locked IO on files 
+ * monitored by all (simpleclient) clients in a vamsas session.
+ * @author jimp
+ *TODO: support non nio file locking capable systems
+ */
+public class SessionFile {
+  private static Log log = LogFactory.getLog(SessionFile.class);
+  protected File sessionFile;
+  protected Lock fileLock = null;
+
+  protected SessionFile(File file) {
+    super();
+    sessionFile = file;
+  }
+
+  protected boolean lockFile(Lock extantlock) {
+    if (fileLock!=null && !fileLock.isLocked()) {
+      fileLock.release();// tidy up invalid lock
+      fileLock=null;
+    }
+    if (extantlock!=null && extantlock.isLocked())
+      fileLock=extantlock; // THIS IS BROKEN - lockFile then nulls the lock.
+    return lockFile();
+  }
+  private boolean ensureSessionFile() {
+    if (sessionFile != null) {
+      if (!sessionFile.exists()) {
+        // create new file
+        try {
+          if (!sessionFile.createNewFile()) {
+            log.error("Failed to create file prior to locking: "+sessionFile);
+            return false;
+          }
+        } catch (IOException e) {
+          log.error("Exception when trying to create file "+sessionFile, e);
+          return false;
+        }
+      }
+      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.
+   * 
+   * @return true if lock was released.
+   */
+  protected void unlockFile() {
+    if (fileLock != null) {
+      fileLock.release();    
+      fileLock = null;
+    }
+  }
+
+  /**
+   * Makes a backup of the sessionFile.
+   * @return Backed up SessionFile or null if failed to make backup.
+   */
+  protected File backupSessionFile() {
+    return backupSessionFile(null, sessionFile.getName(),".old", sessionFile.getParentFile());
+  }
+
+  protected File backupSessionFile(Lock extantLock, String backupPrefix, String backupSuffix, File backupDir) {
+    File tempfile=null;
+    if (lockFile(extantLock)) {
+      try {
+        tempfile = File.createTempFile(backupPrefix, backupSuffix, backupDir);
+        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;
+      } catch (IOException e1) {
+        log.warn("Error when copying content to temp file for "+sessionFile.getName(),e1);
+        tempfile=null;
+      }
+    }
+    return tempfile;
+  }
+  /**
+   * Replaces data in sessionFile with data from file handled by another sessionFile
+   * passes up any exceptions.
+   * @param newData source for new data
+   */
+  protected void updateFrom(Lock extantLock, SessionFile newData) throws IOException {
+    log.debug("Updating "+sessionFile.getAbsolutePath()+" from "+newData.sessionFile.getAbsolutePath());
+    if (newData==null)
+      throw new IOException("Null newData object.");
+    if (newData.sessionFile==null)
+      throw new IOException("Null SessionFile in newData.");
+      
+    if (!lockFile(extantLock))
+      throw new IOException("Failed to get write lock for "+sessionFile);
+    if (!newData.lockFile())
+      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
+   *
+   */
+  protected void eraseExistence() {
+    unlockFile();
+    if (sessionFile!=null) {
+      sessionFile.delete();
+      sessionFile = null;
+    }
+  }
+  /* (non-Javadoc)
+   * @see org.vamsas.client.simpleclient.Lock#getBufferedInputStream(boolean)
+   */
+  public BufferedInputStream getBufferedInputStream(boolean atStart) throws IOException {
+    lockFile();
+    return fileLock.getBufferedInputStream(atStart);
+  }
+
+  /* (non-Javadoc)
+   * @see org.vamsas.client.simpleclient.Lock#getBufferedOutputStream(boolean)
+   */
+  public BufferedOutputStream getBufferedOutputStream(boolean clear) throws IOException {
+    lockFile();
+    return fileLock.getBufferedOutputStream(clear);
+  }
+
+  /* (non-Javadoc)
+   * @see org.vamsas.client.simpleclient.Lock#getFileInputStream(boolean)
+   */
+  public FileInputStream getFileInputStream(boolean atStart) throws IOException {
+    lockFile();
+    return fileLock.getFileInputStream(atStart);
+  }
+
+  /* (non-Javadoc)
+   * @see org.vamsas.client.simpleclient.Lock#getFileOutputStream(boolean)
+   */
+  public FileOutputStream getFileOutputStream(boolean clear) throws IOException {
+    lockFile();
+    return fileLock.getFileOutputStream(clear);
+  }
+  
+}