/* 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; } }