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 }