001 /* Cobertura - http://cobertura.sourceforge.net/ 002 * 003 * Copyright (C) 2006 John Lewis 004 * Copyright (C) 2006 Mark Doliner 005 * 006 * Note: This file is dual licensed under the GPL and the Apache 007 * Source License 1.1 (so that it can be used from both the main 008 * Cobertura classes and the ant tasks). 009 * 010 * Cobertura is free software; you can redistribute it and/or modify 011 * it under the terms of the GNU General Public License as published 012 * by the Free Software Foundation; either version 2 of the License, 013 * or (at your option) any later version. 014 * 015 * Cobertura is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of 017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 018 * General Public License for more details. 019 * 020 * You should have received a copy of the GNU General Public License 021 * along with Cobertura; if not, write to the Free Software 022 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 023 * USA 024 */ 025 026 package net.sourceforge.cobertura.util; 027 028 import java.io.File; 029 import java.io.FileNotFoundException; 030 import java.io.RandomAccessFile; 031 import java.lang.reflect.InvocationTargetException; 032 import java.lang.reflect.Method; 033 034 /** 035 * This class controls access to any file so that multiple JVMs will 036 * not be able to write to the file at the same time. 037 * 038 * A file called "filename.lock" is created and Java's FileLock class 039 * is used to lock the file. 040 * 041 * The java.nio classes were introduced in Java 1.4, so this class 042 * does a no-op when used with Java 1.3. The class maintains 043 * compatability with Java 1.3 by accessing the java.nio classes 044 * using reflection. 045 * 046 * @author John Lewis 047 * @author Mark Doliner 048 */ 049 public class FileLocker 050 { 051 052 /** 053 * An object of type FileLock, created using reflection. 054 */ 055 private Object lock = null; 056 057 /** 058 * An object of type FileChannel, created using reflection. 059 */ 060 private Object lockChannel = null; 061 062 /** 063 * A file called "filename.lock" that resides in the same directory 064 * as "filename" 065 */ 066 private File lockFile; 067 068 public FileLocker(File file) 069 { 070 String lockFileName = file.getName() + ".lock"; 071 File parent = file.getParentFile(); 072 if (parent == null) 073 { 074 lockFile = new File(lockFileName); 075 } 076 else 077 { 078 lockFile = new File(parent, lockFileName); 079 } 080 lockFile.deleteOnExit(); 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 if (lockChannel != null) 161 lockChannel = closeChannel(lockChannel); 162 lockFile.delete(); 163 } 164 165 private static Object releaseFileLock(Object lock) 166 { 167 try 168 { 169 Class aClass = Class.forName("java.nio.channels.FileLock"); 170 Method method = aClass.getDeclaredMethod("isValid", (Class[])null); 171 if (((Boolean)method.invoke(lock, (Object[])null)).booleanValue()) 172 { 173 method = aClass.getDeclaredMethod("release", (Class[])null); 174 method.invoke(lock, (Object[])null); 175 lock = null; 176 } 177 } 178 catch (Throwable t) 179 { 180 System.err.println("Unable to release locked file: " + t.getLocalizedMessage()); 181 } 182 return lock; 183 } 184 185 private static Object closeChannel(Object channel) 186 { 187 try 188 { 189 Class aClass = Class.forName("java.nio.channels.spi.AbstractInterruptibleChannel"); 190 Method method = aClass.getDeclaredMethod("isOpen", (Class[])null); 191 if (((Boolean)method.invoke(channel, (Object[])null)).booleanValue()) 192 { 193 method = aClass.getDeclaredMethod("close", (Class[])null); 194 method.invoke(channel, (Object[])null); 195 channel = null; 196 } 197 } 198 catch (Throwable t) 199 { 200 System.err.println("Unable to close file channel: " + t.getLocalizedMessage()); 201 } 202 return channel; 203 } 204 205 }