mirror of
https://github.com/elastic/logstash.git
synced 2025-04-25 07:07:54 -04:00
exclusive file locking class and tests
use new bin/ruby and bundler without :development refactor to DRY and use expected exception added original Apache 2.0 license and some cosmetics exclude bin/lock from packaging rename variables
This commit is contained in:
parent
b3906c89cd
commit
d79e3730fe
5 changed files with 174 additions and 0 deletions
9
bin/lock
Executable file
9
bin/lock
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bin/ruby
|
||||
|
||||
require_relative "../lib/bootstrap/environment"
|
||||
LogStash::Bundler.setup!({:without => [:build, :development]})
|
||||
require "logstash-core"
|
||||
|
||||
lock = Java::OrgLogstash::FileLockFactory.getDefault.obtainLock(ARGV[0], ".lock")
|
||||
puts("locking " + File.join(ARGV[0], ".lock"))
|
||||
sleep
|
|
@ -0,0 +1,98 @@
|
|||
// this class is largely inspired by Lucene FSLockFactory and friends, below is the Lucene original Apache 2.0 license:
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.logstash;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class FileLockFactory {
|
||||
|
||||
/**
|
||||
* Singleton instance
|
||||
*/
|
||||
public static final FileLockFactory INSTANCE = new FileLockFactory();
|
||||
|
||||
private FileLockFactory() {}
|
||||
|
||||
private static final Set<String> LOCK_HELD = Collections.synchronizedSet(new HashSet<String>());
|
||||
|
||||
public static final FileLockFactory getDefault() {
|
||||
return FileLockFactory.INSTANCE;
|
||||
}
|
||||
|
||||
public FileLock obtainLock(String lockDir, String lockName) throws IOException {
|
||||
Path dirPath = FileSystems.getDefault().getPath(lockDir);
|
||||
|
||||
// Ensure that lockDir exists and is a directory.
|
||||
// note: this will fail if lockDir is a symlink
|
||||
Files.createDirectories(dirPath);
|
||||
|
||||
Path lockPath = dirPath.resolve(lockName);
|
||||
|
||||
try {
|
||||
Files.createFile(lockPath);
|
||||
} catch (IOException ignore) {
|
||||
// we must create the file to have a truly canonical path.
|
||||
// if it's already created, we don't care. if it cant be created, it will fail below.
|
||||
}
|
||||
|
||||
// fails if the lock file does not exist
|
||||
final Path realLockPath = lockPath.toRealPath();
|
||||
|
||||
if (LOCK_HELD.add(realLockPath.toString())) {
|
||||
FileChannel channel = null;
|
||||
FileLock lock = null;
|
||||
try {
|
||||
channel = FileChannel.open(realLockPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
|
||||
lock = channel.tryLock();
|
||||
if (lock != null) {
|
||||
return lock;
|
||||
} else {
|
||||
throw new LockException("Lock held by another program: " + realLockPath);
|
||||
}
|
||||
} finally {
|
||||
if (lock == null) { // not successful - clear up and move out
|
||||
try {
|
||||
if (channel != null) {
|
||||
channel.close();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// suppress any channel close exceptions
|
||||
}
|
||||
|
||||
boolean remove = LOCK_HELD.remove(realLockPath.toString());
|
||||
if (remove == false) {
|
||||
throw new LockException("Lock path was cleared but never marked as held: " + realLockPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new LockException("Lock held by this virtual machine: " + realLockPath);
|
||||
}
|
||||
}
|
||||
}
|
13
logstash-core/src/main/java/org/logstash/LockException.java
Normal file
13
logstash-core/src/main/java/org/logstash/LockException.java
Normal file
|
@ -0,0 +1,13 @@
|
|||
package org.logstash;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LockException extends IOException {
|
||||
public LockException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public LockException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package org.logstash;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
|
||||
public class FileLockFactoryTest {
|
||||
@Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
private String lockDir;
|
||||
private final String LOCK_FILE = ".test";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
lockDir = temporaryFolder.newFolder("lock").getPath();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void lockFirst() throws Exception {
|
||||
FileLock lock = FileLockFactory.getDefault().obtainLock(lockDir, LOCK_FILE);
|
||||
assertThat(lock.isValid(), is(equalTo(true)));
|
||||
assertThat(lock.isShared(), is(equalTo(false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ObtainLockOnNonLocked() throws IOException {
|
||||
// empty to just test the lone @Before lockFirst() test
|
||||
}
|
||||
|
||||
@Test(expected = LockException.class)
|
||||
public void ObtainLockOnLocked() throws IOException {
|
||||
FileLockFactory.getDefault().obtainLock(lockDir, LOCK_FILE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ObtainLockOnOtherLocked() throws IOException {
|
||||
FileLock lock2 = FileLockFactory.getDefault().obtainLock(lockDir, ".test2");
|
||||
assertThat(lock2.isValid(), is(equalTo(true)));
|
||||
assertThat(lock2.isShared(), is(equalTo(false)));
|
||||
}
|
||||
}
|
|
@ -60,6 +60,7 @@ namespace "artifact" do
|
|||
@exclude_paths << "bin/bundle"
|
||||
@exclude_paths << "bin/rspec"
|
||||
@exclude_paths << "bin/rspec.bat"
|
||||
@exclude_paths << "bin/lock"
|
||||
|
||||
@exclude_paths
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue