From f0f4f1684d083170764f7bd3a273c60f44aaa464 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Thu, 13 Aug 2015 23:40:24 -0400 Subject: [PATCH] ARTEMIS-187 hold lock between live server and tools This should avoid users damaging data while the server is running (by for instance running compact while the server is running) https://issues.apache.org/jira/browse/ARTEMIS-187 --- .../cli/commands/tools/CompactJournal.java | 2 ++ .../cli/commands/tools/DataAbstract.java | 31 +++++++++++++++++++ .../artemis/cli/commands/tools/PrintData.java | 2 ++ .../cli/commands/tools/XmlDataExporter.java | 2 ++ .../apache/activemq/cli/test/ArtemisTest.java | 9 ++++++ .../artemis/core/server/NodeManager.java | 3 ++ .../core/server/impl/ActiveMQServerImpl.java | 5 +-- .../core/server/impl/FileLockNodeManager.java | 9 ++++++ .../core/server/impl/InVMNodeManager.java | 7 +++++ .../core/server/impl/LiveOnlyActivation.java | 15 ++++++--- .../discovery/DiscoveryBaseTest.java | 6 ++++ 11 files changed, 84 insertions(+), 7 deletions(-) diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/CompactJournal.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/CompactJournal.java index af553455a28..ba0e9ceddac 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/CompactJournal.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/CompactJournal.java @@ -33,12 +33,14 @@ public final class CompactJournal extends DataAbstract implements Action { public Object execute(ActionContext context) throws Exception { super.execute(context); try { + testLock(); Configuration configuration = getFileConfiguration(); compactJournal(new File(getJournal()), "activemq-data", "amq", configuration.getJournalMinFiles(), configuration.getJournalFileSize(), null); compactJournal(new File(getBinding()), "activemq-bindings", "bindings", 2, 1048576, null); } catch (Exception e) { treatError(e, "data", "compact"); + return e; } return null; } diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataAbstract.java index c08dd448419..47edaee2b75 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataAbstract.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/DataAbstract.java @@ -18,9 +18,15 @@ package org.apache.activemq.artemis.cli.commands.tools; import java.io.File; +import java.nio.channels.FileLock; import io.airlift.airline.Option; import org.apache.activemq.artemis.cli.commands.Configurable; +import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.core.server.JournalType; +import org.apache.activemq.artemis.core.server.impl.AIOFileLockNodeManager; +import org.apache.activemq.artemis.core.server.impl.FileLockNodeManager; +import org.apache.activemq.artemis.jlibaio.LibaioContext; /** * Abstract class for places where you need bindings, journal paging and large messages configuration @@ -39,6 +45,31 @@ public abstract class DataAbstract extends Configurable { @Option(name = "--large-messages", description = "The folder used for large-messages (default from broker.xml)") public String largeMessges; + + protected void testLock() throws Exception { + + FileLockNodeManager fileLockNodeManager; + Configuration configuration = getFileConfiguration(); + if (getFileConfiguration().getJournalType() == JournalType.ASYNCIO && LibaioContext.isLoaded()) { + fileLockNodeManager = new AIOFileLockNodeManager(new File(getJournal()), false, configuration.getJournalLockAcquisitionTimeout()); + } + else { + fileLockNodeManager = new FileLockNodeManager(new File(getJournal()), false, configuration.getJournalLockAcquisitionTimeout()); + } + + fileLockNodeManager.start(); + + try (FileLock lock = fileLockNodeManager.tryLockLive()) { + if (lock == null) { + throw new RuntimeException("Server is locked!"); + } + } + finally { + fileLockNodeManager.stop(); + } + + } + public String getLargeMessages() throws Exception { if (largeMessges == null) { largeMessges = getFileConfiguration().getLargeMessagesLocation().getAbsolutePath(); diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/PrintData.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/PrintData.java index 68dc5dc9ecc..0802b13f739 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/PrintData.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/PrintData.java @@ -63,10 +63,12 @@ public class PrintData extends DataAbstract implements Action { public Object execute(ActionContext context) throws Exception { super.execute(context); try { + testLock(); printData(new File(getBinding()), new File(getJournal()), new File(getPaging())); } catch (Exception e) { treatError(e, "data", "print"); + return e; } return null; } diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/XmlDataExporter.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/XmlDataExporter.java index 0d96aae24d4..66101b9f894 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/XmlDataExporter.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/XmlDataExporter.java @@ -129,10 +129,12 @@ public Object execute(ActionContext context) throws Exception { super.execute(context); try { + testLock(); process(context.out, getBinding(), getJournal(), getPaging(), getLargeMessages()); } catch (Exception e) { treatError(e, "data", "exp"); + return e; } return null; } diff --git a/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java index cfcc53f9853..b6b5ac73ab9 100644 --- a/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java +++ b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java @@ -93,6 +93,15 @@ public void testSimpleRun() throws Exception { // Some exceptions may happen on the initialization, but they should be ok on start the basic core protocol Artemis.execute("run"); + Object object = Artemis.execute("data", "print"); + Assert.assertTrue("An error was expected", object != null && object instanceof Throwable); + + object = Artemis.execute("data", "compact"); + Assert.assertTrue("An error was expected", object != null && object instanceof Throwable); + + object = Artemis.execute("data", "exp"); + Assert.assertTrue("An error was expected", object != null && object instanceof Throwable); + try (ServerLocator locator = ServerLocatorImpl.newLocator("tcp://localhost:61616"); ClientSessionFactory factory = locator.createSessionFactory(); ClientSession coreSession = factory.createSession()) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/NodeManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/NodeManager.java index 421daaa21f0..222f0cefa94 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/NodeManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/NodeManager.java @@ -21,6 +21,7 @@ import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; import org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException; import org.apache.activemq.artemis.api.core.SimpleString; @@ -130,6 +131,8 @@ public final void stopBackup() throws Exception { releaseBackup(); } + public abstract FileLock tryLockLive(); + /** * Ensures existence of persistent information about the server's nodeID. *

diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java index 885315423e0..de1951298dc 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java @@ -347,9 +347,10 @@ public ActiveMQServerImpl(Configuration configuration, protected NodeManager createNodeManager(final File directory, boolean replicatingBackup) { NodeManager manager; if (!configuration.isPersistenceEnabled()) { - manager = new InVMNodeManager(replicatingBackup); + return new InVMNodeManager(replicatingBackup); } - else if (configuration.getJournalType() == JournalType.ASYNCIO && LibaioContext.isLoaded()) { + + if (configuration.getJournalType() == JournalType.ASYNCIO && LibaioContext.isLoaded()) { manager = new AIOFileLockNodeManager(directory, replicatingBackup, configuration.getJournalLockAcquisitionTimeout()); } else { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/FileLockNodeManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/FileLockNodeManager.java index acb431d388a..4b4134b2491 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/FileLockNodeManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/FileLockNodeManager.java @@ -73,6 +73,15 @@ public synchronized void start() throws Exception { super.start(); } + public FileLock tryLockLive() { + try { + return tryLock(FileLockNodeManager.LIVE_LOCK_POS); + } + catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + @Override public boolean isAwaitingFailback() throws Exception { return getState() == FileLockNodeManager.FAILINGBACK; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/InVMNodeManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/InVMNodeManager.java index 726cb500037..c11c810561d 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/InVMNodeManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/InVMNodeManager.java @@ -18,6 +18,7 @@ import java.io.File; import java.io.IOException; +import java.nio.channels.FileLock; import java.util.concurrent.Semaphore; import org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException; @@ -95,6 +96,12 @@ public void startBackup() throws Exception { backupLock.acquire(); } + @Override + public FileLock tryLockLive() { + // no op.. doesn't make sense on InVM + return null; + } + @Override public void startLiveNode() throws Exception { state = FAILING_BACK; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LiveOnlyActivation.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LiveOnlyActivation.java index 1c82bbf7b89..11f860c2c49 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LiveOnlyActivation.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LiveOnlyActivation.java @@ -16,6 +16,11 @@ */ package org.apache.activemq.artemis.core.server.impl; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.api.core.SimpleString; @@ -32,11 +37,6 @@ import org.apache.activemq.artemis.core.server.cluster.ha.LiveOnlyPolicy; import org.apache.activemq.artemis.core.server.cluster.ha.ScaleDownPolicy; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentMap; - public class LiveOnlyActivation extends Activation { //this is how we act when we initially start as live @@ -55,6 +55,11 @@ public LiveOnlyActivation(ActiveMQServerImpl server, LiveOnlyPolicy liveOnlyPoli public void run() { try { + + /* We will hold a lock here so print-data and other tools + * won't be able to run */ + activeMQServer.getNodeManager().startLiveNode(); + activeMQServer.initialisePart1(false); activeMQServer.initialisePart2(false); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryBaseTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryBaseTest.java index 06bc14ef7b3..e2e450bc6c5 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryBaseTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryBaseTest.java @@ -17,6 +17,7 @@ package org.apache.activemq.artemis.tests.integration.discovery; import java.net.InetAddress; +import java.nio.channels.FileLock; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -176,6 +177,11 @@ public FakeNodeManager(String nodeID) { this.setNodeID(nodeID); } + @Override + public FileLock tryLockLive() { + return null; + } + @Override public void awaitLiveNode() throws Exception { }