Former-commit-id: 0c024f815e7a766b99473f40086f2cfeb4f97aa8
This commit is contained in:
Jeremy Long
2013-11-16 23:05:59 -05:00
parent 29d77b2f2c
commit d56e0b0eba
4 changed files with 412 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
/*
* This file is part of dependency-check-core.
*
* Dependency-check-core is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* Dependency-check-core is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.concurrency;
/**
* If thrown, indicates that a problem occurred when locking a directory.
*
* @author Jeremy Long (jeremy.long@owasp.org)
*/
public class DirectoryLockException extends Exception {
/**
* Default serial version UID.
*/
private static final long serialVersionUID = 1L;
/**
* Constructs a new Directory Lock Exception.
*/
public DirectoryLockException() {
super();
}
/**
* Constructs a new Directory Lock Exception.
*
* @param msg the message describing the exception
*/
public DirectoryLockException(String msg) {
super(msg);
}
/**
* Constructs a new Directory Lock Exception.
*
* @param ex the cause of the exception
*/
public DirectoryLockException(Throwable ex) {
super(ex);
}
/**
* Constructs a new Directory Lock Exception.
*
* @param msg the message describing the exception
* @param ex the cause of the exception
*/
public DirectoryLockException(String msg, Throwable ex) {
super(msg, ex);
}
}

View File

