/* 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.

A file called "filename.lock" is created and Java's FileLock class is used to lock the file.

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, Mark Doliner
/** * 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.
/** * An object of type FileLock, created using reflection. */
private Object lock = null;
An object of type FileChannel, created using reflection.
/** * 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"
/** * 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.
/** * 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.
/** * 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; } }