2 * This file is part of the Vamsas Client version 0.2.
\r
3 * Copyright 2010 by Jim Procter, Iain Milne, Pierre Marguerite,
\r
4 * Andrew Waterhouse and Dominik Lindner.
\r
6 * Earlier versions have also been incorporated into Jalview version 2.4
\r
7 * since 2008, and TOPALi version 2 since 2007.
\r
9 * The Vamsas Client is free software: you can redistribute it and/or modify
\r
10 * it under the terms of the GNU Lesser General Public License as published by
\r
11 * the Free Software Foundation, either version 3 of the License, or
\r
12 * (at your option) any later version.
\r
14 * The Vamsas Client is distributed in the hope that it will be useful,
\r
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
17 * GNU Lesser General Public License for more details.
\r
19 * You should have received a copy of the GNU Lesser General Public License
\r
20 * along with the Vamsas Client. If not, see <http://www.gnu.org/licenses/>.
\r
22 package uk.ac.vamsas.client.simpleclient;
\r
24 import java.io.BufferedOutputStream;
\r
25 import java.io.File;
\r
26 import java.io.FileInputStream;
\r
27 import java.io.FileNotFoundException;
\r
28 import java.io.FileOutputStream;
\r
29 import java.io.IOException;
\r
30 import java.io.RandomAccessFile;
\r
31 import java.nio.channels.FileChannel;
\r
32 import java.nio.channels.ReadableByteChannel;
\r
35 * File based Locking mechanism to get around some bizarre limitations of
\r
36 * JarEntry seeking. Abstract locks have a target file, to which access is
\r
37 * controlled when a lock is held. Native locks on WindowsXP seem to conflict
\r
38 * with Jar seek operations, so a file lock creates an advisory lock. Method: A
\r
39 * lock file is created, if it doesn't already exist - the naming convention is
\r
40 * TargetFile+suffixSeparator+_LockSuffix. A lock is obtained by locking the
\r
41 * lock file with a native lock. The NativeLock is used for this.
\r
46 public class FileLock extends Lock {
\r
47 private File _lock = null;
\r
49 protected static String _LockSuffix = "lck";
\r
51 private NativeLock advisory = null;
\r
54 * ensure that the _lock file exists and create a lock
\r
56 private boolean ensureLockFile(boolean block) {
\r
59 if (advisory != null && advisory.isLocked())
\r
62 advisory = new NativeLock(_lock, block);
\r
63 } catch (Exception e) {
\r
64 if (!_lock.exists()) {
\r
65 // advisory cannot be created. this is serious.
\r
66 log.fatal("Failed to create advisory lock file " + _lock, e);
\r
67 throw new Error("Failed to create advisory lock file " + _lock);
\r
70 return (advisory != null) && advisory.isLocked();
\r
74 * call to clear up a filelock file after its been made
\r
77 private void tidy() {
\r
78 if (_lock != null) {
\r
79 if (advisory != null) {
\r
80 File tgt = advisory.target;
\r
81 // TODO: fix occasional exceptions raised here (usually on JVM shutdown)
\r
84 tgt.deleteOnExit(); // release will null the target
\r
85 } catch (NullPointerException e)
\r
87 // ignore - TODO: fix nulls
\r
90 advisory.release(true);
\r
100 * true means thread blocks until FileLock is obtained.
\r
102 public FileLock(File lockfile, boolean block) {
\r
104 // try and get a lock.
\r
106 _lock = make_lockForTarget(lockfile);
\r
107 if (!ensureLockFile(block)) {
\r
108 log.debug("Couldn't get lock on " + _lock);
\r
112 // create target file ready to be written to if necessary.
\r
113 if (!lockfile.exists())
\r
114 if (!lockfile.createNewFile()) {
\r
115 log.warn("Failed to create locked file " + lockfile);
\r
119 } catch (FileNotFoundException e) {
\r
121 log.debug("FileLock failed with target=" + lockfile
\r
122 + " and lockfile suffix of " + _LockSuffix);
\r
123 // log.error("Error! Couldn't create a lockfile at "
\r
124 // + lockfile.getAbsolutePath(), e);
\r
125 } catch (IOException e) {
\r
126 log.error("Error! Problems with IO when creating a lock on "
\r
127 + lockfile.getAbsolutePath(), e);
\r
135 * @return file object that will be locked for lockfile
\r
137 private File make_lockForTarget(File lockfile) {
\r
138 return new File(lockfile.getParentFile(), lockfile.getName() + "."
\r
142 private boolean openRaFile() throws IOException {
\r
143 if (target == null)
\r
145 if (advisory == null || !advisory.isLocked())
\r
147 if (rafile == null || rafile.getFD() == null || !rafile.getFD().valid()) {
\r
148 rafile = new RandomAccessFile(target, "rw");
\r
150 if (log.isDebugEnabled())
\r
151 log.debug("Reusing existing RandomAccessFile on " + target);
\r
153 return (rafile.getChannel() != null) && rafile.getChannel().isOpen();
\r
156 public boolean isLocked() {
\r
157 if (advisory != null) {
\r
158 if (advisory.isLocked())
\r
161 if (log.isDebugEnabled())
\r
162 log.debug("Lockfile " + _lock + " unexpectedly deleted ?");
\r
167 public void release() {
\r
171 public void release(boolean closeChannel) {
\r
174 if (rafile != null) {
\r
175 if (closeChannel) {
\r
178 } catch (Exception e) {
\r
179 log.debug("Unexpected exception whilst closing RandomAccessFile on "
\r
182 rafile = null; // do not hold reference to rafile anymore either
\r
184 if (log.isDebugEnabled())
\r
185 log.debug("Releasing advisory lock on " + target);
\r
186 // TODO: LATER: verify this change in closeChannel semantics really is
\r
187 // correct - ArchiveClient works correctly
\r
192 public FileInputStream getFileInputStream(boolean atStart) throws IOException {
\r
194 log.debug("Don't hold lock on " + target
\r
195 + " to get locked FileInputStream.");
\r
201 return new FileInputStream(rafile.getFD());
\r
204 public FileOutputStream getFileOutputStream(boolean clear) throws IOException {
\r
206 log.debug("Don't hold lock on " + target
\r
207 + " to get locked FileOutputStream.");
\r
213 rafile.setLength(0);
\r
215 rafile.seek(rafile.length());
\r
216 return new LockedFileOutputStream(rafile.getFD());
\r
219 public BufferedOutputStream getBufferedOutputStream(boolean clear)
\r
220 throws IOException {
\r
221 log.debug("Getting BufferedOutputStream (clear=" + clear + ")");
\r
222 FileOutputStream fos = getFileOutputStream(clear);
\r
224 return new BufferedOutputStream(fos);
\r
231 * @see uk.ac.vamsas.client.simpleclient.Lock#getLength()
\r
233 public long length() {
\r
235 if (!target.exists()) {
\r
237 target.createNewFile();
\r
238 } catch (Exception e) {
\r
239 log.error("Invalid lock:Failed to create target file " + target);
\r
245 return target.length();
\r
250 protected void finalize() throws Throwable {
\r
251 release(true); // we explicitly lose the lock here.
\r
255 public RandomAccessFile getRaFile() throws IOException {
\r
256 if (isLocked() && openRaFile()) {
\r
259 log.debug("Failed to getRaFile on target " + target);
\r
263 public FileChannel getRaChannel() throws IOException {
\r
264 if (isLocked() && openRaFile()) {
\r
265 return rafile.getChannel();
\r
267 log.debug("Failed to getRaChannel on target " + target);
\r
271 public boolean isTargetLockFile(File afile) {
\r
273 if (target.equals(afile) || make_lockForTarget(target).equals(afile))
\r