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: * An object of type FileLock, created using reflection.
053: */
054: private Object lock = null;
055:
056: /**
057: * An object of type FileChannel, created using reflection.
058: */
059: private Object lockChannel = null;
060:
061: /**
062: * A file called "filename.lock" that resides in the same directory
063: * as "filename"
064: */
065: private File lockFile;
066:
067: public FileLocker(File file) {
068: String lockFileName = file.getName() + ".lock";
069: File parent = file.getParentFile();
070: if (parent == null) {
071: lockFile = new File(lockFileName);
072: } else {
073: lockFile = new File(parent, lockFileName);
074: }
075: lockFile.deleteOnExit();
076: }
077:
078: /**
079: * Obtains a lock on the file. This blocks until the lock is obtained.
080: */
081: public boolean lock() {
082: String useNioProperty = System
083: .getProperty("cobertura.use.java.nio");
084: if (System.getProperty("java.version").startsWith("1.3")
085: || ((useNioProperty != null) && useNioProperty
086: .equalsIgnoreCase("false"))) {
087: return true;
088: }
089:
090: try {
091: Class aClass = Class.forName("java.io.RandomAccessFile");
092: Method method = aClass.getDeclaredMethod("getChannel",
093: (Class[]) null);
094: lockChannel = method.invoke(new RandomAccessFile(lockFile,
095: "rw"), (Object[]) null);
096: } catch (FileNotFoundException e) {
097: System.err.println("Unable to get lock channel for "
098: + lockFile.getAbsolutePath() + ": "
099: + e.getLocalizedMessage());
100: return false;
101: } catch (InvocationTargetException e) {
102: System.err.println("Unable to get lock channel for "
103: + lockFile.getAbsolutePath() + ": "
104: + e.getLocalizedMessage());
105: return false;
106: } catch (Throwable t) {
107: System.err
108: .println("Unable to execute RandomAccessFile.getChannel() using reflection: "
109: + t.getLocalizedMessage());
110: t.printStackTrace();
111: }
112:
113: try {
114: Class aClass = Class
115: .forName("java.nio.channels.FileChannel");
116: Method method = aClass.getDeclaredMethod("lock",
117: (Class[]) null);
118: lock = method.invoke(lockChannel, (Object[]) null);
119: } catch (InvocationTargetException e) {
120: System.err
121: .println("---------------------------------------");
122: e.printStackTrace(System.err);
123: System.err
124: .println("---------------------------------------");
125: System.err.println("Unable to get lock on "
126: + lockFile.getAbsolutePath() + ": "
127: + e.getLocalizedMessage());
128: System.err
129: .println("This is known to happen on Linux kernel 2.6.20.");
130: System.err
131: .println("Make sure cobertura.jar is in the root classpath of the jvm ");
132: System.err
133: .println("process running the instrumented code. If the instrumented code ");
134: System.err
135: .println("is running in a web server, this means cobertura.jar should be in ");
136: System.err.println("the web server's lib directory.");
137: System.err
138: .println("Don't put multiple copies of cobertura.jar in different WEB-INF/lib directories.");
139: System.err
140: .println("Only one classloader should load cobertura. It should be the root classloader.");
141: System.err
142: .println("---------------------------------------");
143: return false;
144: } catch (Throwable t) {
145: System.err
146: .println("Unable to execute FileChannel.lock() using reflection: "
147: + t.getLocalizedMessage());
148: t.printStackTrace();
149: }
150:
151: return true;
152: }
153:
154: /**
155: * Releases the lock on the file.
156: */
157: public void release() {
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: try {
167: Class aClass = Class.forName("java.nio.channels.FileLock");
168: Method method = aClass.getDeclaredMethod("isValid",
169: (Class[]) null);
170: if (((Boolean) method.invoke(lock, (Object[]) null))
171: .booleanValue()) {
172: method = aClass.getDeclaredMethod("release",
173: (Class[]) null);
174: method.invoke(lock, (Object[]) null);
175: lock = null;
176: }
177: } catch (Throwable t) {
178: System.err.println("Unable to release locked file: "
179: + t.getLocalizedMessage());
180: }
181: return lock;
182: }
183:
184: private static Object closeChannel(Object channel) {
185: try {
186: Class aClass = Class
187: .forName("java.nio.channels.spi.AbstractInterruptibleChannel");
188: Method method = aClass.getDeclaredMethod("isOpen",
189: (Class[]) null);
190: if (((Boolean) method.invoke(channel, (Object[]) null))
191: .booleanValue()) {
192: method = aClass.getDeclaredMethod("close",
193: (Class[]) null);
194: method.invoke(channel, (Object[]) null);
195: channel = null;
196: }
197: } catch (Throwable t) {
198: System.err.println("Unable to close file channel: "
199: + t.getLocalizedMessage());
200: }
201: return channel;
202: }
203:
204: }
|