/*******************************************************************************
* Copyright (c) 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.orion.locktest;
import java.io.*;
import java.text.*;
import java.util.Date;
import java.util.Random;
/**
* Tests to ensure Java file lock actually guarantees exclusive access to a file. Run this test
* concurrently on multiple compute nodes, pointing at the same file, to ensure that locks
* are respected.
*
* This test works by writing a token to the lock file while it is locked by this process. The
* test ensures that the lock is empty when acquired, and contains only the token for the
* duration that the lock is held. The file is emptied before releasing the lock. In short,
* the lock file should be non-empty IFF some process believes it has the file locked.
*/
public class FileLockTest {
private static final DateFormat DEBUG_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS");
private static final int DEFAULT_ITERATIONS = 10;
private static final String PARAM_VERBOSE = "-verbose";
private static boolean verbose = false;
private static boolean blockingAcquire(Locker lock) throws IOException {
int attempts = 0;
int timeout = 1000;
long sleepTime = 10;
while (attempts++ < timeout) {
if (lock.lock())
return true;
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
//ignore and loop
}
}
logWarn("Timeout waiting for lock after " + (timeout * sleepTime) + "ms");
return false;
}
private static void doWait(Random rand) {
try {
//wait for up to 1 second
long millis = rand.nextInt(10) * 100L;
logInfo("Waiting for " + millis + "ms");
Thread.sleep(millis);
} catch (InterruptedException e) {
//ignore
}
}
private static void logInfo(String msg) {
if (verbose)
logWarn(msg, null);
}
private static void logWarn(String msg) {
logWarn(msg, null);
}
private static void logWarn(String msg, Throwable failure) {
StringBuffer msgBuf = new StringBuffer(msg.length() + 40);
DEBUG_FORMAT.format(new Date(), msgBuf, new FieldPosition(0));
msgBuf.append('-');
msgBuf.append('[').append(Thread.currentThread()).append(']').append(msg);
System.out.println(msgBuf.toString());
if (failure != null)
failure.printStackTrace();
}
public static void main(String[] args) {
if (args.length == 0) {
logWarn("Usage: FileLockTest <filePathToLock> [iterations] [-verbose]");
return;
}
File lockFile = null;
try {
lockFile = setupLock(args[0]);
} catch (IOException e) {
logWarn("Exception during lock setup", e);
}
int iterations = DEFAULT_ITERATIONS;
if (args.length > 1) {
try {
iterations = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
//use default iterations
}
}
if (args.length > 2) {
if (args[2].equalsIgnoreCase(PARAM_VERBOSE))
verbose = true;
}
if (lockFile == null)
return;
try {
Locker lock = new Locker(lockFile);
runLockTest(lock, iterations);
} catch (IOException e) {
logWarn("Exception during lock test", e);
}
logInfo("Test complete. Deleting lock file: " + lockFile);
lockFile.delete();
}
private static void runLockTest(Locker lock, int iterations) throws IOException {
Random rand = new Random();
final int token = rand.nextInt();
logWarn("Running lock test with token: " + token + ", iterations: " + iterations);
for (int i = 0; i < iterations; i++) {
//wait random time
doWait(rand);
//acquire lock
if (!blockingAcquire(lock))
return;
logInfo("Acquired lock: " + lock + " iteration: " + i);
try {
//at the moment we acquire the lock, the file should be empty
RandomAccessFile raf = lock.getFile();
if (raf.length() != 0) {
logWarn("FAIL: lock acquired when not empty");
return;
}
//now write our token to file
logInfo("Writing to file: " + lock);
raf.seek(0);
raf.writeInt(token);
doWait(rand);
//make sure token is still there
raf.seek(0);
int readToken = raf.readInt();
if (readToken != token) {
logWarn("FAIL: lock contained token: " + readToken + " but should be: " + token);
return;
}
//finally put file back into empty state
raf.setLength(0);
} finally {
//release lock
lock.release();
logInfo("Lock released: " + lock + " iteration: " + i);
}
}
logWarn("Finished lock test with token: " + token + ", iterations: " + iterations);
}
private static File setupLock(String fileName) throws IOException {
File file = new File(fileName);
if (!file.exists()) {
logInfo("Creating lock file: " + fileName);
if (!file.createNewFile()) {
System.out.println("Failed to create lock file");
return null;
}
}
return file;
}
}