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 }