@@ -0,0 +1,267 @@
/*
* This file is part of dependency-check-core.
*
* Dependency-check-core is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* Dependency-check-core is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.concurrency;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.FileLockInterruptionException;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.OverlappingFileLockException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Implements a spin lock on a given directory. If the lock cannot be obtained,
* the process will "spin" waiting for an opportunity to obtain the lock
* requested.
*
* @author Jeremy Long (jeremy.long@owasp.org)
*/
public class DirectorySpinLock implements Closeable /*, AutoCloseable*/ {
/**
* The name of the lock file.
*/
public static final String LOCK_NAME = "data.lock";
/**
* The maximum wait period used when attempting to obtain a lock.
*/
public static final int MAX_SPIN = 100;
/**
* The file channel used to perform the lock.
*/
private FileChannel channel = null;
/**
* The file used to perform the lock.
*/
private File lockFile = null;
/**
* The lock object.
*/
private FileLock lock = null;
/**
* The maximum number of seconds that the spin lock will wait while trying
* to obtain a lock.
*/
private long maxWait = MAX_SPIN;
/**
* Get the maximum wait time, in seconds, that the spin lock will wait while
* trying to obtain a lock.
*
* @return the number of seconds the spin lock will wait
*/
public long getMaxWait() {
return maxWait / 2; //sleep is for 500, so / 2
}
/**
* Set the maximum wait time, in seconds, that the spin lock will wait while
* trying to obtain a lock.
*
* @param maxWait the number of seconds the spin lock will wait
*/
public void setMaxWait(long maxWait) {
this.maxWait = maxWait * 2; //sleep is for 500, so * 2
}
/**
* Constructs a new spin lock on the given directory.
*
* @param directory the directory to monitor/lock
* @throws InvalidDirectoryException thrown if there is an issue with the
* directory provided
* @throws DirectoryLockException thrown there is an issue obtaining a
* handle to the lock file
*/
public DirectorySpinLock(File directory) throws InvalidDirectoryException, DirectoryLockException {
checkDirectory(directory);
lockFile = new File(directory, LOCK_NAME);
RandomAccessFile file = null;
try {
file = new RandomAccessFile(lockFile, "rw");
} catch (FileNotFoundException ex) {
throw new DirectoryLockException("Lock file not found", ex);
}
channel = file.getChannel();
}
/**
* Attempts to obtain an exclusive lock; an exception is thrown if the lock
* could not be obtained. This method may block for a few seconds if a lock
* cannot be obtained.
*
* @throws DirectoryLockException thrown if there is an exception obtaining
* the lock
*/
public void obtainSharedLock() throws DirectoryLockException {
obtainLock(true);
}
/**
* Attempts to obtain an exclusive lock; an exception is thrown if the lock
* could not be obtained. This method may block for a few seconds if a lock
* cannot be obtained.
*
* @throws DirectoryLockException thrown if there is an exception obtaining
* the lock
*/
public void obtainExclusiveLock() throws DirectoryLockException {
obtainLock(false);
}
/**
* Attempts to obtain a lock; an exception is thrown if the lock could not
* be obtained. This method may block for a few seconds if a lock cannot be
* obtained.
*
* @param shared true if the lock is shared, otherwise false
* @param maxWait the maximum time to wait, in seconds, while trying to
* obtain the lock
* @throws DirectoryLockException thrown if there is an exception obtaining
* the lock
*/
protected void obtainLock(boolean shared, long maxWait) throws DirectoryLockException {
setMaxWait(maxWait);
obtainLock(shared);
}
/**
* Attempts to obtain a lock; an exception is thrown if the lock could not
* be obtained. This method may block for a few seconds if a lock cannot be
* obtained.
*
* @param shared true if the lock is shared, otherwise false
* @throws DirectoryLockException thrown if there is an exception obtaining
* the lock
*/
protected void obtainLock(boolean shared) throws DirectoryLockException {
if (lock != null) {
release();
}
if (channel == null) {
throw new DirectoryLockException("Unable to create lock, no file channel exists");
}
int count = 0;
Exception lastException = null;
while (lock == null && count++ < maxWait) {
try {
lock = channel.lock(0, Long.MAX_VALUE, shared);
} catch (AsynchronousCloseException ex) {
lastException = ex;
} catch (ClosedChannelException ex) {
lastException = ex;
} catch (FileLockInterruptionException ex) {
lastException = ex;
} catch (OverlappingFileLockException ex) {
lastException = ex;
} catch (NonWritableChannelException ex) {
lastException = ex;
} catch (IOException ex) {
lastException = ex;
}
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
if (lock == null) {
if (lastException == null) {
throw new DirectoryLockException("Unable to obtain lock");
} else {
throw new DirectoryLockException("Unable to obtain lock", lastException);
}
}
}
/**
* Performs a few simple rudimentary checks on the specified directory.
* Specifically, does the file exist and is it a directory.
*
* @param directory the File object to inspect
* @throws InvalidDirectoryException thrown if the directory is null or is
* not a directory
*/
private void checkDirectory(File directory) throws InvalidDirectoryException {
if (directory == null) {
throw new InvalidDirectoryException("Unable to obtain lock on a null File");
}
if (!directory.isDirectory()) {
final String msg = String.format("File, '%s', does not exist or is not a directory", directory.getAbsolutePath());
throw new InvalidDirectoryException(msg);
}
}
/**
* Releases any locks and closes the underlying channel.
*
* @throws IOException if an IO Exception occurs
*/
@Override
public void close() throws IOException {
release();
// TODO uncomment this once support for 1.6 is dropped.
// if (lock != null) {
// try {
// lock.close();
// } catch (IOException ex) {
// Logger.getLogger(DirectorySpinLock.class.getName()).log(Level.FINEST, "Unable to close file lock due to IO Exception", ex);
// }
// }
if (channel != null) {
try {
channel.close();
} catch (IOException ex) {
Logger.getLogger(DirectorySpinLock.class.getName()).log(Level.FINEST, "Unable to close the channel for the file lock", ex);
}
}
if (lockFile != null) {
if (lockFile.exists()) {
/* yes, this delete could fail which is totally fine. The other
* thread holding the lock while delete it.
*/
lockFile.delete();
}
}
}
/**
* Releases the lock. Any exceptions that are thrown by the underlying lock
* during the release are ignored.
*/
public void release() {
if (lock != null) {
try {
lock.release();
} catch (ClosedChannelException ex) {
Logger.getLogger(DirectorySpinLock.class.getName()).log(Level.FINEST, "Uable to release file lock", ex);
} catch (IOException ex) {
Logger.getLogger(DirectorySpinLock.class.getName()).log(Level.FINEST, "Unable to release file lock due to IO Exception", ex);
}
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* This file is part of dependency-check-core.
*
* Dependency-check-core is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* Dependency-check-core is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.concurrency;
/**
* If thrown, indicates that there is a problem with a directory.
*
* @author Jeremy Long (jeremy.long@owasp.org)
*/
public class InvalidDirectoryException extends Exception {
/**
* Default serial version UID.
*/
private static final long serialVersionUID = 1L;
/**
* Constructs a new Invalid Directory Exception.
*/
public InvalidDirectoryException() {
super();
}
/**
* Constructs a new Invalid Directory Exception.
*
* @param msg the message describing the exception
*/
public InvalidDirectoryException(String msg) {
super(msg);
}
/**
* Constructs a new Invalid Directory Exception.
*
* @param ex the cause of the exception
*/
public InvalidDirectoryException(Throwable ex) {
super(ex);
}
/**
* Constructs a new Invalid Directory Exception.
*
* @param msg the message describing the exception
* @param ex the cause of the exception
*/
public InvalidDirectoryException(String msg, Throwable ex) {
super(msg, ex);
}
}

View File

@@ -0,0 +1,11 @@
/**
* <html>
* <head>
* <title>org.owasp.dependencycheck.concurrency</title>
* </head>
* <body>
* Contains classes used to create shared and exclusive locks on directories.
* </body>
* </html>
*/
package org.owasp.dependencycheck.concurrency;