From ccbbecb4a40c5a944ac2727579f462680c96c2da Mon Sep 17 00:00:00 2001 From: gtully Date: Tue, 12 May 2015 23:19:07 +0100 Subject: [PATCH] https://issues.apache.org/jira/browse/AMQ-4705 - apply patch from ganesh murthy with thanks. Fix and test making use of last modified to track deletion and recreation events --- .../org/apache/activemq/util/LockFile.java | 34 +++++- .../store/kahadb/KahaDBDeleteLockTest.java | 108 ++++++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 activemq-unit-tests/src/test/java/org/apache/activemq/store/kahadb/KahaDBDeleteLockTest.java diff --git a/activemq-broker/src/main/java/org/apache/activemq/util/LockFile.java b/activemq-broker/src/main/java/org/apache/activemq/util/LockFile.java index 299fb4b983..4f99abed7d 100644 --- a/activemq-broker/src/main/java/org/apache/activemq/util/LockFile.java +++ b/activemq-broker/src/main/java/org/apache/activemq/util/LockFile.java @@ -16,11 +16,15 @@ */ package org.apache.activemq.util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; +import java.util.Calendar; import java.util.Date; /** @@ -32,6 +36,7 @@ public class LockFile { private static final boolean DISABLE_FILE_LOCK = Boolean.getBoolean("java.nio.channels.FileLock.broken"); final private File file; + private long lastModified; private FileLock lock; private RandomAccessFile readFile; @@ -39,6 +44,8 @@ public class LockFile { private final boolean deleteOnUnlock; private volatile boolean locked; + private static final Logger LOG = LoggerFactory.getLogger(LockFile.class); + public LockFile(File file, boolean deleteOnUnlock) { this.file = file; this.deleteOnUnlock = deleteOnUnlock; @@ -75,6 +82,8 @@ public class LockFile { reason = ioe; } if (lock != null) { + //Set lastModified only if we are able to successfully obtain the lock. + lastModified = file.lastModified(); lockCounter++; System.setProperty(getVmLockKey(), new Date().toString()); locked = true; @@ -138,11 +147,34 @@ public class LockFile { } readFile = null; } + } + /** + * @return true if the lock file's last modified does not match the locally cached lastModified, false otherwise + */ + private boolean hasBeenModified() { + boolean modified = false; + + //Create a new instance of the File object so we can get the most up to date information on the file. + File localFile = new File(file.getAbsolutePath()); + + if (localFile.exists()) { + if(localFile.lastModified() != lastModified) { + LOG.info("Lock file " + file.getAbsolutePath() + ", locked at " + new Date(lastModified) + ", has been modified at " + new Date(localFile.lastModified())); + modified = true; + } + } + else { + //The lock file is missing + LOG.info("Lock file " + file.getAbsolutePath() + ", does not exist"); + modified = true; + } + + return modified; } public boolean keepAlive() { - locked = locked && lock != null && lock.isValid() && file.exists(); + locked = locked && lock != null && lock.isValid() && !hasBeenModified(); return locked; } diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/store/kahadb/KahaDBDeleteLockTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/store/kahadb/KahaDBDeleteLockTest.java new file mode 100644 index 0000000000..b57a5c799c --- /dev/null +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/store/kahadb/KahaDBDeleteLockTest.java @@ -0,0 +1,108 @@ +/** + * 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.apache.activemq.store.kahadb; + +import org.apache.activemq.broker.BrokerService; +import org.apache.activemq.util.Wait; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.RandomAccessFile; +import java.util.concurrent.TimeUnit; + + +import static org.junit.Assert.assertTrue; + +public class KahaDBDeleteLockTest { + + protected BrokerService master; + + protected KahaDBPersistenceAdapter masterPersistenceAdapter = new KahaDBPersistenceAdapter(); + + private final File testDataDir = new File("target/activemq-data/KahaDBDeleteLockTest"); + private final File kahaDataDir = new File(testDataDir, "kahadb"); + + @Before + public void createMaster() throws Exception{ + master = new BrokerService(); + master.setBrokerName("Master"); + master.setDataDirectoryFile(testDataDir); + + masterPersistenceAdapter.setDirectory(kahaDataDir); + masterPersistenceAdapter.setLockKeepAlivePeriod(500); + + master.setPersistenceAdapter(masterPersistenceAdapter); + master.start(); + master.waitUntilStarted(); + } + + @After + public void stopBrokerJustInCase() throws Exception { + if (master != null) { + master.stop(); + } + } + + /** + * Deletes the lock file and makes sure that the broken stops. + * @throws Exception + */ + @Test + public void testLockFileDelete() throws Exception { + assertTrue(master.isStarted()); + + //Delete the lock file + File lockFile = new File(kahaDataDir, "lock"); + + if(lockFile.exists()) { + lockFile.delete(); + } + + assertTrue("Master stops on lock file delete", Wait.waitFor(new Wait.Condition() { + @Override + public boolean isSatisified() throws Exception { + return master.isStopped(); + } + })); + } + + /** + * Modifies the lock file so that the last modified date is not the same when the broker obtained the lock. + * This should force the broker to stop. + * @throws Exception + */ + @Test + public void testModifyLockFile() throws Exception { + assertTrue(master.isStarted()); + + // ensure modification will be seen, milisecond granularity + TimeUnit.MILLISECONDS.sleep(1); + RandomAccessFile file = new RandomAccessFile(new File(kahaDataDir, "lock"), "rw"); + file.write(4); + file.close(); + + assertTrue("Master stops on lock file modification", Wait.waitFor(new Wait.Condition() { + @Override + public boolean isSatisified() throws Exception { + return master.isStopped(); + } + }, 5000)); + + } +} \ No newline at end of file