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