/* Cobertura - http://cobertura.sourceforge.net/
*
* Copyright (C) 2006 John Lewis
* Copyright (C) 2006 Mark Doliner
* Copyright (C) 2009 Chris van Es
*
* Note: This file is dual licensed under the GPL and the Apache
* Source License 1.1 (so that it can be used from both the main
* Cobertura classes and the ant tasks).
*
* Cobertura is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Cobertura 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cobertura; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
package net.sourceforge.cobertura.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* This class controls access to any file so that multiple JVMs will
* not be able to write to the file at the same time.
* <p/>
* A file called "filename.lock" is created and Java's FileLock class
* is used to lock the file.
* <p/>
* The java.nio classes were introduced in Java 1.4, so this class
* does a no-op when used with Java 1.3. The class maintains
* compatability with Java 1.3 by accessing the java.nio classes
* using reflection.
*
* @author John Lewis
* @author Mark Doliner
*/
public class FileLocker {
/**
* An object of type FileLock, created using reflection.
*/
private Object lock = null;
/**
* An object of type FileChannel, created using reflection.
*/
private Object lockChannel = null;
/**
* A file called "filename.lock" that resides in the same directory
* as "filename"
*/
private File lockFile;
public FileLocker(File file) {
String lockFileName = file.getName() + ".lock";
File parent = file.getParentFile();
if (parent == null) {
lockFile = new File(lockFileName);
} else {
lockFile = new File(parent, lockFileName);
}
}
/**
* Obtains a lock on the file. This blocks until the lock is obtained.
*/
public boolean lock() {
String useNioProperty = System.getProperty("cobertura.use.java.nio");
if (System.getProperty("java.version").startsWith("1.3")
|| ((useNioProperty != null) && useNioProperty
.equalsIgnoreCase("false"))) {
return true;
}
try {
Class aClass = Class.forName("java.io.RandomAccessFile");
Method method = aClass.getDeclaredMethod("getChannel",
(Class[]) null);
lockChannel = method.invoke(new RandomAccessFile(lockFile, "rw"),
(Object[]) null);
} catch (FileNotFoundException e) {
System.err.println("Unable to get lock channel for "
+ lockFile.getAbsolutePath() + ": "
+ e.getLocalizedMessage());
return false;
} catch (InvocationTargetException e) {
System.err.println("Unable to get lock channel for "
+ lockFile.getAbsolutePath() + ": "
+ e.getLocalizedMessage());
return false;
} catch (Throwable t) {
System.err
.println("Unable to execute RandomAccessFile.getChannel() using reflection: "
+ t.getLocalizedMessage());
t.printStackTrace();
}
try {
Class aClass = Class.forName("java.nio.channels.FileChannel");
Method method = aClass.getDeclaredMethod("lock", (Class[]) null);
lock = method.invoke(lockChannel, (Object[]) null);
} catch (InvocationTargetException e) {
System.err.println("---------------------------------------");
e.printStackTrace(System.err);
System.err.println("---------------------------------------");
System.err.println("Unable to get lock on "
+ lockFile.getAbsolutePath() + ": "
+ e.getLocalizedMessage());
System.err
.println("This is known to happen on Linux kernel 2.6.20.");
System.err
.println("Make sure cobertura.jar is in the root classpath of the jvm ");
System.err
.println("process running the instrumented code. If the instrumented code ");
System.err
.println("is running in a web server, this means cobertura.jar should be in ");
System.err.println("the web server's lib directory.");
System.err
.println("Don't put multiple copies of cobertura.jar in different WEB-INF/lib directories.");
System.err
.println("Only one classloader should load cobertura. It should be the root classloader.");
System.err.println("---------------------------------------");
return false;
} catch (Throwable t) {
System.err
.println("Unable to execute FileChannel.lock() using reflection: "
+ t.getLocalizedMessage());
t.printStackTrace();
}
return true;
}
/**
* Releases the lock on the file.
*/
public void release() {
if (lock != null)
lock = releaseFileLock(lock);
if (lockChannel != null)
lockChannel = closeChannel(lockChannel);
if (!lockFile.delete()) {
System.err.println("lock file could not be deleted");
}
}
private static Object releaseFileLock(Object lock) {
try {
Class aClass = Class.forName("java.nio.channels.FileLock");
Method method = aClass.getDeclaredMethod("isValid", (Class[]) null);
if (((Boolean) method.invoke(lock, (Object[]) null)).booleanValue()) {
method = aClass.getDeclaredMethod("release", (Class[]) null);
method.invoke(lock, (Object[]) null);
lock = null;
}
} catch (Throwable t) {
System.err.println("Unable to release locked file: "
+ t.getLocalizedMessage());
}
return lock;
}
private static Object closeChannel(Object channel) {
try {
Class aClass = Class
.forName("java.nio.channels.spi.AbstractInterruptibleChannel");
Method method = aClass.getDeclaredMethod("isOpen", (Class[]) null);
if (((Boolean) method.invoke(channel, (Object[]) null))
.booleanValue()) {
method = aClass.getDeclaredMethod("close", (Class[]) null);
method.invoke(channel, (Object[]) null);
channel = null;
}
} catch (Throwable t) {
System.err.println("Unable to close file channel: "
+ t.getLocalizedMessage());
}
return channel;
}
}