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.kahadb.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 = "true".equals(System.getProperty("java.nio.channels.FileLock.broken", "false"));
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            if (System.getProperty(getVmLockKey()) != null) {
060                throw new IOException("File '" + file + "' could not be locked as lock is already held for this jvm.");
061            }
062            if (lock == null) {
063                readFile = new RandomAccessFile(file, "rw");
064                IOException reason = null;
065                try {
066                    lock = readFile.getChannel().tryLock();
067                } catch (OverlappingFileLockException e) {
068                    reason = IOExceptionSupport.create("File '" + file + "' could not be locked.",e);
069                }
070                if (lock != null) {
071                    lockCounter++;
072                    System.setProperty(getVmLockKey(), new Date().toString());
073                } else {
074                    // new read file for next attempt
075                    closeReadFile();
076                    if (reason != null) {
077                        throw reason;
078                    }
079                    throw new IOException("File '" + file + "' could not be locked.");
080                }
081                  
082            }
083        }
084    
085        /**
086         */
087        public void unlock() {
088            if (DISABLE_FILE_LOCK) {
089                return;
090            }
091            
092            lockCounter--;
093            if( lockCounter!=0 ) {
094                return;
095            }
096            
097            // release the lock..
098            if (lock != null) {
099                try {
100                    lock.release();
101                    System.getProperties().remove(getVmLockKey());
102                } catch (Throwable ignore) {
103                }
104                lock = null;
105            }
106            closeReadFile();
107            
108            if( deleteOnUnlock ) {
109                file.delete();
110            }
111        }
112    
113        private String getVmLockKey() throws IOException {
114            return getClass().getName() + ".lock." + file.getCanonicalPath();
115        }
116    
117        private void closeReadFile() {
118            // close the file.
119            if (readFile != null) {
120                try {
121                    readFile.close();
122                } catch (Throwable ignore) {
123                }
124                readFile = null;
125            }
126            
127        }
128    
129    }