X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fuk%2Fac%2Fvamsas%2Fclient%2Fsimpleclient%2FFileLock.java;h=cc5f0c9af270b68502eaebef8d2adaa67cfe6c53;hb=1eea4b639911330e7cd65e17c5421cc584f0d22e;hp=4ab0a6586fadec373b368556926caa80cecdf4e4;hpb=5156a824b13ecd091af9f84870f41d83eb59bd71;p=vamsas.git diff --git a/src/uk/ac/vamsas/client/simpleclient/FileLock.java b/src/uk/ac/vamsas/client/simpleclient/FileLock.java index 4ab0a65..cc5f0c9 100644 --- a/src/uk/ac/vamsas/client/simpleclient/FileLock.java +++ b/src/uk/ac/vamsas/client/simpleclient/FileLock.java @@ -1,205 +1,278 @@ -package uk.ac.vamsas.client.simpleclient; - -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.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 { - 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) { - if ( advisory!=null) - advisory.release(true); - advisory.target.deleteOnExit(); - advisory=null; - _lock=null; - } - } - /** - * @param lockfile - * @param block true means thread blocks until FileLock is obtained. - */ - public FileLock(File lockfile, boolean block) { - super(lockfile); - // try and get a lock. - try { - _lock = new File(lockfile.getParentFile(), lockfile.getName()+"."+_LockSuffix); - if (!ensureLockFile(block)) { - log.debug("Couldn't get lock on "+_lock); - tidy(); - return; - } - // 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(); - } catch (FileNotFoundException e) { - // - log.debug("FileLock failed with target="+lockfile+" and lockfile suffix of "+_LockSuffix); - //log.error("Error! Couldn't create a lockfile at " - // + lockfile.getAbsolutePath(), e); - } catch (IOException e) { - log.error("Error! Problems with IO when creating a lock on " - + lockfile.getAbsolutePath(),e); - } - } - - private boolean openRaFile() throws IOException { - if (target==null) - return false; - if (advisory==null || !advisory.isLocked()) - return false; - 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 (advisory != null) { - if (advisory.isLocked()) - return true; - advisory=null; - if (log.isDebugEnabled()) - log.debug("Lockfile "+_lock+" unexpectedly deleted ?"); - } - return false; - } - - public void release() { - release(true); - } - - public void release(boolean closeChannel) { - if (!isLocked()) - return; - if (log.isDebugEnabled()) - log.debug("Releasing advisory lock on "+target); - if (closeChannel) { - if (rafile!=null) - try { - rafile.close(); - } catch (Exception e) { - log.debug("Unexpected exception whilst closing RandomAccessFile on "+target, e); - } - rafile=null; - } - tidy(); - } - - public FileInputStream getFileInputStream(boolean atStart) throws IOException { - if (!isLocked()) { - log.debug("Don't hold lock on "+target+" to get locked FileInputStream."); - return null; - } - openRaFile(); - if (atStart) - rafile.seek(0); - return new FileInputStream(rafile.getFD()); - } - - - public FileOutputStream getFileOutputStream(boolean clear) throws IOException { - if (!isLocked()) { - log.debug("Don't hold lock on "+target+" to get locked FileOutputStream."); - return null; - } - openRaFile(); - if (clear) { - rafile.seek(0); - rafile.setLength(0); - } 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 uk.ac.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; - } -} +/* + * This file is part of the Vamsas Client version 0.2. + * Copyright 2010 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.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.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 { + 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) { + if (!_lock.exists()) { + // advisory cannot be created. this is serious. + log.fatal("Failed to create advisory lock file " + _lock, e); + throw new Error("Failed to create advisory lock file " + _lock); + } + } + return (advisory != null) && advisory.isLocked(); + } + + /** + * call to clear up a filelock file after its been made + * + */ + private void tidy() { + if (_lock != null) { + if (advisory != null) { + File tgt = advisory.target; + // TODO: fix occasional exceptions raised here (usually on JVM shutdown) + if (tgt != null) { + try { + tgt.deleteOnExit(); // release will null the target + } catch (NullPointerException e) + { + // ignore - TODO: fix nulls + } + } + advisory.release(true); + } + advisory = null; + _lock = null; + } + } + + /** + * @param lockfile + * @param block + * true means thread blocks until FileLock is obtained. + */ + public FileLock(File lockfile, boolean block) { + super(lockfile); + // try and get a lock. + try { + _lock = make_lockForTarget(lockfile); + if (!ensureLockFile(block)) { + log.debug("Couldn't get lock on " + _lock); + tidy(); + return; + } + // 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(); + } catch (FileNotFoundException e) { + // + log.debug("FileLock failed with target=" + lockfile + + " and lockfile suffix of " + _LockSuffix); + // log.error("Error! Couldn't create a lockfile at " + // + lockfile.getAbsolutePath(), e); + } catch (IOException e) { + log.error("Error! Problems with IO when creating a lock on " + + lockfile.getAbsolutePath(), e); + } + } + + /** + * + * @param lockfile + * target of lock + * @return file object that will be locked for lockfile + */ + private File make_lockForTarget(File lockfile) { + return new File(lockfile.getParentFile(), lockfile.getName() + "." + + _LockSuffix); + } + + private boolean openRaFile() throws IOException { + if (target == null) + return false; + if (advisory == null || !advisory.isLocked()) + return false; + 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 (advisory != null) { + if (advisory.isLocked()) + return true; + advisory = null; + if (log.isDebugEnabled()) + log.debug("Lockfile " + _lock + " unexpectedly deleted ?"); + } + return false; + } + + public void release() { + release(true); + } + + public void release(boolean closeChannel) { + if (!isLocked()) + return; + if (rafile != null) { + if (closeChannel) { + try { + rafile.close(); + } catch (Exception e) { + log.debug("Unexpected exception whilst closing RandomAccessFile on " + + target, e); + } + rafile = null; // do not hold reference to rafile anymore either + } + if (log.isDebugEnabled()) + log.debug("Releasing advisory lock on " + target); + // TODO: LATER: verify this change in closeChannel semantics really is + // correct - ArchiveClient works correctly + } + tidy(); + } + + public FileInputStream getFileInputStream(boolean atStart) throws IOException { + if (!isLocked()) { + log.debug("Don't hold lock on " + target + + " to get locked FileInputStream."); + return null; + } + openRaFile(); + if (atStart) + rafile.seek(0); + return new FileInputStream(rafile.getFD()); + } + + public FileOutputStream getFileOutputStream(boolean clear) throws IOException { + if (!isLocked()) { + log.debug("Don't hold lock on " + target + + " to get locked FileOutputStream."); + return null; + } + openRaFile(); + if (clear) { + rafile.seek(0); + rafile.setLength(0); + } 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 uk.ac.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; + } + + public boolean isTargetLockFile(File afile) { + if (isLocked()) { + if (target.equals(afile) || make_lockForTarget(target).equals(afile)) + return true; + } + return false; + } +}