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    
025    /**
026     * Used to lock a File.
027     * 
028     * @author chirino
029     */
030    public class LockFile {
031        
032        private static final boolean DISABLE_FILE_LOCK = "true".equals(System.getProperty("java.nio.channels.FileLock.broken", "false"));
033        final private File file;
034        
035        private FileLock lock;
036        private RandomAccessFile readFile;
037        private int lockCounter;
038        private final boolean deleteOnUnlock;
039        
040        public LockFile(File file, boolean deleteOnUnlock) {
041            this.file = file;
042            this.deleteOnUnlock = deleteOnUnlock;
043        }
044    
045        /**
046         * @throws IOException
047         */
048        synchronized public void lock() throws IOException {
049            if (DISABLE_FILE_LOCK) {
050                return;
051            }
052    
053            if( lockCounter>0 ) {
054                return;
055            }
056            
057            IOHelper.mkdirs(file.getParentFile());
058            
059            if (lock == null) {
060                readFile = new RandomAccessFile(file, "rw");
061                IOException reason = null;
062                try {
063                    lock = readFile.getChannel().tryLock();
064                } catch (OverlappingFileLockException e) {
065                    reason = IOExceptionSupport.create("File '" + file + "' could not be locked.",e);
066                }
067                if (lock != null) {
068                    lockCounter++;
069                } else {
070                    // new read file for next attempt
071                    closeReadFile();
072                    if (reason != null) {
073                        throw reason;
074                    }
075                    throw new IOException("File '" + file + "' could not be locked.");
076                }
077                  
078            }
079        }
080    
081        /**
082         */
083        public void unlock() {
084            if (DISABLE_FILE_LOCK) {
085                return;
086            }
087            
088            lockCounter--;
089            if( lockCounter!=0 ) {
090                return;
091            }
092            
093            // release the lock..
094            if (lock != null) {
095                try {
096                    lock.release();
097                } catch (Throwable ignore) {
098                }
099                lock = null;
100            }
101            closeReadFile();
102            
103            if( deleteOnUnlock ) {
104                file.delete();
105            }
106        }
107    
108        private void closeReadFile() {
109            // close the file.
110            if (readFile != null) {
111                try {
112                    readFile.close();
113                } catch (Throwable ignore) {
114                }
115                readFile = null;
116            }
117            
118        }
119    
120    }