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 } 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 }