001/* Cobertura - http://cobertura.sourceforge.net/ 002 * 003 * Copyright (C) 2006 John Lewis 004 * Copyright (C) 2006 Mark Doliner 005 * Copyright (C) 2009 Chris van Es 006 * 007 * Note: This file is dual licensed under the GPL and the Apache 008 * Source License 1.1 (so that it can be used from both the main 009 * Cobertura classes and the ant tasks). 010 * 011 * Cobertura is free software; you can redistribute it and/or modify 012 * it under the terms of the GNU General Public License as published 013 * by the Free Software Foundation; either version 2 of the License, 014 * or (at your option) any later version. 015 * 016 * Cobertura is distributed in the hope that it will be useful, but 017 * WITHOUT ANY WARRANTY; without even the implied warranty of 018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 * General Public License for more details. 020 * 021 * You should have received a copy of the GNU General Public License 022 * along with Cobertura; if not, write to the Free Software 023 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 024 * USA 025 */ 026 027package net.sourceforge.cobertura.util; 028 029import java.io.File; 030import java.io.FileNotFoundException; 031import java.io.RandomAccessFile; 032import java.lang.reflect.InvocationTargetException; 033import java.lang.reflect.Method; 034 035/** 036 * This class controls access to any file so that multiple JVMs will 037 * not be able to write to the file at the same time. 038 * 039 * A file called "filename.lock" is created and Java's FileLock class 040 * is used to lock the file. 041 * 042 * The java.nio classes were introduced in Java 1.4, so this class 043 * does a no-op when used with Java 1.3. The class maintains 044 * compatability with Java 1.3 by accessing the java.nio classes 045 * using reflection. 046 * 047 * @author John Lewis 048 * @author Mark Doliner 049 */ 050public class FileLocker 051{ 052 053 /** 054 * An object of type FileLock, created using reflection. 055 */ 056 private Object lock = null; 057 058 /** 059 * An object of type FileChannel, created using reflection. 060 */ 061 private Object lockChannel = null; 062 063 /** 064 * A file called "filename.lock" that resides in the same directory 065 * as "filename" 066 */ 067 private File lockFile; 068 069 public FileLocker(File file) 070 { 071 String lockFileName = file.getName() + ".lock"; 072 File parent = file.getParentFile(); 073 if (parent == null) 074 { 075 lockFile = new File(lockFileName); 076 } 077 else 078 { 079 lockFile = new File(parent, lockFileName); 080 } 081 } 082 083 /** 084 * Obtains a lock on the file. This blocks until the lock is obtained. 085 */ 086 public boolean lock() 087 { 088 String useNioProperty = System.getProperty("cobertura.use.java.nio"); 089 if (System.getProperty("java.version").startsWith("1.3") || 090 ((useNioProperty != null) && useNioProperty.equalsIgnoreCase("false"))) 091 { 092 return true; 093 } 094 095 try 096 { 097 Class aClass = Class.forName("java.io.RandomAccessFile"); 098 Method method = aClass.getDeclaredMethod("getChannel", (Class[])null); 099 lockChannel = method.invoke(new RandomAccessFile(lockFile, "rw"), (Object[])null); 100 } 101 catch (FileNotFoundException e) 102 { 103 System.err.println("Unable to get lock channel for " + lockFile.getAbsolutePath() 104 + ": " + e.getLocalizedMessage()); 105 return false; 106 } 107 catch (InvocationTargetException e) 108 { 109 System.err.println("Unable to get lock channel for " + lockFile.getAbsolutePath() 110 + ": " + e.getLocalizedMessage()); 111 return false; 112 } 113 catch (Throwable t) 114 { 115 System.err.println("Unable to execute RandomAccessFile.getChannel() using reflection: " 116 + t.getLocalizedMessage()); 117 t.printStackTrace(); 118 } 119 120 try 121 { 122 Class aClass = Class.forName("java.nio.channels.FileChannel"); 123 Method method = aClass.getDeclaredMethod("lock", (Class[])null); 124 lock = method.invoke(lockChannel, (Object[])null); 125 } 126 catch (InvocationTargetException e) 127 { 128 System.err.println("---------------------------------------"); 129 e.printStackTrace(System.err); 130 System.err.println("---------------------------------------"); 131 System.err.println("Unable to get lock on " + lockFile.getAbsolutePath() + ": " 132 + e.getLocalizedMessage()); 133 System.err.println("This is known to happen on Linux kernel 2.6.20."); 134 System.err.println("Make sure cobertura.jar is in the root classpath of the jvm "); 135 System.err.println("process running the instrumented code. If the instrumented code "); 136 System.err.println("is running in a web server, this means cobertura.jar should be in "); 137 System.err.println("the web server's lib directory."); 138 System.err.println("Don't put multiple copies of cobertura.jar in different WEB-INF/lib directories."); 139 System.err.println("Only one classloader should load cobertura. It should be the root classloader."); 140 System.err.println("---------------------------------------"); 141 return false; 142 } 143 catch (Throwable t) 144 { 145 System.err.println("Unable to execute FileChannel.lock() using reflection: " 146 + t.getLocalizedMessage()); 147 t.printStackTrace(); 148 } 149 150 return true; 151 } 152 153 /** 154 * Releases the lock on the file. 155 */ 156 public void release() 157 { 158 if (lock != null) 159 lock = releaseFileLock(lock); 160 161 if (lockChannel != null) 162 lockChannel = closeChannel(lockChannel); 163 if (!lockFile.delete()) 164 { 165 System.err.println("lock file could not be deleted"); 166 } 167 } 168 169 private static Object releaseFileLock(Object lock) 170 { 171 try 172 { 173 Class aClass = Class.forName("java.nio.channels.FileLock"); 174 Method method = aClass.getDeclaredMethod("isValid", (Class[])null); 175 if (((Boolean)method.invoke(lock, (Object[])null)).booleanValue()) 176 { 177 method = aClass.getDeclaredMethod("release", (Class[])null); 178 method.invoke(lock, (Object[])null); 179 lock = null; 180 } 181 } 182 catch (Throwable t) 183 { 184 System.err.println("Unable to release locked file: " + t.getLocalizedMessage()); 185 } 186 return lock; 187 } 188 189 private static Object closeChannel(Object channel) 190 { 191 try 192 { 193 Class aClass = Class.forName("java.nio.channels.spi.AbstractInterruptibleChannel"); 194 Method method = aClass.getDeclaredMethod("isOpen", (Class[])null); 195 if (((Boolean)method.invoke(channel, (Object[])null)).booleanValue()) 196 { 197 method = aClass.getDeclaredMethod("close", (Class[])null); 198 method.invoke(channel, (Object[])null); 199 channel = null; 200 } 201 } 202 catch (Throwable t) 203 { 204 System.err.println("Unable to close file channel: " + t.getLocalizedMessage()); 205 } 206 return channel; 207 } 208 209}