/* * This file is part of the Vamsas Client version 0.1. * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, * Andrew Waterhouse and Dominik Lindner. * * Earlier versions have also been incorporated into Jalview version 2.4 * since 2008, and TOPALi version 2 since 2007. * * The Vamsas Client is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Vamsas Client is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Vamsas Client. If not, see . */ package uk.ac.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(fileLock, 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); long sourceln = fileLock.length(); if (sourceln > 0) { FileOutputStream tos = new FileOutputStream(tempfile); ReadableByteChannel channel; channel = fileLock.getRaChannel().position(0); long ntrans = 0; while (ntrans < sourceln) { long tlen; ntrans += tlen = tos.getChannel().transferFrom(channel, ntrans, sourceln); if (log.isDebugEnabled()) { log.debug("Transferred " + tlen + " out of " + sourceln + " bytes"); } } tos.close(); if (!channel.isOpen()) throw new Error("LIBRARY PORTABILITY ISSUE: " + tos.getChannel().getClass() + ".transferFrom closes source channel!"); if (!lockFile(extantLock)) throw new Error("LIBRARY PORTABILITY ISSUE: 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(); } */ // TODO JBPNote: attempt to ensure save really saves the VamDoc.jar file trafile.seek(0); trafile.getChannel() .transferFrom(nrafile.getChannel(), 0, nrafile.length()); // JBPNote: attempt to close the streams to flush the data out // trafile.close(); // nrafile.close(); } /** * remove all trace of the sessionFile file * */ protected void eraseExistence() { unlockFile(); if (sessionFile != null) { sessionFile.delete(); sessionFile = null; } } /* * (non-Javadoc) * * @see uk.ac.vamsas.client.simpleclient.Lock#getBufferedInputStream(boolean) */ public BufferedInputStream getBufferedInputStream(boolean atStart) throws IOException { lockFile(); return fileLock.getBufferedInputStream(atStart); } /* * (non-Javadoc) * * @see uk.ac.vamsas.client.simpleclient.Lock#getBufferedOutputStream(boolean) */ public BufferedOutputStream getBufferedOutputStream(boolean clear) throws IOException { lockFile(); return fileLock.getBufferedOutputStream(clear); } /* * (non-Javadoc) * * @see uk.ac.vamsas.client.simpleclient.Lock#getFileInputStream(boolean) */ public FileInputStream getFileInputStream(boolean atStart) throws IOException { lockFile(); return fileLock.getFileInputStream(atStart); } /* * (non-Javadoc) * * @see uk.ac.vamsas.client.simpleclient.Lock#getFileOutputStream(boolean) */ public FileOutputStream getFileOutputStream(boolean clear) throws IOException { lockFile(); return fileLock.getFileOutputStream(clear); } }