BATCHEE-151 fix race condition when exploding apps
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
index 2784943..d39d8b2 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
@@ -31,14 +31,18 @@
import javax.batch.runtime.BatchRuntime;
import java.io.File;
import java.io.FileFilter;
+import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
import java.util.Collection;
import java.util.LinkedList;
import static java.lang.Thread.currentThread;
+import static java.lang.Thread.sleep;
/**
* base class handling:
@@ -255,13 +259,10 @@
final File timestamp = new File(exploded, "timestamp.txt");
long ts = Long.MIN_VALUE;
+
if (exploded.exists()) {
if (timestamp.exists()) {
- try {
- ts = Long.parseLong(FileUtils.readFileToString(timestamp).trim());
- } catch (final IOException e) {
- ts = Long.MIN_VALUE;
- }
+ ts = getTimestampFromFile(timestamp);
}
}
@@ -300,6 +301,16 @@
return classLoader;
}
+ private static long getTimestampFromFile(File timestamp) {
+ long ts;
+ try {
+ ts = Long.parseLong(FileUtils.readFileToString(timestamp).trim());
+ } catch (final IOException e) {
+ ts = Long.MIN_VALUE;
+ }
+ return ts;
+ }
+
private static void addFolderIfExist(final File file, final Collection<URL> urls) throws MalformedURLException {
if (file.isDirectory()) {
urls.add(file.toURI().toURL());
@@ -364,12 +375,39 @@
}
private static void explode(final File source, final File target, final File timestampFile, final long time) {
- try {
- FileUtils.deleteDirectory(target);
- Zips.unzip(source, target);
- FileUtils.write(timestampFile, Long.toString(time));
+ FileLock lock = null;
+ final File lockFile = new File(target.getName() + ".batchee.lock");
+ try (FileOutputStream fileOutputStream = new FileOutputStream(lockFile)) {
+ FileChannel channel = fileOutputStream.getChannel();
+
+ lock = channel.tryLock();
+ for (int i=0; i < 60 && lock == null; i++) { // 3 seconds
+ try {
+ sleep(50L);
+ } catch (InterruptedException e) {
+ // all fine
+ }
+ lock = channel.tryLock();
+ }
+ if (lock == null) {
+ // no lock could be aquired, lets give up.
+ throw new RuntimeException("could not aquire lock for unpacking library " + source.getName());
+ }
+
+ // check timestamp again
+ long ts = getTimestampFromFile(timestampFile);
+ if (ts == Long.MIN_VALUE || ts < source.lastModified()) {
+ FileUtils.deleteDirectory(target);
+ Zips.unzip(source, target);
+ FileUtils.write(timestampFile, Long.toString(time));
+ }
+ lock.release();
} catch (final IOException e) {
// no-op
+ } finally {
+ if (lockFile.exists()) {
+ lockFile.delete();
+ }
}
}