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    }