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