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
027 package net.sourceforge.cobertura.util;
028
029 import java.io.File;
030 import java.io.FileNotFoundException;
031 import java.io.RandomAccessFile;
032 import java.lang.reflect.InvocationTargetException;
033 import 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 */
050 public 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 }