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    }