001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.activemq.util;
018    
019    import java.io.File;
020    import java.io.IOException;
021    import java.io.RandomAccessFile;
022    import java.nio.channels.FileLock;
023    import java.nio.channels.OverlappingFileLockException;
024    import java.util.Date;
025    
026    /**
027     * Used to lock a File.
028     *
029     * @author chirino
030     */
031    public class LockFile {
032    
033        private static final boolean DISABLE_FILE_LOCK = Boolean.getBoolean("java.nio.channels.FileLock.broken");
034        final private File file;
035    
036        private FileLock lock;
037        private RandomAccessFile readFile;
038        private int lockCounter;
039        private final boolean deleteOnUnlock;
040    
041        public LockFile(File file, boolean deleteOnUnlock) {
042            this.file = file;
043            this.deleteOnUnlock = deleteOnUnlock;
044        }
045    
046        /**
047         * @throws IOException
048         */
049        synchronized public void lock() throws IOException {
050            if (DISABLE_FILE_LOCK) {
051                return;
052            }
053    
054            if (lockCounter > 0) {
055                return;
056            }
057    
058            IOHelper.mkdirs(file.getParentFile());
059            synchronized (LockFile.class) {
060                if (System.getProperty(getVmLockKey()) != null) {
061                    throw new IOException("File '" + file + "' could not be locked as lock is already held for this jvm.");
062                }
063                System.setProperty(getVmLockKey(), new Date().toString());
064            }
065            try {
066                if (lock == null) {
067                    readFile = new RandomAccessFile(file, "rw");
068                    IOException reason = null;
069                    try {
070                        lock = readFile.getChannel().tryLock(0, Math.max(1, readFile.getChannel().size()), false);
071                    } catch (OverlappingFileLockException e) {
072                        reason = IOExceptionSupport.create("File '" + file + "' could not be locked.", e);
073                    } catch (IOException ioe) {
074                        reason = ioe;
075                    }
076                    if (lock != null) {
077                        lockCounter++;
078                        System.setProperty(getVmLockKey(), new Date().toString());
079                    } else {
080                        // new read file for next attempt
081                        closeReadFile();
082                        if (reason != null) {
083                            throw reason;
084                        }
085                        throw new IOException("File '" + file + "' could not be locked.");
086                    }
087    
088                }
089            } finally {
090                synchronized (LockFile.class) {
091                    if (lock == null) {
092                        System.getProperties().remove(getVmLockKey());
093                    }
094                }
095            }
096        }
097    
098        /**
099         */
100        public void unlock() {
101            if (DISABLE_FILE_LOCK) {
102                return;
103            }
104    
105            lockCounter--;
106            if (lockCounter != 0) {
107                return;
108            }
109    
110            // release the lock..
111            if (lock != null) {
112                try {
113                    lock.release();
114                    System.getProperties().remove(getVmLockKey());
115                } catch (Throwable ignore) {
116                }
117                lock = null;
118            }
119            closeReadFile();
120    
121            if (deleteOnUnlock) {
122                file.delete();
123            }
124        }
125    
126        private String getVmLockKey() throws IOException {
127            return getClass().getName() + ".lock." + file.getCanonicalPath();
128        }
129    
130        private void closeReadFile() {
131            // close the file.
132            if (readFile != null) {
133                try {
134                    readFile.close();
135                } catch (Throwable ignore) {
136                }
137                readFile = null;
138            }
139    
140        }
141    
142    }