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: */
018: package org.apache.ivy.plugins.lock;
019:
020: import java.io.File;
021: import java.io.IOException;
022: import java.io.RandomAccessFile;
023: import java.nio.channels.FileChannel;
024: import java.nio.channels.FileLock;
025: import java.util.HashMap;
026: import java.util.Map;
027:
028: import org.apache.ivy.util.Message;
029:
030: public abstract class FileBasedLockStrategy extends
031: AbstractLockStrategy {
032: private static final int SLEEP_TIME = 100;
033:
034: private static final long DEFAULT_TIMEOUT = 2 * 60 * 1000;
035:
036: /**
037: * The locker to use to make file lock attempts.
038: * <p>
039: * Two implementations of FileLocker are provided below, according to our tests the
040: * CreateFileLocker is both performing better and much more reliable than NIOFileLocker.
041: * </p>
042: */
043: private FileLocker locker;
044:
045: private long timeout = DEFAULT_TIMEOUT;
046:
047: private Map/*<File, Integer>*/currentLockCounters = new HashMap();
048:
049: protected FileBasedLockStrategy() {
050: this (new CreateFileLocker(false), false);
051: }
052:
053: protected FileBasedLockStrategy(boolean debugLocking) {
054: this (new CreateFileLocker(debugLocking), debugLocking);
055: }
056:
057: protected FileBasedLockStrategy(FileLocker locker,
058: boolean debugLocking) {
059: super (debugLocking);
060: this .locker = locker;
061: }
062:
063: protected boolean acquireLock(File file)
064: throws InterruptedException {
065: if (isDebugLocking()) {
066: debugLocking("acquiring lock on " + file);
067: }
068: long start = System.currentTimeMillis();
069: do {
070: synchronized (this ) {
071: if (hasLock(file)) {
072: int holdLocks = incrementLock(file);
073: if (isDebugLocking()) {
074: debugLocking("reentrant lock acquired on "
075: + file + " in "
076: + (System.currentTimeMillis() - start)
077: + "ms" + " - hold locks = " + holdLocks);
078: }
079: return true;
080: }
081: if (locker.tryLock(file)) {
082: if (isDebugLocking()) {
083: debugLocking("lock acquired on " + file
084: + " in "
085: + (System.currentTimeMillis() - start)
086: + "ms");
087: }
088: incrementLock(file);
089: return true;
090: }
091: }
092: Thread.sleep(SLEEP_TIME);
093: } while (System.currentTimeMillis() - start < timeout);
094: return false;
095: }
096:
097: protected void releaseLock(File file) {
098: synchronized (this ) {
099: int holdLocks = decrementLock(file);
100: if (holdLocks == 0) {
101: locker.unlock(file);
102: if (isDebugLocking()) {
103: debugLocking("lock released on " + file);
104: }
105: } else {
106: if (isDebugLocking()) {
107: debugLocking("reentrant lock released on " + file
108: + " - hold locks = " + holdLocks);
109: }
110: }
111: }
112: }
113:
114: private static void debugLocking(String msg) {
115: Message.info(Thread.currentThread() + " "
116: + System.currentTimeMillis() + " " + msg);
117: }
118:
119: private boolean hasLock(File file) {
120: Integer c = (Integer) currentLockCounters.get(file);
121: return c != null && c.intValue() > 0;
122: }
123:
124: private int incrementLock(File file) {
125: Integer c = (Integer) currentLockCounters.get(file);
126: int holdLocks = c == null ? 1 : c.intValue() + 1;
127: currentLockCounters.put(file, new Integer(holdLocks));
128: return holdLocks;
129: }
130:
131: private int decrementLock(File file) {
132: Integer c = (Integer) currentLockCounters.get(file);
133: int dc = c == null ? 0 : c.intValue() - 1;
134: currentLockCounters.put(file, new Integer(dc));
135: return dc;
136: }
137:
138: public static interface FileLocker {
139: boolean tryLock(File f);
140:
141: void unlock(File f);
142: }
143:
144: /**
145: * "locks" a file by creating it if it doesn't exist, relying on the
146: * {@link File#createNewFile()} atomicity.
147: */
148: public static class CreateFileLocker implements FileLocker {
149: private boolean debugLocking;
150:
151: public CreateFileLocker(boolean debugLocking) {
152: this .debugLocking = debugLocking;
153: }
154:
155: public boolean tryLock(File file) {
156: try {
157: if (file.getParentFile().exists()
158: || file.getParentFile().mkdirs()) {
159: if (file.createNewFile()) {
160: return true;
161: } else {
162: if (debugLocking) {
163: debugLocking("file creation failed " + file);
164: }
165: }
166: }
167: } catch (IOException e) {
168: // ignored
169: Message
170: .verbose("file creation failed due to an exception: "
171: + e.getMessage() + " (" + file + ")");
172: }
173: return false;
174: }
175:
176: public void unlock(File file) {
177: file.delete();
178: }
179: }
180:
181: /**
182: * Locks a file using the {@link FileLock} mechanism.
183: */
184: public static class NIOFileLocker implements FileLocker {
185:
186: private Map locks = new HashMap();
187: private boolean debugLocking;
188:
189: public NIOFileLocker(boolean debugLocking) {
190: this .debugLocking = debugLocking;
191: }
192:
193: public boolean tryLock(File file) {
194: try {
195: if (file.getParentFile().exists()
196: || file.getParentFile().mkdirs()) {
197: RandomAccessFile raf = new RandomAccessFile(file,
198: "rw");
199: FileChannel channel = raf.getChannel();
200: try {
201: FileLock l = channel.tryLock();
202: if (l != null) {
203: synchronized (this ) {
204: locks.put(file, l);
205: }
206: return true;
207: } else {
208: if (debugLocking) {
209: debugLocking("failed to acquire lock on "
210: + file);
211: }
212: }
213: } finally {
214: raf.close();
215: }
216: }
217: } catch (IOException e) {
218: // ignored
219: Message
220: .verbose("file lock failed due to an exception: "
221: + e.getMessage() + " (" + file + ")");
222: }
223: return false;
224: }
225:
226: public void unlock(File file) {
227: synchronized (this ) {
228: FileLock l = (FileLock) locks.get(file);
229: if (l == null) {
230: throw new IllegalArgumentException(
231: "file not previously locked: " + file);
232: }
233: try {
234: l.release();
235: } catch (IOException e) {
236: Message.error("problem while releasing lock on "
237: + file + ": " + e.getMessage());
238: }
239: }
240: }
241:
242: }
243: }
|