/* * 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.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) { // TODO: fix occasional exceptions raised here (usually on JVM shutdown) if (advisory.target != null) { advisory.target.deleteOnExit(); // release will null the target } 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; } }