From 6ba6bb4037834556c8244a52e661473f5abfc183 Mon Sep 17 00:00:00 2001 From: Colin Surprenant Date: Wed, 1 Feb 2017 14:57:02 -0500 Subject: [PATCH] support releaseLock added usage comment --- .../java/org/logstash/FileLockFactory.java | 29 ++++++++++++-- .../org/logstash/FileLockFactoryTest.java | 40 ++++++++++++++++++- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/logstash-core/src/main/java/org/logstash/FileLockFactory.java b/logstash-core/src/main/java/org/logstash/FileLockFactory.java index b26f52e72..58f7fd757 100644 --- a/logstash-core/src/main/java/org/logstash/FileLockFactory.java +++ b/logstash-core/src/main/java/org/logstash/FileLockFactory.java @@ -27,9 +27,21 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; +/** + * FileLockFactory provides a way to obtain an exclusive file lock for a given dir path and lock name. + * The obtainLock() method will return a Filelock object which should be released using the releaseLock() + * method. Normally the returned FileLock object should not be manipulated directly. Only the obtainLock() + * and releaseLock() methods should be use to gain and release exclusive access. + * This is threadsafe and will work across threads on the same JVM and will also work across processes/JVM. + * + * TODO: because of the releaseLock() method, strictly speaking this class is not only a factory anymore, + * maybe we should rename it FileLockManager? + */ public class FileLockFactory { /** @@ -39,7 +51,8 @@ public class FileLockFactory { private FileLockFactory() {} - private static final Set LOCK_HELD = Collections.synchronizedSet(new HashSet()); + private static final Set LOCK_HELD = Collections.synchronizedSet(new HashSet<>()); + private static final Map LOCK_MAP = Collections.synchronizedMap(new HashMap<>()); public static final FileLockFactory getDefault() { return FileLockFactory.INSTANCE; @@ -71,6 +84,7 @@ public class FileLockFactory { channel = FileChannel.open(realLockPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE); lock = channel.tryLock(); if (lock != null) { + LOCK_MAP.put(lock, realLockPath.toString()); return lock; } else { throw new LockException("Lock held by another program: " + realLockPath); @@ -85,8 +99,8 @@ public class FileLockFactory { // suppress any channel close exceptions } - boolean remove = LOCK_HELD.remove(realLockPath.toString()); - if (remove == false) { + boolean removed = LOCK_HELD.remove(realLockPath.toString()); + if (removed == false) { throw new LockException("Lock path was cleared but never marked as held: " + realLockPath); } } @@ -95,4 +109,13 @@ public class FileLockFactory { throw new LockException("Lock held by this virtual machine: " + realLockPath); } } + + public void releaseLock(FileLock lock) throws IOException { + String lockPath = LOCK_MAP.remove(lock); + if (lockPath == null) { throw new LockException("Cannot release unobtained lock"); } + lock.release(); + Boolean removed = LOCK_HELD.remove(lockPath); + if (removed == false) { throw new LockException("Lock path was not marked as held: " + lockPath); } + } + } diff --git a/logstash-core/src/test/java/org/logstash/FileLockFactoryTest.java b/logstash-core/src/test/java/org/logstash/FileLockFactoryTest.java index d0b5cd3f1..f11c97dd2 100644 --- a/logstash-core/src/test/java/org/logstash/FileLockFactoryTest.java +++ b/logstash-core/src/test/java/org/logstash/FileLockFactoryTest.java @@ -22,6 +22,8 @@ public class FileLockFactoryTest { private String lockDir; private final String LOCK_FILE = ".test"; + private FileLock lock; + @Before public void setUp() throws Exception { lockDir = temporaryFolder.newFolder("lock").getPath(); @@ -29,7 +31,7 @@ public class FileLockFactoryTest { @Before public void lockFirst() throws Exception { - FileLock lock = FileLockFactory.getDefault().obtainLock(lockDir, LOCK_FILE); + lock = FileLockFactory.getDefault().obtainLock(lockDir, LOCK_FILE); assertThat(lock.isValid(), is(equalTo(true))); assertThat(lock.isShared(), is(equalTo(false))); } @@ -50,4 +52,40 @@ public class FileLockFactoryTest { assertThat(lock2.isValid(), is(equalTo(true))); assertThat(lock2.isShared(), is(equalTo(false))); } + + @Test + public void LockReleaseLock() throws IOException { + FileLockFactory.getDefault().releaseLock(lock); + } + + @Test + public void LockReleaseLockObtainLock() throws IOException { + FileLockFactory.getDefault().releaseLock(lock); + + FileLock lock2 = FileLockFactory.getDefault().obtainLock(lockDir, LOCK_FILE); + assertThat(lock2.isValid(), is(equalTo(true))); + assertThat(lock2.isShared(), is(equalTo(false))); + } + + @Test + public void LockReleaseLockObtainLockRelease() throws IOException { + FileLockFactory.getDefault().releaseLock(lock); + + FileLock lock2 = FileLockFactory.getDefault().obtainLock(lockDir, LOCK_FILE); + assertThat(lock2.isValid(), is(equalTo(true))); + assertThat(lock2.isShared(), is(equalTo(false))); + + FileLockFactory.getDefault().releaseLock(lock2); + } + + @Test(expected = LockException.class) + public void ReleaseNullLock() throws IOException { + FileLockFactory.getDefault().releaseLock(null); + } + + @Test(expected = LockException.class) + public void ReleaseUnobtainedLock() throws IOException { + FileLockFactory.getDefault().releaseLock(lock); + FileLockFactory.getDefault().releaseLock(lock); + } }