From b97bf8fac48e662fa9a69f484dc57e86da99cbc1 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 24 May 2018 14:09:17 -0500 Subject: [PATCH 001/207] ARTEMIS-1872 fix examples broken after authn change --- .../src/main/resources/activemq/server0/broker.xml | 1 + .../src/main/resources/activemq/server0/broker.xml | 1 + .../src/main/resources/activemq/server0/broker.xml | 8 +------- .../src/main/resources/activemq/server0/broker.xml | 1 + 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/features/standard/management/src/main/resources/activemq/server0/broker.xml b/examples/features/standard/management/src/main/resources/activemq/server0/broker.xml index 6d041c7ff1a..004faf98307 100644 --- a/examples/features/standard/management/src/main/resources/activemq/server0/broker.xml +++ b/examples/features/standard/management/src/main/resources/activemq/server0/broker.xml @@ -55,6 +55,7 @@ under the License. + diff --git a/examples/features/standard/pre-acknowledge/src/main/resources/activemq/server0/broker.xml b/examples/features/standard/pre-acknowledge/src/main/resources/activemq/server0/broker.xml index 50227dd9ff2..65ddcde9d68 100644 --- a/examples/features/standard/pre-acknowledge/src/main/resources/activemq/server0/broker.xml +++ b/examples/features/standard/pre-acknowledge/src/main/resources/activemq/server0/broker.xml @@ -37,6 +37,7 @@ under the License. + diff --git a/examples/features/standard/queue-requestor/src/main/resources/activemq/server0/broker.xml b/examples/features/standard/queue-requestor/src/main/resources/activemq/server0/broker.xml index bf9d4c873a2..f6ca85e9c67 100644 --- a/examples/features/standard/queue-requestor/src/main/resources/activemq/server0/broker.xml +++ b/examples/features/standard/queue-requestor/src/main/resources/activemq/server0/broker.xml @@ -38,6 +38,7 @@ under the License. + @@ -45,13 +46,6 @@ under the License. - - - - - - - diff --git a/examples/features/standard/temp-queue/src/main/resources/activemq/server0/broker.xml b/examples/features/standard/temp-queue/src/main/resources/activemq/server0/broker.xml index ee0398bfc56..9d00b789571 100644 --- a/examples/features/standard/temp-queue/src/main/resources/activemq/server0/broker.xml +++ b/examples/features/standard/temp-queue/src/main/resources/activemq/server0/broker.xml @@ -37,6 +37,7 @@ under the License. + From 6211fb7ad603d8020f59ea8a68172a072b9a55ba Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 24 May 2018 21:48:04 -0500 Subject: [PATCH 002/207] ARTEMIS-1882 fix failing SecurityTest --- .../tests/integration/ssl/CoreClientOverOneWaySSLTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java index 6217dbdb6e1..b9b9bc8131c 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java @@ -55,6 +55,7 @@ @RunWith(value = Parameterized.class) public class CoreClientOverOneWaySSLTest extends ActiveMQTestBase { + String suffix = ""; @Parameterized.Parameters(name = "storeType={0}") public static Collection getParameters() { @@ -63,7 +64,7 @@ public static Collection getParameters() { public CoreClientOverOneWaySSLTest(String storeType) { this.storeType = storeType; - String suffix = storeType.toLowerCase(); + suffix = storeType.toLowerCase(); // keytool expects PKCS12 stores to use the extension "p12" if (storeType.equals("PKCS12")) { suffix = "p12"; @@ -327,7 +328,7 @@ public void testOneWaySSLReloaded() throws Exception { // create an invalid SSL connection tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); tc.getParams().put(TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME, storeType); - tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, "other-client-side-truststore." + storeType.toLowerCase()); + tc.getParams().put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, "other-client-side-truststore." + suffix); tc.getParams().put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, PASSWORD); ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)).setCallTimeout(3000); @@ -340,7 +341,7 @@ public void testOneWaySSLReloaded() throws Exception { // reload the acceptor to reload the SSL stores NettyAcceptor acceptor = (NettyAcceptor) server.getRemotingService().getAcceptor("nettySSL"); - acceptor.setKeyStorePath("other-server-side-keystore." + storeType.toLowerCase()); + acceptor.setKeyStorePath("other-server-side-keystore." + suffix); acceptor.reload(); // create a session with the locator which failed previously proving that the SSL stores have been reloaded From d11eed2f9e8a5581cc0f1e2e30f0130c338c7d37 Mon Sep 17 00:00:00 2001 From: saurabhrai Date: Mon, 14 May 2018 17:59:48 +0530 Subject: [PATCH 003/207] ARTEMIS-1866 Make Quorum vote result wait time configurable. Quorum voting is used by both the live and the backup to decide what to do if a replication connection is disconnected. Basically, the server will request each live server in the cluster to vote as to whether it thinks the server it is replicating to or from is still alive. You can also configure the time for which the quorum manager will wait for the quorum vote response. Currently, the value is hardcoded as 30 sec. We should change this 30-second wait to be configurable. --- .../config/ActiveMQDefaultConfiguration.java | 7 +++ .../core/config/ConfigurationUtils.java | 9 +-- .../config/ha/ReplicaPolicyConfiguration.java | 9 ++- .../ha/ReplicatedPolicyConfiguration.java | 9 ++- .../impl/FileConfigurationParser.java | 4 +- .../core/server/ActiveMQServerLogger.java | 4 ++ .../core/server/cluster/ha/ReplicaPolicy.java | 23 ++++++-- .../server/cluster/ha/ReplicatedPolicy.java | 23 ++++++-- .../qourum/SharedNothingBackupQuorum.java | 8 ++- .../impl/SharedNothingBackupActivation.java | 2 +- .../schema/artemis-configuration.xsd | 14 +++++ docs/user-manual/en/network-isolation.md | 15 ++++- .../jms/bridge/ClusteredBridgeTestBase.java | 5 +- .../cluster/distribution/ClusterTestBase.java | 9 +-- .../AutomaticColocatedQuorumVoteTest.java | 5 +- ...tipleLivesMultipleBackupsFailoverTest.java | 5 +- .../MultipleServerFailoverTestBase.java | 5 +- .../failover/QuorumResultWaitTest.java | 58 +++++++++++++++++++ ...SingleLiveMultipleBackupsFailoverTest.java | 5 +- .../cluster/util/MultiServerTestBase.java | 5 +- .../jms/cluster/JMSFailoverTest.java | 5 +- .../SharedNothingReplicationTest.java | 5 +- .../artemis/tests/util/HAConfigUtils.java | 21 +++++++ .../tests/util/ReplicatedBackupUtils.java | 4 +- 24 files changed, 214 insertions(+), 45 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/HAConfigUtils.java diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java index c764408fc9c..70ba3149ddf 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java @@ -497,6 +497,9 @@ public static String getDefaultHapolicyBackupStrategy() { //how long we wait between votes, 5 secs private static long DEFAULT_VOTE_RETRY_WAIT = 5000; + //how long we wait for vote result, 30 secs + private static int DEFAULT_QUORUM_VOTE_WAIT = 30; + public static int DEFAULT_QUORUM_SIZE = -1; public static final boolean DEFAULT_ANALYZE_CRITICAL = true; @@ -1365,4 +1368,8 @@ public static int getDefaultVoteRetries() { public static long getDefaultVoteRetryWait() { return DEFAULT_VOTE_RETRY_WAIT; } + + public static int getDefaultQuorumVoteWait() { + return DEFAULT_QUORUM_VOTE_WAIT; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java index 7ea26ae0a80..1076611e7ce 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java @@ -19,6 +19,7 @@ import java.net.URI; import java.util.List; +import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException; import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.core.config.ha.ColocatedPolicyConfiguration; @@ -72,11 +73,11 @@ public static HAPolicy getHAPolicy(HAPolicyConfiguration conf, } case REPLICATED: { ReplicatedPolicyConfiguration pc = (ReplicatedPolicyConfiguration) conf; - return new ReplicatedPolicy(pc.isCheckForLiveServer(), pc.getGroupName(), pc.getClusterName(), pc.getInitialReplicationSyncTimeout(), server.getNetworkHealthCheck(), pc.getVoteOnReplicationFailure(), pc.getQuorumSize(), pc.getVoteRetries(), pc.getVoteRetryWait()); + return new ReplicatedPolicy(pc.isCheckForLiveServer(), pc.getGroupName(), pc.getClusterName(), pc.getInitialReplicationSyncTimeout(), server.getNetworkHealthCheck(), pc.getVoteOnReplicationFailure(), pc.getQuorumSize(), pc.getVoteRetries(), pc.getVoteRetryWait(), pc.getQuorumVoteWait()); } case REPLICA: { ReplicaPolicyConfiguration pc = (ReplicaPolicyConfiguration) conf; - return new ReplicaPolicy(pc.getClusterName(), pc.getMaxSavedReplicatedJournalsSize(), pc.getGroupName(), pc.isRestartBackup(), pc.isAllowFailBack(), pc.getInitialReplicationSyncTimeout(), getScaleDownPolicy(pc.getScaleDownConfiguration()), server.getNetworkHealthCheck(), pc.getVoteOnReplicationFailure(), pc.getQuorumSize(), pc.getVoteRetries(), pc.getVoteRetryWait()); + return new ReplicaPolicy(pc.getClusterName(), pc.getMaxSavedReplicatedJournalsSize(), pc.getGroupName(), pc.isRestartBackup(), pc.isAllowFailBack(), pc.getInitialReplicationSyncTimeout(), getScaleDownPolicy(pc.getScaleDownConfiguration()), server.getNetworkHealthCheck(), pc.getVoteOnReplicationFailure(), pc.getQuorumSize(), pc.getVoteRetries(), pc.getVoteRetryWait(), pc.getQuorumVoteWait()); } case SHARED_STORE_MASTER: { SharedStoreMasterPolicyConfiguration pc = (SharedStoreMasterPolicyConfiguration) conf; @@ -93,7 +94,7 @@ public static HAPolicy getHAPolicy(HAPolicyConfiguration conf, HAPolicy livePolicy; //if null default to colocated if (liveConf == null) { - livePolicy = new ReplicatedPolicy(server.getNetworkHealthCheck()); + livePolicy = new ReplicatedPolicy(server.getNetworkHealthCheck(),ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait()); } else { livePolicy = getHAPolicy(liveConf, server); } @@ -101,7 +102,7 @@ public static HAPolicy getHAPolicy(HAPolicyConfiguration conf, BackupPolicy backupPolicy; if (backupConf == null) { if (livePolicy instanceof ReplicatedPolicy) { - backupPolicy = new ReplicaPolicy(server.getNetworkHealthCheck()); + backupPolicy = new ReplicaPolicy(server.getNetworkHealthCheck(),ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait()); } else if (livePolicy instanceof SharedStoreMasterPolicy) { backupPolicy = new SharedStoreSlavePolicy(); } else { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java index 0e6c82d64df..1760aa33aff 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java @@ -47,7 +47,10 @@ public class ReplicaPolicyConfiguration implements HAPolicyConfiguration { private long voteRetryWait = ActiveMQDefaultConfiguration.getDefaultVoteRetryWait(); - public ReplicaPolicyConfiguration() { + private final int quorumVoteWait; + + public ReplicaPolicyConfiguration(int quorumVoteWait) { + this.quorumVoteWait = quorumVoteWait; } @Override @@ -159,4 +162,8 @@ public void setVoteRetryWait(long voteRetryWait) { public long getVoteRetryWait() { return voteRetryWait; } + + public int getQuorumVoteWait() { + return quorumVoteWait; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java index 68d69bb6187..fe12168571e 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java @@ -37,7 +37,10 @@ public class ReplicatedPolicyConfiguration implements HAPolicyConfiguration { private long voteRetryWait = ActiveMQDefaultConfiguration.getDefaultVoteRetryWait(); - public ReplicatedPolicyConfiguration() { + private final int quorumVoteWait; + + public ReplicatedPolicyConfiguration(int quorumVoteWait) { + this.quorumVoteWait = quorumVoteWait; } @Override @@ -112,4 +115,8 @@ public void setVoteRetryWait(long voteRetryWait) { public long getVoteRetryWait() { return voteRetryWait; } + + public int getQuorumVoteWait() { + return quorumVoteWait; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java index 17f3b67a4ea..d2d17633273 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java @@ -1303,7 +1303,7 @@ private LiveOnlyPolicyConfiguration createLiveOnlyHaPolicy(Element policyNode) { } private ReplicatedPolicyConfiguration createReplicatedHaPolicy(Element policyNode) { - ReplicatedPolicyConfiguration configuration = new ReplicatedPolicyConfiguration(); + ReplicatedPolicyConfiguration configuration = new ReplicatedPolicyConfiguration(getInteger(policyNode, "quorum-vote-wait", ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(), Validators.GT_ZERO)); configuration.setCheckForLiveServer(getBoolean(policyNode, "check-for-live-server", configuration.isCheckForLiveServer())); @@ -1325,7 +1325,7 @@ private ReplicatedPolicyConfiguration createReplicatedHaPolicy(Element policyNod } private ReplicaPolicyConfiguration createReplicaHaPolicy(Element policyNode) { - ReplicaPolicyConfiguration configuration = new ReplicaPolicyConfiguration(); + ReplicaPolicyConfiguration configuration = new ReplicaPolicyConfiguration(getInteger(policyNode, "quorum-vote-wait", ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(), Validators.GT_ZERO)); configuration.setRestartBackup(getBoolean(policyNode, "restart-backup", configuration.isRestartBackup())); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java index 61ae6ca248c..677f1acb310 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java @@ -1942,4 +1942,8 @@ void slowConsumerDetected(String sessionID, @LogMessage(level = Logger.Level.ERROR) @Message(id = 224093, value = "Reference to message is null", format = Message.Format.MESSAGE_FORMAT) void nullRefMessage(); + + @LogMessage(level = Logger.Level.TRACE) + @Message(id = 224094, value = "Quorum vote result await is interrupted", format = Message.Format.MESSAGE_FORMAT) + void quorumVoteAwaitInterrupted(); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicaPolicy.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicaPolicy.java index 40559cf1328..7a4b06bc042 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicaPolicy.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicaPolicy.java @@ -57,14 +57,19 @@ public class ReplicaPolicy extends BackupPolicy { private long voteRetryWait; - public ReplicaPolicy(final NetworkHealthCheck networkHealthCheck) { + private final int quorumVoteWait; + + public ReplicaPolicy(final NetworkHealthCheck networkHealthCheck, int quorumVoteWait) { this.networkHealthCheck = networkHealthCheck; + this.quorumVoteWait = quorumVoteWait; } public ReplicaPolicy(final NetworkHealthCheck networkHealthCheck, - ReplicatedPolicy replicatedPolicy) { + ReplicatedPolicy replicatedPolicy, + int quorumVoteWait) { this.networkHealthCheck = networkHealthCheck; this.replicatedPolicy = replicatedPolicy; + this.quorumVoteWait = quorumVoteWait; } public ReplicaPolicy(String clusterName, @@ -78,7 +83,8 @@ public ReplicaPolicy(String clusterName, boolean voteOnReplicationFailure, int quorumSize, int voteRetries, - long voteRetryWait) { + long voteRetryWait, + int quorumVoteWait) { this.clusterName = clusterName; this.maxSavedReplicatedJournalsSize = maxSavedReplicatedJournalsSize; this.groupName = groupName; @@ -91,18 +97,21 @@ public ReplicaPolicy(String clusterName, this.scaleDownPolicy = scaleDownPolicy; this.networkHealthCheck = networkHealthCheck; this.voteOnReplicationFailure = voteOnReplicationFailure; + this.quorumVoteWait = quorumVoteWait; } public ReplicaPolicy(String clusterName, int maxSavedReplicatedJournalsSize, String groupName, ReplicatedPolicy replicatedPolicy, - NetworkHealthCheck networkHealthCheck) { + NetworkHealthCheck networkHealthCheck, + int quorumVoteWait) { this.clusterName = clusterName; this.maxSavedReplicatedJournalsSize = maxSavedReplicatedJournalsSize; this.groupName = groupName; this.replicatedPolicy = replicatedPolicy; this.networkHealthCheck = networkHealthCheck; + this.quorumVoteWait = quorumVoteWait; } public String getClusterName() { @@ -123,7 +132,7 @@ public void setMaxSavedReplicatedJournalsSize(int maxSavedReplicatedJournalsSize public ReplicatedPolicy getReplicatedPolicy() { if (replicatedPolicy == null) { - replicatedPolicy = new ReplicatedPolicy(false, allowFailback, initialReplicationSyncTimeout, groupName, clusterName, this, networkHealthCheck, voteOnReplicationFailure, quorumSize, voteRetries, voteRetryWait); + replicatedPolicy = new ReplicatedPolicy(false, allowFailback, initialReplicationSyncTimeout, groupName, clusterName, this, networkHealthCheck, voteOnReplicationFailure, quorumSize, voteRetries, voteRetryWait, quorumVoteWait); } return replicatedPolicy; } @@ -234,4 +243,8 @@ public int getVoteRetries() { public long getVoteRetryWait() { return voteRetryWait; } + + public int getQuorumVoteWait() { + return quorumVoteWait; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicatedPolicy.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicatedPolicy.java index 135e8d0f396..e170429ac49 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicatedPolicy.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicatedPolicy.java @@ -61,9 +61,12 @@ public class ReplicatedPolicy implements HAPolicy { private final NetworkHealthCheck networkHealthCheck; - public ReplicatedPolicy(NetworkHealthCheck networkHealthCheck) { - replicaPolicy = new ReplicaPolicy(networkHealthCheck, this); + private final int quorumVoteWait; + + public ReplicatedPolicy(NetworkHealthCheck networkHealthCheck, int quorumVoteWait) { + replicaPolicy = new ReplicaPolicy(networkHealthCheck, this, quorumVoteWait); this.networkHealthCheck = networkHealthCheck; + this.quorumVoteWait = quorumVoteWait; } public ReplicatedPolicy(boolean checkForLiveServer, @@ -74,7 +77,8 @@ public ReplicatedPolicy(boolean checkForLiveServer, boolean voteOnReplicationFailure, int quorumSize, int voteRetries, - long voteRetryWait) { + long voteRetryWait, + int quorumVoteWait) { this.checkForLiveServer = checkForLiveServer; this.groupName = groupName; this.clusterName = clusterName; @@ -84,6 +88,7 @@ public ReplicatedPolicy(boolean checkForLiveServer, this.quorumSize = quorumSize; this.voteRetries = voteRetries; this.voteRetryWait = voteRetryWait; + this.quorumVoteWait = quorumVoteWait; } public ReplicatedPolicy(boolean checkForLiveServer, @@ -96,7 +101,8 @@ public ReplicatedPolicy(boolean checkForLiveServer, boolean voteOnReplicationFailure, int quorumSize, int voteRetries, - long voteRetryWait) { + long voteRetryWait, + int quorumVoteWait) { this.checkForLiveServer = checkForLiveServer; this.clusterName = clusterName; this.groupName = groupName; @@ -106,6 +112,7 @@ public ReplicatedPolicy(boolean checkForLiveServer, this.networkHealthCheck = networkHealthCheck; this.voteOnReplicationFailure = voteOnReplicationFailure; this.quorumSize = quorumSize; + this.quorumVoteWait = quorumVoteWait; } public boolean isCheckForLiveServer() { @@ -147,7 +154,7 @@ public void setClusterName(String clusterName) { public ReplicaPolicy getReplicaPolicy() { if (replicaPolicy == null) { - replicaPolicy = new ReplicaPolicy(networkHealthCheck, this); + replicaPolicy = new ReplicaPolicy(networkHealthCheck, this, quorumVoteWait); replicaPolicy.setQuorumSize(quorumSize); replicaPolicy.setVoteOnReplicationFailure(voteOnReplicationFailure); replicaPolicy.setVoteRetries(voteRetries); @@ -230,4 +237,8 @@ public int getQuorumSize() { public void setQuorumSize(int quorumSize) { this.quorumSize = quorumSize; } -} + + public int getQuorumVoteWait() { + return quorumVoteWait; + } +} \ No newline at end of file diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/qourum/SharedNothingBackupQuorum.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/qourum/SharedNothingBackupQuorum.java index 035ca05a497..5e498af42f5 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/qourum/SharedNothingBackupQuorum.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/qourum/SharedNothingBackupQuorum.java @@ -66,6 +66,7 @@ public enum BACKUP_ACTIVATION { private volatile boolean stopped = false; + private final int quorumVoteWait; /** * This is a safety net in case the live sends the first {@link ReplicationLiveIsStoppingMessage} * with code {@link org.apache.activemq.artemis.core.protocol.core.impl.wireformat.ReplicationLiveIsStoppingMessage.LiveStopping#STOP_CALLED} and crashes before sending the second with @@ -81,7 +82,8 @@ public SharedNothingBackupQuorum(StorageManager storageManager, NetworkHealthCheck networkHealthCheck, int quorumSize, int voteRetries, - long voteRetryWait) { + long voteRetryWait, + int quorumVoteWait) { this.storageManager = storageManager; this.scheduledPool = scheduledPool; this.quorumSize = quorumSize; @@ -90,6 +92,7 @@ public SharedNothingBackupQuorum(StorageManager storageManager, this.networkHealthCheck = networkHealthCheck; this.voteRetries = voteRetries; this.voteRetryWait = voteRetryWait; + this.quorumVoteWait = quorumVoteWait; } private volatile BACKUP_ACTIVATION signal; @@ -297,9 +300,10 @@ private boolean isLiveDown() { quorumManager.vote(quorumVote); try { - quorumVote.await(LATCH_TIMEOUT, TimeUnit.SECONDS); + quorumVote.await(quorumVoteWait, TimeUnit.SECONDS); } catch (InterruptedException interruption) { // No-op. The best the quorum can do now is to return the latest number it has + ActiveMQServerLogger.LOGGER.quorumVoteAwaitInterrupted(); } quorumManager.voteComplete(quorumVote); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingBackupActivation.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingBackupActivation.java index 5ca6fdab035..863923de067 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingBackupActivation.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingBackupActivation.java @@ -131,7 +131,7 @@ public void run() { logger.trace("Entered a synchronized"); if (closed) return; - backupQuorum = new SharedNothingBackupQuorum(activeMQServer.getStorageManager(), activeMQServer.getNodeManager(), activeMQServer.getScheduledPool(), networkHealthCheck, replicaPolicy.getQuorumSize(), replicaPolicy.getVoteRetries(), replicaPolicy.getVoteRetryWait()); + backupQuorum = new SharedNothingBackupQuorum(activeMQServer.getStorageManager(), activeMQServer.getNodeManager(), activeMQServer.getScheduledPool(), networkHealthCheck, replicaPolicy.getQuorumSize(), replicaPolicy.getVoteRetries(), replicaPolicy.getVoteRetryWait(), replicaPolicy.getQuorumVoteWait()); activeMQServer.getClusterManager().getQuorumManager().registerQuorum(backupQuorum); activeMQServer.getClusterManager().getQuorumManager().registerQuorumHandler(new ServerConnectVoteHandler(activeMQServer)); } diff --git a/artemis-server/src/main/resources/schema/artemis-configuration.xsd b/artemis-server/src/main/resources/schema/artemis-configuration.xsd index 32e68e02fba..6a7067141c2 100644 --- a/artemis-server/src/main/resources/schema/artemis-configuration.xsd +++ b/artemis-server/src/main/resources/schema/artemis-configuration.xsd @@ -2260,6 +2260,13 @@ + + + + How long to wait (in seconds) for vote results + + + @@ -2363,6 +2370,13 @@ + + + + How long to wait (in seconds) for vote results + + + diff --git a/docs/user-manual/en/network-isolation.md b/docs/user-manual/en/network-isolation.md index ded89b7ab4f..78426e37dfd 100644 --- a/docs/user-manual/en/network-isolation.md +++ b/docs/user-manual/en/network-isolation.md @@ -8,7 +8,20 @@ from that will help mitigate this problem Quorum voting is used by both the live and the backup to decide what to do if a replication connection is disconnected. Basically the server will request each live server in the cluster to vote as to whether it thinks the server it is replicating -to or from is still alive. This being the case the minimum number of live/backup pairs needed is 3. If less than 3 pairs +to or from is still alive. You can also configure the time for which the quorum manager will wait for the quorum vote response. +The default time is 30 sec you can configure like so for master and also for the slave: + +```xml + + + + 12 + + + +``` + +This being the case the minimum number of live/backup pairs needed is 3. If less than 3 pairs are used then the only option is to use a Network Pinger which is explained later in this chapter or choose how you want each server to react which the following details: diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java index 4081e2140c7..9d99a90e9f4 100644 --- a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java @@ -65,6 +65,7 @@ public abstract class ClusteredBridgeTestBase extends ActiveMQTestBase { private static int index = 0; + private static final int QUORUM_VOTE_WAIT_TIME_SEC = 30; protected Map groups = new HashMap<>(); @@ -146,7 +147,7 @@ public void create() throws Exception { backupConnector = new TransportConfiguration(INVM_CONNECTOR_FACTORY, params, "in-vm-backup"); //live - Configuration conf0 = createBasicConfig().setJournalDirectory(getJournalDir(id, false)).setBindingsDirectory(getBindingsDir(id, false)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params0)).addConnectorConfiguration(liveConnector.getName(), liveConnector).setHAPolicyConfiguration(new ReplicatedPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())); + Configuration conf0 = createBasicConfig().setJournalDirectory(getJournalDir(id, false)).setBindingsDirectory(getBindingsDir(id, false)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params0)).addConnectorConfiguration(liveConnector.getName(), liveConnector).setHAPolicyConfiguration(new ReplicatedPolicyConfiguration(QUORUM_VOTE_WAIT_TIME_SEC)).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())); ActiveMQServer server0 = addServer(ActiveMQServers.newActiveMQServer(conf0, true)); @@ -155,7 +156,7 @@ public void create() throws Exception { liveNode.setRegistry(new JndiBindingRegistry(liveContext)); //backup - Configuration config = createBasicConfig().setJournalDirectory(getJournalDir(id, true)).setBindingsDirectory(getBindingsDir(id, true)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params)).addConnectorConfiguration(backupConnector.getName(), backupConnector).addConnectorConfiguration(liveConnector.getName(), liveConnector).setHAPolicyConfiguration(new ReplicaPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(backupConnector.getName(), liveConnector.getName())); + Configuration config = createBasicConfig().setJournalDirectory(getJournalDir(id, true)).setBindingsDirectory(getBindingsDir(id, true)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params)).addConnectorConfiguration(backupConnector.getName(), backupConnector).addConnectorConfiguration(liveConnector.getName(), liveConnector).setHAPolicyConfiguration(new ReplicaPolicyConfiguration(QUORUM_VOTE_WAIT_TIME_SEC)).addClusterConfiguration(basicClusterConnectionConfig(backupConnector.getName(), liveConnector.getName())); ActiveMQServer backup = addServer(ActiveMQServers.newActiveMQServer(config, true)); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java index 89d5175dbbb..edb4fbbd92b 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java @@ -84,6 +84,7 @@ import org.apache.activemq.artemis.core.server.impl.InVMNodeManager; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -1483,7 +1484,7 @@ protected void setupLiveServer(final int node, if (sharedStorage) haPolicyConfiguration = new SharedStoreMasterPolicyConfiguration(); else - haPolicyConfiguration = new ReplicatedPolicyConfiguration(); + haPolicyConfiguration = new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); } Configuration configuration = createBasicConfig(node).setJournalMaxIO_AIO(1000).setThreadPoolMaxSize(10).clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(netty, true, generateParams(node, netty))).setHAPolicyConfiguration(haPolicyConfiguration).setResolveProtocols(isResolveProtocols()); @@ -1538,7 +1539,7 @@ protected void setupBackupServer(final int node, TransportConfiguration backupConfig = createTransportConfiguration(netty, false, generateParams(node, netty)); TransportConfiguration acceptorConfig = createTransportConfiguration(netty, true, generateParams(node, netty)); - Configuration configuration = createBasicConfig(sharedStorage ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(acceptorConfig).addConnectorConfiguration(liveConfig.getName(), liveConfig).addConnectorConfiguration(backupConfig.getName(), backupConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration()); + Configuration configuration = createBasicConfig(sharedStorage ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(acceptorConfig).addConnectorConfiguration(liveConfig.getName(), liveConfig).addConnectorConfiguration(backupConfig.getName(), backupConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); ActiveMQServer server; @@ -1575,7 +1576,7 @@ protected void setupLiveServerWithDiscovery(final int node, DiscoveryGroupConfiguration dcConfig = new DiscoveryGroupConfiguration().setName("dg1").setRefreshTimeout(1000).setDiscoveryInitialWaitTimeout(1000).setBroadcastEndpointFactory(endpoint); - Configuration configuration = createBasicConfig(node).setJournalMaxIO_AIO(1000).clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(netty, true, params)).addConnectorConfiguration(connector.getName(), connector).addBroadcastGroupConfiguration(bcConfig).addDiscoveryGroupConfiguration(dcConfig.getName(), dcConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration()); + Configuration configuration = createBasicConfig(node).setJournalMaxIO_AIO(1000).clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(netty, true, params)).addConnectorConfiguration(connector.getName(), connector).addBroadcastGroupConfiguration(bcConfig).addDiscoveryGroupConfiguration(dcConfig.getName(), dcConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); ActiveMQServer server; if (fileStorage) { @@ -1620,7 +1621,7 @@ protected void setupBackupServerWithDiscovery(final int node, DiscoveryGroupConfiguration dcConfig = new DiscoveryGroupConfiguration().setName("dg1").setRefreshTimeout(5000).setDiscoveryInitialWaitTimeout(5000).setBroadcastEndpointFactory(endpoint); - Configuration configuration = createBasicConfig(sharedStorage ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(netty, true, params)).addConnectorConfiguration(connector.getName(), connector).addBroadcastGroupConfiguration(bcConfig).addDiscoveryGroupConfiguration(dcConfig.getName(), dcConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreSlavePolicyConfiguration() : new ReplicatedPolicyConfiguration()); + Configuration configuration = createBasicConfig(sharedStorage ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(netty, true, params)).addConnectorConfiguration(connector.getName(), connector).addBroadcastGroupConfiguration(bcConfig).addDiscoveryGroupConfiguration(dcConfig.getName(), dcConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreSlavePolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); ActiveMQServer server; if (sharedStorage) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/AutomaticColocatedQuorumVoteTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/AutomaticColocatedQuorumVoteTest.java index 0f792b52326..7cc59041118 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/AutomaticColocatedQuorumVoteTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/AutomaticColocatedQuorumVoteTest.java @@ -41,6 +41,7 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -311,8 +312,8 @@ private Configuration getConfiguration(String identity, sssc.setScaleDownConfiguration(new ScaleDownConfiguration()); } } else { - ReplicatedPolicyConfiguration rpc = new ReplicatedPolicyConfiguration(); - ReplicaPolicyConfiguration rpc2 = new ReplicaPolicyConfiguration(); + ReplicatedPolicyConfiguration rpc = new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); + ReplicaPolicyConfiguration rpc2 = new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); haPolicy.setLiveConfig(rpc); haPolicy.setBackupConfig(rpc2); if (scaleDown) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleLivesMultipleBackupsFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleLivesMultipleBackupsFailoverTest.java index 30bd24c090d..bfd85a7bd4e 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleLivesMultipleBackupsFailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleLivesMultipleBackupsFailoverTest.java @@ -33,6 +33,7 @@ import org.apache.activemq.artemis.core.server.impl.InVMNodeManager; import org.apache.activemq.artemis.tests.integration.cluster.util.SameProcessActiveMQServer; import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer; +import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.Test; /** @@ -125,7 +126,7 @@ protected void createBackupConfig(NodeManager nodeManager, boolean createClusterConnections, int[] otherBackupNodes, int... otherClusterNodes) throws Exception { - Configuration config1 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(nodeid, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration()).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); + Configuration config1 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(nodeid, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); for (int node : otherBackupNodes) { TransportConfiguration liveConnector = createTransportConfiguration(isNetty(), false, generateParams(node, isNetty())); @@ -149,7 +150,7 @@ protected void createBackupConfig(NodeManager nodeManager, protected void createLiveConfig(NodeManager nodeManager, int liveNode, int... otherLiveNodes) throws Exception { TransportConfiguration liveConnector = createTransportConfiguration(isNetty(), false, generateParams(liveNode, isNetty())); - Configuration config0 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(liveNode, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration()).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode).addConnectorConfiguration(liveConnector.getName(), liveConnector); + Configuration config0 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(liveNode, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode).addConnectorConfiguration(liveConnector.getName(), liveConnector); String[] pairs = new String[otherLiveNodes.length]; for (int i = 0; i < otherLiveNodes.length; i++) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleServerFailoverTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleServerFailoverTestBase.java index 0775e085238..c7ddefea317 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleServerFailoverTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleServerFailoverTestBase.java @@ -39,6 +39,7 @@ import org.apache.activemq.artemis.tests.integration.cluster.util.SameProcessActiveMQServer; import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.apache.activemq.artemis.tests.util.TransportConfigurationUtils; import org.junit.Before; @@ -86,7 +87,7 @@ public void setUp() throws Exception { if (isSharedStore()) { haPolicyConfiguration = new SharedStoreMasterPolicyConfiguration(); } else { - haPolicyConfiguration = new ReplicatedPolicyConfiguration(); + haPolicyConfiguration = new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); if (getNodeGroupName() != null) { ((ReplicatedPolicyConfiguration) haPolicyConfiguration).setGroupName(getNodeGroupName() + "-" + i); } @@ -129,7 +130,7 @@ public void setUp() throws Exception { if (isSharedStore()) { haPolicyConfiguration = new SharedStoreSlavePolicyConfiguration(); } else { - haPolicyConfiguration = new ReplicaPolicyConfiguration(); + haPolicyConfiguration = new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); if (getNodeGroupName() != null) { ((ReplicaPolicyConfiguration) haPolicyConfiguration).setGroupName(getNodeGroupName() + "-" + i); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java new file mode 100644 index 00000000000..ada45e6bab4 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java @@ -0,0 +1,58 @@ +/* + * 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.artemis.tests.integration.cluster.failover; + +import org.apache.activemq.artemis.core.config.ha.ReplicaPolicyConfiguration; +import org.apache.activemq.artemis.core.config.ha.ReplicatedPolicyConfiguration; +import org.apache.activemq.artemis.core.server.cluster.ha.ReplicatedPolicy; +import org.apache.activemq.artemis.tests.util.HAConfigUtils; +import org.junit.Test; +public class QuorumResultWaitTest extends StaticClusterWithBackupFailoverTest { + + public static final int QUORUM_VOTE_WAIT_CONFIGURED_TIME_SEC = 12; + @Override + protected void setupServers() throws Exception { + super.setupServers(); + //we need to know who is connected to who + ((ReplicatedPolicyConfiguration) servers[0].getConfiguration().getHAPolicyConfiguration()).setGroupName("group0"); + ((ReplicatedPolicyConfiguration) servers[1].getConfiguration().getHAPolicyConfiguration()).setGroupName("group1"); + ((ReplicatedPolicyConfiguration) servers[2].getConfiguration().getHAPolicyConfiguration()).setGroupName("group2"); + ((ReplicaPolicyConfiguration) servers[4].getConfiguration().getHAPolicyConfiguration()).setGroupName("group1"); + ((ReplicaPolicyConfiguration) servers[5].getConfiguration().getHAPolicyConfiguration()).setGroupName("group2"); + ReplicatedPolicyConfiguration replicatedPolicyConf = new ReplicatedPolicyConfiguration(QUORUM_VOTE_WAIT_CONFIGURED_TIME_SEC); + replicatedPolicyConf.setGroupName("group0"); + replicatedPolicyConf.setVoteRetries(5); + replicatedPolicyConf.setVoteRetryWait(100); + servers[3].getConfiguration().setHAPolicyConfiguration(replicatedPolicyConf); + } + + @Test + public void testQuorumVotingResultWait() throws Exception { + setupCluster(); + startServers(0, 1, 2); + startServers(3, 4, 5); + //Assert if the default time 30 sec is used + assertEquals(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC, ((ReplicatedPolicy)(servers[0].getHAPolicy())).getQuorumVoteWait()); + //Assert if the configured time is used. + assertEquals(QUORUM_VOTE_WAIT_CONFIGURED_TIME_SEC, ((ReplicatedPolicy)(servers[3].getHAPolicy())).getQuorumVoteWait()); + } + + @Override + protected boolean isSharedStorage() { + return false; + } +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SingleLiveMultipleBackupsFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SingleLiveMultipleBackupsFailoverTest.java index a3174d7cc9c..6853faaeb9c 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SingleLiveMultipleBackupsFailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SingleLiveMultipleBackupsFailoverTest.java @@ -33,6 +33,7 @@ import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.integration.cluster.util.SameProcessActiveMQServer; import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer; +import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.Test; /** @@ -125,7 +126,7 @@ public void testMultipleFailovers() throws Exception { protected void createBackupConfig(int liveNode, int nodeid, int... nodes) throws Exception { TransportConfiguration backupConnector = createTransportConfiguration(isNetty(), false, generateParams(nodeid, isNetty())); - Configuration config1 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(nodeid, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicatedPolicyConfiguration()).addConnectorConfiguration(backupConnector.getName(), backupConnector).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); + Configuration config1 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(nodeid, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).addConnectorConfiguration(backupConnector.getName(), backupConnector).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); String[] staticConnectors = new String[nodes.length]; for (int i = 0; i < nodes.length; i++) { @@ -141,7 +142,7 @@ protected void createBackupConfig(int liveNode, int nodeid, int... nodes) throws protected void createLiveConfig(int liveNode) throws Exception { TransportConfiguration liveConnector = createTransportConfiguration(isNetty(), false, generateParams(liveNode, isNetty())); - Configuration config0 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(liveNode, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())).addConnectorConfiguration(liveConnector.getName(), liveConnector).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); + Configuration config0 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(liveNode, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())).addConnectorConfiguration(liveConnector.getName(), liveConnector).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); SameProcessActiveMQServer server = new SameProcessActiveMQServer(createInVMFailoverServer(true, config0, nodeManager, liveNode)); addActiveMQComponent(server); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/util/MultiServerTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/util/MultiServerTestBase.java index 8ceff8829a5..39fcf098ae2 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/util/MultiServerTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/util/MultiServerTestBase.java @@ -35,6 +35,7 @@ import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; import org.apache.activemq.artemis.core.server.impl.InVMNodeManager; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.Before; public class MultiServerTestBase extends ActiveMQTestBase { @@ -156,7 +157,7 @@ protected Pair setupLiveServer(final int node, nodeManager = new InVMNodeManager(false); } - Configuration configuration = createBasicConfig(node).setJournalMaxIO_AIO(1000).setThreadPoolMaxSize(10).clearAcceptorConfigurations().addAcceptorConfiguration(serverConfigAcceptor).addConnectorConfiguration("thisConnector", thisConnector).setHAPolicyConfiguration(sharedStorage ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration()); + Configuration configuration = createBasicConfig(node).setJournalMaxIO_AIO(1000).setThreadPoolMaxSize(10).clearAcceptorConfigurations().addAcceptorConfiguration(serverConfigAcceptor).addConnectorConfiguration("thisConnector", thisConnector).setHAPolicyConfiguration(sharedStorage ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); List targetServersOnConnection = new ArrayList<>(); @@ -202,7 +203,7 @@ protected ActiveMQServer setupBackupServer(final int node, TransportConfiguration serverConfigAcceptor = createTransportConfiguration(useNetty(), true, generateParams(node, useNetty())); TransportConfiguration thisConnector = createTransportConfiguration(useNetty(), false, generateParams(node, useNetty())); - Configuration configuration = createBasicConfig(useSharedStorage() ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(serverConfigAcceptor).addConnectorConfiguration("thisConnector", thisConnector).setHAPolicyConfiguration(useSharedStorage() ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration()); + Configuration configuration = createBasicConfig(useSharedStorage() ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(serverConfigAcceptor).addConnectorConfiguration("thisConnector", thisConnector).setHAPolicyConfiguration(useSharedStorage() ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); List targetServersOnConnection = new ArrayList<>(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverTest.java index 4b4e89b1be3..800f8a306d9 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverTest.java @@ -62,6 +62,7 @@ import org.apache.activemq.artemis.tests.integration.jms.server.management.JMSUtil; import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.apache.activemq.artemis.tests.util.InVMNodeManagerServer; import org.apache.activemq.artemis.utils.RandomUtil; import org.junit.Assert; @@ -465,7 +466,7 @@ protected void startServers() throws Exception { backupParams.put(TransportConstants.SERVER_ID_PROP_NAME, 1); - backupConf = createBasicConfig().addAcceptorConfiguration(backupAcceptortc).addConnectorConfiguration(livetc.getName(), livetc).addConnectorConfiguration(backuptc.getName(), backuptc).setSecurityEnabled(false).setJournalType(getDefaultJournalType()).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, backupParams)).setBindingsDirectory(getBindingsDir()).setJournalMinFiles(2).setJournalDirectory(getJournalDir()).setPagingDirectory(getPageDir()).setLargeMessagesDirectory(getLargeMessagesDir()).setPersistenceEnabled(true).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(backuptc.getName(), livetc.getName())); + backupConf = createBasicConfig().addAcceptorConfiguration(backupAcceptortc).addConnectorConfiguration(livetc.getName(), livetc).addConnectorConfiguration(backuptc.getName(), backuptc).setSecurityEnabled(false).setJournalType(getDefaultJournalType()).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, backupParams)).setBindingsDirectory(getBindingsDir()).setJournalMinFiles(2).setJournalDirectory(getJournalDir()).setPagingDirectory(getPageDir()).setLargeMessagesDirectory(getLargeMessagesDir()).setPersistenceEnabled(true).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).addClusterConfiguration(basicClusterConnectionConfig(backuptc.getName(), livetc.getName())); backupServer = addServer(new InVMNodeManagerServer(backupConf, nodeManager)); @@ -477,7 +478,7 @@ protected void startServers() throws Exception { log.info("Starting backup"); backupJMSServer.start(); - liveConf = createBasicConfig().setJournalDirectory(getJournalDir()).setBindingsDirectory(getBindingsDir()).setSecurityEnabled(false).addAcceptorConfiguration(liveAcceptortc).setJournalType(getDefaultJournalType()).setBindingsDirectory(getBindingsDir()).setJournalMinFiles(2).setJournalDirectory(getJournalDir()).setPagingDirectory(getPageDir()).setLargeMessagesDirectory(getLargeMessagesDir()).addConnectorConfiguration(livetc.getName(), livetc).setPersistenceEnabled(true).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(livetc.getName())); + liveConf = createBasicConfig().setJournalDirectory(getJournalDir()).setBindingsDirectory(getBindingsDir()).setSecurityEnabled(false).addAcceptorConfiguration(liveAcceptortc).setJournalType(getDefaultJournalType()).setBindingsDirectory(getBindingsDir()).setJournalMinFiles(2).setJournalDirectory(getJournalDir()).setPagingDirectory(getPageDir()).setLargeMessagesDirectory(getLargeMessagesDir()).addConnectorConfiguration(livetc.getName(), livetc).setPersistenceEnabled(true).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).addClusterConfiguration(basicClusterConnectionConfig(livetc.getName())); liveServer = addServer(new InVMNodeManagerServer(liveConf, nodeManager)); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationTest.java index 3997c1dad3f..869ec6c7dcf 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationTest.java @@ -46,6 +46,7 @@ import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.core.server.JournalType; import org.apache.activemq.artemis.junit.Wait; +import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.jboss.logging.Logger; import org.junit.After; import org.junit.Assert; @@ -247,7 +248,7 @@ private Configuration createLiveConfiguration() throws Exception { conf.setClusterUser("mycluster"); conf.setClusterPassword("mypassword"); - ReplicatedPolicyConfiguration haPolicy = new ReplicatedPolicyConfiguration(); + ReplicatedPolicyConfiguration haPolicy = new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); haPolicy.setVoteOnReplicationFailure(false); haPolicy.setCheckForLiveServer(false); conf.setHAPolicyConfiguration(haPolicy); @@ -270,7 +271,7 @@ private Configuration createBackupConfiguration() throws Exception { File backupDir = brokersFolder.newFolder("backup"); conf.setBrokerInstance(backupDir); - ReplicaPolicyConfiguration haPolicy = new ReplicaPolicyConfiguration(); + ReplicaPolicyConfiguration haPolicy = new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); haPolicy.setClusterName("cluster"); conf.setHAPolicyConfiguration(haPolicy); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/HAConfigUtils.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/HAConfigUtils.java new file mode 100644 index 00000000000..096c41bb6ef --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/HAConfigUtils.java @@ -0,0 +1,21 @@ +/* + * 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.artemis.tests.util; + +public final class HAConfigUtils { + public static final int QUORUM_VOTE_WAIT_TIME_SEC = 30; +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/ReplicatedBackupUtils.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/ReplicatedBackupUtils.java index 1a38a6ac702..7d313e3cf93 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/ReplicatedBackupUtils.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/ReplicatedBackupUtils.java @@ -44,8 +44,8 @@ public static void configureReplicationPair(Configuration backupConfig, liveConfig.clearAcceptorConfigurations().addAcceptorConfiguration(liveAcceptor); } - backupConfig.addConnectorConfiguration(BACKUP_NODE_NAME, backupConnector).addConnectorConfiguration(LIVE_NODE_NAME, liveConnector).addClusterConfiguration(ActiveMQTestBase.basicClusterConnectionConfig(BACKUP_NODE_NAME, LIVE_NODE_NAME)).setHAPolicyConfiguration(new ReplicaPolicyConfiguration()); + backupConfig.addConnectorConfiguration(BACKUP_NODE_NAME, backupConnector).addConnectorConfiguration(LIVE_NODE_NAME, liveConnector).addClusterConfiguration(ActiveMQTestBase.basicClusterConnectionConfig(BACKUP_NODE_NAME, LIVE_NODE_NAME)).setHAPolicyConfiguration(new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); - liveConfig.setName(LIVE_NODE_NAME).addConnectorConfiguration(LIVE_NODE_NAME, liveConnector).addConnectorConfiguration(BACKUP_NODE_NAME, backupConnector).setSecurityEnabled(false).addClusterConfiguration(ActiveMQTestBase.basicClusterConnectionConfig(LIVE_NODE_NAME, BACKUP_NODE_NAME)).setHAPolicyConfiguration(new ReplicatedPolicyConfiguration()); + liveConfig.setName(LIVE_NODE_NAME).addConnectorConfiguration(LIVE_NODE_NAME, liveConnector).addConnectorConfiguration(BACKUP_NODE_NAME, backupConnector).setSecurityEnabled(false).addClusterConfiguration(ActiveMQTestBase.basicClusterConnectionConfig(LIVE_NODE_NAME, BACKUP_NODE_NAME)).setHAPolicyConfiguration(new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); } } From f09a41d4332564913a618dc93bd1c4e23c6391a3 Mon Sep 17 00:00:00 2001 From: "Christopher L. Shannon (cshannon)" Date: Fri, 25 May 2018 07:11:26 -0400 Subject: [PATCH 004/207] ARTEMIS-1888 - Add forceSSLParameters flag to override system SSL properties If true the connection factory will prefer SSL settings set via the connector configuration vs system properties --- .../remoting/impl/netty/NettyConnector.java | 19 +- .../impl/netty/TransportConstants.java | 5 + docs/user-manual/en/configuring-transports.md | 8 + .../impl/netty/NettyConnectorTest.java | 194 ++++++++++++++++-- 4 files changed, 205 insertions(+), 21 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java index ebb274a1d8e..b32da929115 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java @@ -130,8 +130,10 @@ public class NettyConnector extends AbstractConnector { // Constants ----------------------------------------------------- public static final String JAVAX_KEYSTORE_PATH_PROP_NAME = "javax.net.ssl.keyStore"; public static final String JAVAX_KEYSTORE_PASSWORD_PROP_NAME = "javax.net.ssl.keyStorePassword"; + public static final String JAVAX_KEYSTORE_PROVIDER_PROP_NAME = "javax.net.ssl.keyStoreType"; public static final String JAVAX_TRUSTSTORE_PATH_PROP_NAME = "javax.net.ssl.trustStore"; public static final String JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME = "javax.net.ssl.trustStorePassword"; + public static final String JAVAX_TRUSTSTORE_PROVIDER_PROP_NAME = "javax.net.ssl.trustStoreType"; public static final String ACTIVEMQ_KEYSTORE_PROVIDER_PROP_NAME = "org.apache.activemq.ssl.keyStoreProvider"; public static final String ACTIVEMQ_KEYSTORE_PATH_PROP_NAME = "org.apache.activemq.ssl.keyStore"; public static final String ACTIVEMQ_KEYSTORE_PASSWORD_PROP_NAME = "org.apache.activemq.ssl.keyStorePassword"; @@ -224,6 +226,8 @@ public class NettyConnector extends AbstractConnector { private boolean trustAll; + private boolean forceSSLParameters; + private String sniHost; private String kerb5Config; @@ -358,6 +362,8 @@ public NettyConnector(final Map configuration, trustAll = ConfigurationHelper.getBooleanProperty(TransportConstants.TRUST_ALL_PROP_NAME, TransportConstants.DEFAULT_TRUST_ALL, configuration); + forceSSLParameters = ConfigurationHelper.getBooleanProperty(TransportConstants.FORCE_SSL_PARAMETERS, TransportConstants.DEFAULT_FORCE_SSL_PARAMETERS, configuration); + sslProvider = ConfigurationHelper.getStringProperty(TransportConstants.SSL_PROVIDER, TransportConstants.DEFAULT_SSL_PROVIDER, configuration); sniHost = ConfigurationHelper.getStringProperty(TransportConstants.SNIHOST_PROP_NAME, TransportConstants.DEFAULT_SNIHOST_CONFIG, configuration); @@ -500,13 +506,14 @@ public synchronized void start() { if (sslEnabled) { // HORNETQ-680 - override the server-side config if client-side system properties are set - realKeyStorePath = Stream.of(System.getProperty(JAVAX_KEYSTORE_PATH_PROP_NAME), System.getProperty(ACTIVEMQ_KEYSTORE_PATH_PROP_NAME), keyStorePath).map(v -> useDefaultSslContext ? keyStorePath : v).filter(Objects::nonNull).findFirst().orElse(null); - realKeyStorePassword = Stream.of(System.getProperty(JAVAX_KEYSTORE_PASSWORD_PROP_NAME), System.getProperty(ACTIVEMQ_KEYSTORE_PASSWORD_PROP_NAME), keyStorePassword).map(v -> useDefaultSslContext ? keyStorePassword : v).filter(Objects::nonNull).findFirst().orElse(null); - realKeyStoreProvider = Stream.of(System.getProperty(ACTIVEMQ_KEYSTORE_PROVIDER_PROP_NAME), keyStoreProvider).map(v -> useDefaultSslContext ? keyStoreProvider : v).filter(Objects::nonNull).findFirst().orElse(null); + realKeyStorePath = forceSSLParameters && keyStorePath != null ? keyStorePath : Stream.of(System.getProperty(JAVAX_KEYSTORE_PATH_PROP_NAME), System.getProperty(ACTIVEMQ_KEYSTORE_PATH_PROP_NAME), keyStorePath).map(v -> useDefaultSslContext ? keyStorePath : v).filter(Objects::nonNull).findFirst().orElse(null); + realKeyStorePassword = forceSSLParameters && keyStorePassword != null ? keyStorePassword : Stream.of(System.getProperty(JAVAX_KEYSTORE_PASSWORD_PROP_NAME), System.getProperty(ACTIVEMQ_KEYSTORE_PASSWORD_PROP_NAME), keyStorePassword).map(v -> useDefaultSslContext ? keyStorePassword : v).filter(Objects::nonNull).findFirst().orElse(null); + realKeyStoreProvider = forceSSLParameters && keyStoreProvider != null ? keyStoreProvider : Stream.of(System.getProperty(JAVAX_KEYSTORE_PROVIDER_PROP_NAME),System.getProperty(ACTIVEMQ_KEYSTORE_PROVIDER_PROP_NAME), keyStoreProvider).map(v -> useDefaultSslContext ? keyStoreProvider : v).filter(Objects::nonNull).findFirst().orElse(null); + + realTrustStorePath = forceSSLParameters && trustStorePath != null ? trustStorePath : Stream.of(System.getProperty(JAVAX_TRUSTSTORE_PATH_PROP_NAME), System.getProperty(ACTIVEMQ_TRUSTSTORE_PATH_PROP_NAME), trustStorePath).map(v -> useDefaultSslContext ? trustStorePath : v).filter(Objects::nonNull).findFirst().orElse(null); + realTrustStorePassword = forceSSLParameters && trustStorePassword != null ? trustStorePassword : Stream.of(System.getProperty(JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME), System.getProperty(ACTIVEMQ_TRUSTSTORE_PASSWORD_PROP_NAME), trustStorePassword).map(v -> useDefaultSslContext ? trustStorePassword : v).filter(Objects::nonNull).findFirst().orElse(null); + realTrustStoreProvider = forceSSLParameters && trustStoreProvider != null ? trustStoreProvider : Stream.of(System.getProperty(JAVAX_TRUSTSTORE_PROVIDER_PROP_NAME), System.getProperty(ACTIVEMQ_TRUSTSTORE_PROVIDER_PROP_NAME), trustStoreProvider).map(v -> useDefaultSslContext ? trustStoreProvider : v).filter(Objects::nonNull).findFirst().orElse(null); - realTrustStorePath = Stream.of(System.getProperty(JAVAX_TRUSTSTORE_PATH_PROP_NAME), System.getProperty(ACTIVEMQ_TRUSTSTORE_PATH_PROP_NAME), trustStorePath).map(v -> useDefaultSslContext ? trustStorePath : v).filter(Objects::nonNull).findFirst().orElse(null); - realTrustStorePassword = Stream.of(System.getProperty(JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME), System.getProperty(ACTIVEMQ_TRUSTSTORE_PASSWORD_PROP_NAME), trustStorePassword).map(v -> useDefaultSslContext ? trustStorePassword : v).filter(Objects::nonNull).findFirst().orElse(null); - realTrustStoreProvider = Stream.of(System.getProperty(ACTIVEMQ_TRUSTSTORE_PROVIDER_PROP_NAME), trustStoreProvider).map(v -> useDefaultSslContext ? trustStoreProvider : v).filter(Objects::nonNull).findFirst().orElse(null); } else { realKeyStorePath = null; realKeyStoreProvider = null; diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java index 9ba2c8bf84b..50da17950a6 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java @@ -109,6 +109,8 @@ public class TransportConstants { public static final String TRUST_ALL_PROP_NAME = "trustAll"; + public static final String FORCE_SSL_PARAMETERS = "forceSSLParameters"; + public static final String SNIHOST_PROP_NAME = "sniHost"; public static final String BACKLOG_PROP_NAME = "backlog"; @@ -213,6 +215,8 @@ public class TransportConstants { public static final boolean DEFAULT_TRUST_ALL = false; + public static final boolean DEFAULT_FORCE_SSL_PARAMETERS = false; + public static final boolean DEFAULT_USE_DEFAULT_SSL_CONTEXT = false; public static final boolean DEFAULT_TCP_NODELAY = true; @@ -361,6 +365,7 @@ public class TransportConstants { allowableConnectorKeys.add(TransportConstants.ENABLED_PROTOCOLS_PROP_NAME); allowableConnectorKeys.add(TransportConstants.VERIFY_HOST_PROP_NAME); allowableConnectorKeys.add(TransportConstants.TRUST_ALL_PROP_NAME); + allowableConnectorKeys.add(TransportConstants.FORCE_SSL_PARAMETERS); allowableConnectorKeys.add(TransportConstants.TCP_NODELAY_PROPNAME); allowableConnectorKeys.add(TransportConstants.TCP_SENDBUFFER_SIZE_PROPNAME); allowableConnectorKeys.add(TransportConstants.TCP_RECEIVEBUFFER_SIZE_PROPNAME); diff --git a/docs/user-manual/en/configuring-transports.md b/docs/user-manual/en/configuring-transports.md index 189e3dad408..47dfc7b6322 100644 --- a/docs/user-manual/en/configuring-transports.md +++ b/docs/user-manual/en/configuring-transports.md @@ -443,6 +443,14 @@ following additional properties: primarily for testing purposes only and should not be used in production. Valid values are `true` or `false`. Default is `false`. + +- `forceSSLParameters` + + When used on a `connector` any SSL settings that are set as parameters on the connector will + be used instead of JVM system properties including both javax.net.ssl and ActiveMQ system properties + to configure the SSL context for this connector. + + Valid values are `true` or `false`. Default is `false`. - `useDefaultSslContext` diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/remoting/impl/netty/NettyConnectorTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/remoting/impl/netty/NettyConnectorTest.java index d302be786bd..98293299e12 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/remoting/impl/netty/NettyConnectorTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/remoting/impl/netty/NettyConnectorTest.java @@ -18,13 +18,17 @@ import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.TransportConfiguration; +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnector; import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; import org.apache.activemq.artemis.core.server.ActiveMQComponent; +import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.spi.core.remoting.BufferHandler; import org.apache.activemq.artemis.spi.core.remoting.ClientConnectionLifeCycleListener; import org.apache.activemq.artemis.spi.core.remoting.ClientProtocolManager; @@ -32,10 +36,40 @@ import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.utils.ActiveMQThreadFactory; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; public class NettyConnectorTest extends ActiveMQTestBase { + private ActiveMQServer server; + private ExecutorService executorService; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + executorService = Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()); + + Map params = new HashMap<>(); + params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + params.put(TransportConstants.SSL_PROVIDER, TransportConstants.OPENSSL_PROVIDER); + params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "openssl-server-side-keystore.jks"); + params.put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, "secureexample"); + params.put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, "openssl-server-side-truststore.jks"); + params.put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, "secureexample"); + params.put(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); + ConfigurationImpl config = createBasicConfig().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params, "nettySSL")); + server = createServer(false, config); + server.start(); + waitForServerToStart(server); + } + + @Override + public void tearDown() throws Exception { + executorService.shutdown(); + super.tearDown(); + } + private ClientConnectionLifeCycleListener listener = new ClientConnectionLifeCycleListener() { @Override public void connectionException(final Object connectionID, final ActiveMQException me) { @@ -99,61 +133,191 @@ public void bufferReceived(final Object connectionID, final ActiveMQBuffer buffe } } + + /** + * that java system properties are read + */ @Test - public void testJavaSystemPropertyOverrides() throws Exception { + public void testJavaSystemProperty() throws Exception { BufferHandler handler = new BufferHandler() { @Override public void bufferReceived(final Object connectionID, final ActiveMQBuffer buffer) { } }; + + System.setProperty(NettyConnector.JAVAX_KEYSTORE_PATH_PROP_NAME, "openssl-client-side-keystore.jks"); + System.setProperty(NettyConnector.JAVAX_KEYSTORE_PASSWORD_PROP_NAME, "secureexample"); + System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PATH_PROP_NAME, "openssl-client-side-truststore.jks"); + System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME, "secureexample"); + Map params = new HashMap<>(); params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true); - params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "bad path"); - params.put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, "bad password"); - params.put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, "bad path"); - params.put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, "bad password"); - NettyConnector connector = new NettyConnector(params, handler, listener, Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()), Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()), Executors.newScheduledThreadPool(5, ActiveMQThreadFactory.defaultThreadFactory())); + NettyConnector connector = new NettyConnector(params, handler, listener, executorService, Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()), Executors.newScheduledThreadPool(5, ActiveMQThreadFactory.defaultThreadFactory())); - System.setProperty(NettyConnector.JAVAX_KEYSTORE_PATH_PROP_NAME, "client-side-keystore.jks"); - System.setProperty(NettyConnector.JAVAX_KEYSTORE_PASSWORD_PROP_NAME, "secureexample"); - System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PATH_PROP_NAME, "client-side-truststore.jks"); - System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME, "secureexample"); + connector.start(); + Assert.assertTrue(connector.isStarted()); + Connection c = connector.createConnection(); + assertNotNull(c); + c.close(); + connector.close(); + Assert.assertFalse(connector.isStarted()); + + } + + @Test + public void testOverridesJavaSystemPropertyFail() throws Exception { + BufferHandler handler = new BufferHandler() { + @Override + public void bufferReceived(final Object connectionID, final ActiveMQBuffer buffer) { + } + }; + + //bad system properties will override the transport constants + System.setProperty(NettyConnector.JAVAX_KEYSTORE_PATH_PROP_NAME, "bad path"); + System.setProperty(NettyConnector.JAVAX_KEYSTORE_PASSWORD_PROP_NAME, "bad password"); + System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PATH_PROP_NAME, "bad path"); + System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME, "bad password"); + + Map params = new HashMap<>(); + params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "openssl-client-side-keystore.jks"); + params.put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, "secureexample"); + params.put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME,"openssl-client-side-truststore.jks"); + params.put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, "secureexample"); + + NettyConnector connector = new NettyConnector(params, handler, listener, executorService, Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()), Executors.newScheduledThreadPool(5, ActiveMQThreadFactory.defaultThreadFactory())); connector.start(); Assert.assertTrue(connector.isStarted()); + Connection c = connector.createConnection(); + + //Should have failed because SSL props override transport config options + assertNull(c); connector.close(); Assert.assertFalse(connector.isStarted()); } @Test - public void testActiveMQSystemPropertyOverrides() throws Exception { + public void testOverridesJavaSystemProperty() throws Exception { BufferHandler handler = new BufferHandler() { @Override public void bufferReceived(final Object connectionID, final ActiveMQBuffer buffer) { } }; + + //system properties will override the bad transport constants + System.setProperty(NettyConnector.JAVAX_KEYSTORE_PATH_PROP_NAME, "openssl-client-side-keystore.jks"); + System.setProperty(NettyConnector.JAVAX_KEYSTORE_PASSWORD_PROP_NAME, "secureexample"); + System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PATH_PROP_NAME, "openssl-client-side-truststore.jks"); + System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME, "secureexample"); + Map params = new HashMap<>(); params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true); - params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "bad path"); + params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "bad path"); params.put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, "bad password"); params.put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME, "bad path"); params.put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, "bad password"); - NettyConnector connector = new NettyConnector(params, handler, listener, Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()), Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()), Executors.newScheduledThreadPool(5, ActiveMQThreadFactory.defaultThreadFactory())); + NettyConnector connector = new NettyConnector(params, handler, listener, executorService, Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()), Executors.newScheduledThreadPool(5, ActiveMQThreadFactory.defaultThreadFactory())); + + connector.start(); + Assert.assertTrue(connector.isStarted()); + Connection c = connector.createConnection(); + + //Should not fail because SSL props override transport config options + assertNotNull(c); + c.close(); + connector.close(); + Assert.assertFalse(connector.isStarted()); + } + @Test + public void testOverridesJavaSystemPropertyForceSSLParameters() throws Exception { + BufferHandler handler = new BufferHandler() { + @Override + public void bufferReceived(final Object connectionID, final ActiveMQBuffer buffer) { + } + }; + + //bad system properties will override the transport constants System.setProperty(NettyConnector.JAVAX_KEYSTORE_PATH_PROP_NAME, "bad path"); System.setProperty(NettyConnector.JAVAX_KEYSTORE_PASSWORD_PROP_NAME, "bad password"); System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PATH_PROP_NAME, "bad path"); System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME, "bad password"); - System.setProperty(NettyConnector.ACTIVEMQ_KEYSTORE_PATH_PROP_NAME, "client-side-keystore.jks"); + Map params = new HashMap<>(); + params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + params.put(TransportConstants.FORCE_SSL_PARAMETERS, true); + params.put(TransportConstants.KEYSTORE_PATH_PROP_NAME, "openssl-client-side-keystore.jks"); + params.put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, "secureexample"); + params.put(TransportConstants.TRUSTSTORE_PATH_PROP_NAME,"openssl-client-side-truststore.jks"); + params.put(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, "secureexample"); + + NettyConnector connector = new NettyConnector(params, handler, listener, executorService, Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()), Executors.newScheduledThreadPool(5, ActiveMQThreadFactory.defaultThreadFactory())); + + connector.start(); + Assert.assertTrue(connector.isStarted()); + Connection c = connector.createConnection(); + + //Should not fail because forceSSLParameters is set + assertNotNull(c); + c.close(); + connector.close(); + Assert.assertFalse(connector.isStarted()); + } + + @Test + public void tesActiveMQSystemProperties() throws Exception { + BufferHandler handler = new BufferHandler() { + @Override + public void bufferReceived(final Object connectionID, final ActiveMQBuffer buffer) { + } + }; + Map params = new HashMap<>(); + params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + + NettyConnector connector = new NettyConnector(params, handler, listener, executorService, Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()), Executors.newScheduledThreadPool(5, ActiveMQThreadFactory.defaultThreadFactory())); + + System.setProperty(NettyConnector.ACTIVEMQ_KEYSTORE_PATH_PROP_NAME, "openssl-client-side-keystore.jks"); System.setProperty(NettyConnector.ACTIVEMQ_KEYSTORE_PASSWORD_PROP_NAME, "secureexample"); - System.setProperty(NettyConnector.ACTIVEMQ_TRUSTSTORE_PATH_PROP_NAME, "client-side-truststore.jks"); + System.setProperty(NettyConnector.ACTIVEMQ_TRUSTSTORE_PATH_PROP_NAME, "openssl-client-side-truststore.jks"); System.setProperty(NettyConnector.ACTIVEMQ_TRUSTSTORE_PASSWORD_PROP_NAME, "secureexample"); connector.start(); Assert.assertTrue(connector.isStarted()); + Connection c = connector.createConnection(); + assertNotNull(c); + connector.close(); + Assert.assertFalse(connector.isStarted()); + } + + @Test + public void testSystemPropertyOverridesActiveMQ() throws Exception { + BufferHandler handler = new BufferHandler() { + @Override + public void bufferReceived(final Object connectionID, final ActiveMQBuffer buffer) { + } + }; + Map params = new HashMap<>(); + params.put(TransportConstants.SSL_ENABLED_PROP_NAME, true); + + NettyConnector connector = new NettyConnector(params, handler, listener, executorService, Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()), Executors.newScheduledThreadPool(5, ActiveMQThreadFactory.defaultThreadFactory())); + + System.setProperty(NettyConnector.JAVAX_KEYSTORE_PATH_PROP_NAME, "openssl-client-side-keystore.jks"); + System.setProperty(NettyConnector.JAVAX_KEYSTORE_PASSWORD_PROP_NAME, "secureexample"); + System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PATH_PROP_NAME, "openssl-client-side-truststore.jks"); + System.setProperty(NettyConnector.JAVAX_TRUSTSTORE_PASSWORD_PROP_NAME, "secureexample"); + + System.setProperty(NettyConnector.ACTIVEMQ_KEYSTORE_PATH_PROP_NAME, "bad path"); + System.setProperty(NettyConnector.ACTIVEMQ_KEYSTORE_PASSWORD_PROP_NAME, "bad password"); + System.setProperty(NettyConnector.ACTIVEMQ_TRUSTSTORE_PATH_PROP_NAME, "bad path"); + System.setProperty(NettyConnector.ACTIVEMQ_TRUSTSTORE_PASSWORD_PROP_NAME, "bad password"); + + connector.start(); + Assert.assertTrue(connector.isStarted()); + Connection c = connector.createConnection(); + assertNotNull(c); connector.close(); Assert.assertFalse(connector.isStarted()); } From 06eb82cb1408a045037be82873c97d235946a761 Mon Sep 17 00:00:00 2001 From: yang wei Date: Mon, 28 May 2018 14:12:21 +0800 Subject: [PATCH 005/207] ARTEMIS-1891 use io executor to send replicate packet After sending pages, the thread will hold the storage manager write lock and send synchronization finished packet(use the parent thread pool) to the backup node. At the same time, thread pool is full bcs they are waiting for the storage manager read lock to write the page or journal, leading to replication starting failure. Here we use io executor to send replicate packet to fix thread pool starvation problem. --- .../artemis/core/replication/ReplicationManager.java | 10 +++++----- .../core/server/impl/SharedNothingLiveActivation.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java index 36422720a07..be5963a3739 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java @@ -120,7 +120,7 @@ public static ADD_OPERATION_TYPE toOperation(boolean isUpdate) { private final Queue pendingTokens = new ConcurrentLinkedQueue<>(); - private final ExecutorFactory executorFactory; + private final ExecutorFactory ioExecutorFactory; private final Executor replicationStream; @@ -142,12 +142,12 @@ public static ADD_OPERATION_TYPE toOperation(boolean isUpdate) { public ReplicationManager(CoreRemotingConnection remotingConnection, final long timeout, final long initialReplicationSyncTimeout, - final ExecutorFactory executorFactory) { - this.executorFactory = executorFactory; + final ExecutorFactory ioExecutorFactory) { + this.ioExecutorFactory = ioExecutorFactory; this.initialReplicationSyncTimeout = initialReplicationSyncTimeout; this.replicatingChannel = remotingConnection.getChannel(CHANNEL_ID.REPLICATION.id, -1); this.remotingConnection = remotingConnection; - this.replicationStream = executorFactory.getExecutor(); + this.replicationStream = ioExecutorFactory.getExecutor(); this.timeout = timeout; } @@ -355,7 +355,7 @@ private OperationContext sendReplicatePacket(final Packet packet, boolean lineUp return null; } - final OperationContext repliToken = OperationContextImpl.getContext(executorFactory); + final OperationContext repliToken = OperationContextImpl.getContext(ioExecutorFactory); if (lineUp) { repliToken.replicationLineUp(); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingLiveActivation.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingLiveActivation.java index 2e289b5acf9..c03fd198131 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingLiveActivation.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingLiveActivation.java @@ -169,7 +169,7 @@ public void startReplication(CoreRemotingConnection rc, ReplicationFailureListener listener = new ReplicationFailureListener(); rc.addCloseListener(listener); rc.addFailureListener(listener); - replicationManager = new ReplicationManager(rc, clusterConnection.getCallTimeout(), replicatedPolicy.getInitialReplicationSyncTimeout(), activeMQServer.getExecutorFactory()); + replicationManager = new ReplicationManager(rc, clusterConnection.getCallTimeout(), replicatedPolicy.getInitialReplicationSyncTimeout(), activeMQServer.getIOExecutorFactory()); replicationManager.start(); Thread t = new Thread(new Runnable() { @Override From 9b7ebef9fd163815a5b4cab1cef4a88b1e7baefa Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 30 May 2018 15:28:37 -0400 Subject: [PATCH 006/207] NO-JIRA Improving Assert.equals count on SessionTest --- .../activemq/artemis/tests/integration/client/SessionTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SessionTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SessionTest.java index 0175caaf43f..e1f995906a6 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SessionTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SessionTest.java @@ -483,8 +483,7 @@ public void testRollbackWithReceive() throws Exception { Assert.assertNotNull(m); m.acknowledge(); clientSession.rollback(); - Wait.waitFor(() -> getMessageCount(q) == 10); - Assert.assertEquals(10, getMessageCount(q)); + Wait.assertEquals(10, () -> getMessageCount(q)); clientSession.close(); sendSession.close(); } From 1ae2784dc6075875b18780fa8ba40f86cb895f7b Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 29 May 2018 22:12:58 -0400 Subject: [PATCH 007/207] ARTEMIS-1858 Expiry messages are not transversing clustering with AMQP --- .../activemq/artemis/api/core/Message.java | 18 ++ .../message/impl/CoreMessageObjectPools.java | 4 + .../protocol/amqp/broker/AMQPMessage.java | 44 ++- .../amqp/broker/AMQPSessionCallback.java | 2 +- .../amqp/converter/AmqpCoreConverter.java | 3 + .../amqp/converter/CoreAmqpConverter.java | 2 +- .../amqp/message/AMQPMessageTest.java | 4 +- .../core/postoffice/impl/PostOfficeImpl.java | 4 +- .../ProtocolsMessageLoadBalancingTest.java | 284 ++++++++++++++++++ .../cluster/crossprotocol/package-info.java | 20 ++ 10 files changed, 366 insertions(+), 19 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/package-info.java diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/Message.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/Message.java index 6ca37ea2020..667f95f673f 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/Message.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/Message.java @@ -355,10 +355,28 @@ default Message setValidatedUserID(String validatedUserID) { String getAddress(); + /** + * Look at {@link #setAddress(SimpleString)} for the doc. + * @param address + * @return + */ Message setAddress(String address); SimpleString getAddressSimpleString(); + /** + * This will set the address on CoreMessage. + * + * Note for AMQPMessages: + * in AMQPMessages this will not really change the address on the message. Instead it will add a property + * on extraProperties which only transverse internally at the broker. + * Whatever you change here it won't affect anything towards the received message. + * + * If you wish to change AMQPMessages address you will have to do it directly at the AMQP Message, however beware + * that AMQPMessages are not supposed to be changed at the broker, so only do it if you know what you are doing. + * @param address + * @return + */ Message setAddress(SimpleString address); long getTimestamp(); diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessageObjectPools.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessageObjectPools.java index 7ee7d0a0b44..4c56eac346d 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessageObjectPools.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessageObjectPools.java @@ -54,4 +54,8 @@ public TypedProperties.TypedPropertiesDecoderPools getPropertiesDecoderPools() { public TypedProperties.TypedPropertiesStringSimpleStringPools getPropertiesStringSimpleStringPools() { return propertiesStringSimpleStringPools.get(); } + + public static SimpleString cachedAddressSimpleString(String address, CoreMessageObjectPools coreMessageObjectPools) { + return SimpleString.toSimpleString(address, coreMessageObjectPools == null ? null : coreMessageObjectPools.getAddressStringSimpleStringPool()); + } } diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java index 2775f774f9d..27d571e00c2 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java @@ -67,6 +67,8 @@ // see https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#section-message-format public class AMQPMessage extends RefCountMessage { + public static final SimpleString ADDRESS_PROPERTY = SimpleString.toSimpleString("_AMQ_AD"); + public static final int DEFAULT_MESSAGE_PRIORITY = 4; public static final int MAX_MESSAGE_PRIORITY = 9; @@ -103,19 +105,20 @@ public class AMQPMessage extends RefCountMessage { * these are properties created by the broker only */ private volatile TypedProperties extraProperties; - public AMQPMessage(long messageFormat, byte[] data) { - this(messageFormat, data, null); + public AMQPMessage(long messageFormat, byte[] data, TypedProperties extraProperties) { + this(messageFormat, data, extraProperties, null); } - public AMQPMessage(long messageFormat, byte[] data, CoreMessageObjectPools coreMessageObjectPools) { - this(messageFormat, ReadableBuffer.ByteBufferReader.wrap(ByteBuffer.wrap(data)), coreMessageObjectPools); + public AMQPMessage(long messageFormat, byte[] data, TypedProperties extraProperties, CoreMessageObjectPools coreMessageObjectPools) { + this(messageFormat, ReadableBuffer.ByteBufferReader.wrap(ByteBuffer.wrap(data)), extraProperties, coreMessageObjectPools); } - public AMQPMessage(long messageFormat, ReadableBuffer data, CoreMessageObjectPools coreMessageObjectPools) { + public AMQPMessage(long messageFormat, ReadableBuffer data, TypedProperties extraProperties, CoreMessageObjectPools coreMessageObjectPools) { this.data = data; this.messageFormat = messageFormat; this.bufferValid = true; this.coreMessageObjectPools = coreMessageObjectPools; + this.extraProperties = extraProperties == null ? null : new TypedProperties(extraProperties); parseHeaders(); } @@ -496,7 +499,7 @@ public org.apache.activemq.artemis.api.core.Message copy() { view.position(messagePaylodStart); view.get(newData, headerEnds, view.remaining()); - AMQPMessage newEncode = new AMQPMessage(this.messageFormat, newData); + AMQPMessage newEncode = new AMQPMessage(this.messageFormat, newData, extraProperties, coreMessageObjectPools); newEncode.setDurable(isDurable()).setMessageID(this.getMessageID()); return newEncode; } @@ -604,26 +607,40 @@ public String getAddress() { return addressSimpleString == null ? null : addressSimpleString.toString(); } + + public SimpleString cachedAddressSimpleString(String address) { + return CoreMessageObjectPools.cachedAddressSimpleString(address, coreMessageObjectPools); + } + @Override public AMQPMessage setAddress(String address) { - this.address = SimpleString.toSimpleString(address, coreMessageObjectPools == null ? null : coreMessageObjectPools.getAddressStringSimpleStringPool()); + setAddress(cachedAddressSimpleString(address)); return this; } @Override public AMQPMessage setAddress(SimpleString address) { this.address = address; + createExtraProperties().putSimpleStringProperty(ADDRESS_PROPERTY, address); return this; } @Override public SimpleString getAddressSimpleString() { if (address == null) { - Properties properties = getProtonMessage().getProperties(); - if (properties != null) { - setAddress(properties.getTo()); - } else { - return null; + TypedProperties extraProperties = getExtraProperties(); + + // we first check if extraProperties is not null, no need to create it just to check it here + if (extraProperties != null) { + address = extraProperties.getSimpleStringProperty(ADDRESS_PROPERTY); + } + + if (address == null) { + // if it still null, it will look for the address on the getTo(); + Properties properties = getProperties(); + if (properties != null && properties.getTo() != null) { + address = cachedAddressSimpleString(properties.getTo()); + } } } return address; @@ -1261,6 +1278,9 @@ public String toString() { ", messageID=" + getMessageID() + ", address=" + getAddress() + ", size=" + getEncodeSize() + + ", applicationProperties=" + getApplicationProperties() + + ", properties=" + getProperties() + + ", extraProperties = " + getExtraProperties() + "]"; } diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java index aac3f2a26a0..6461bb20111 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java @@ -460,7 +460,7 @@ public void serverSend(final ProtonServerReceiverContext context, SimpleString address, int messageFormat, ReadableBuffer data) throws Exception { - AMQPMessage message = new AMQPMessage(messageFormat, data, coreMessageObjectPools); + AMQPMessage message = new AMQPMessage(messageFormat, data, null, coreMessageObjectPools); if (address != null) { message.setAddress(address); } else { diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java index 80969f6655e..d0705795bd1 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java @@ -179,6 +179,9 @@ public static ICoreMessage toCore(AMQPMessage message, CoreMessageObjectPools co TypedProperties properties = message.getExtraProperties(); if (properties != null) { for (SimpleString str : properties.getPropertyNames()) { + if (str.equals(AMQPMessage.ADDRESS_PROPERTY)) { + continue; + } result.getInnerMessage().putBytesProperty(str, properties.getBytesProperty(str)); } } diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java index abda58a7390..66c75a8fe7d 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java @@ -316,7 +316,7 @@ public static AMQPMessage fromCore(ICoreMessage coreMessage) throws Exception { byte[] data = new byte[buffer.writerIndex()]; buffer.readBytes(data); - AMQPMessage amqpMessage = new AMQPMessage(messageFormat, data); + AMQPMessage amqpMessage = new AMQPMessage(messageFormat, data, null); amqpMessage.setMessageID(message.getInnerMessage().getMessageID()); amqpMessage.setReplyTo(coreMessage.getReplyTo()); return amqpMessage; diff --git a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/message/AMQPMessageTest.java b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/message/AMQPMessageTest.java index bff43a848ed..42ffaeeca90 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/message/AMQPMessageTest.java +++ b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/message/AMQPMessageTest.java @@ -291,7 +291,7 @@ private AMQPMessage encodeAndDecodeMessage(MessageImpl message) { byte[] bytes = new byte[nettyBuffer.writerIndex()]; nettyBuffer.readBytes(bytes); - return new AMQPMessage(0, bytes); + return new AMQPMessage(0, bytes, null); } private AMQPMessage encodeDelivery(AMQPMessage message, int deliveryCount) { @@ -302,6 +302,6 @@ private AMQPMessage encodeDelivery(AMQPMessage message, int deliveryCount) { byte[] bytes = new byte[nettyBuffer.writerIndex()]; nettyBuffer.readBytes(bytes); - return new AMQPMessage(0, bytes); + return new AMQPMessage(0, bytes, null); } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index b722532c0f5..0e21da2f16b 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -977,9 +977,7 @@ public Pair redistribute(final Message message, // arrived the target node // as described on https://issues.jboss.org/browse/JBPAPP-6130 Message copyRedistribute = message.copy(storageManager.generateID()); - if (copyRedistribute.getAddress() == null) { - copyRedistribute.setAddress(originatingQueue.getAddress()); - } + copyRedistribute.setAddress(originatingQueue.getAddress()); if (tx != null) { tx.addOperation(new TransactionOperationAbstract() { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java new file mode 100644 index 00000000000..f1d0906433f --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java @@ -0,0 +1,284 @@ +/* + * 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.artemis.tests.integration.cluster.crossprotocol; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import java.util.Arrays; +import java.util.Collection; + +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.core.server.cluster.ClusterConnection; +import org.apache.activemq.artemis.core.server.cluster.MessageFlowRecord; +import org.apache.activemq.artemis.core.server.cluster.impl.ClusterConnectionImpl; +import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; +import org.apache.activemq.artemis.core.settings.impl.AddressSettings; +import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; +import org.apache.activemq.artemis.protocol.amqp.broker.ProtonProtocolManagerFactory; +import org.apache.activemq.artemis.tests.integration.cluster.distribution.ClusterTestBase; +import org.apache.qpid.jms.JmsConnectionFactory; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(value = Parameterized.class) +public class ProtocolsMessageLoadBalancingTest extends ClusterTestBase { + + private static final int NUMBER_OF_SERVERS = 2; + private static final SimpleString queueName = SimpleString.toSimpleString("queues.0"); + + + // I'm taking any number that /2 = Odd + // to avoid perfect roundings and making sure messages are evenly distributed + private static final int NUMBER_OF_MESSAGES = 77 * 2; + + + @Parameterized.Parameters(name = "protocol={0}") + public static Collection getParameters() { + return Arrays.asList(new Object[][]{{"AMQP"}, {"CORE"}}); + } + + @Parameterized.Parameter(0) + public String protocol; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + + } + + private void startServers(MessageLoadBalancingType loadBalancingType) throws Exception { + setupServers(); + + setRedistributionDelay(0); + + setupCluster(loadBalancingType); + + AddressSettings as = new AddressSettings().setRedistributionDelay(0).setExpiryAddress(SimpleString.toSimpleString("queues.expiry")); + + getServer(0).getAddressSettingsRepository().addMatch("queues.*", as); + getServer(1).getAddressSettingsRepository().addMatch("queues.*", as); + + startServers(0); + startServers(1); + + createQueue(SimpleString.toSimpleString("queues.expiry")); + createQueue(queueName); + } + + private void createQueue(SimpleString queueName) throws Exception { + servers[0].createQueue(queueName, RoutingType.ANYCAST, queueName, (SimpleString) null, (SimpleString) null, true, false, false, false, false, -1, false, false, false, true); + servers[1].createQueue(queueName, RoutingType.ANYCAST, queueName, (SimpleString) null, (SimpleString) null, true, false, false, false, false, -1, false, false, false, true); + } + + protected boolean isNetty() { + return true; + } + + private ConnectionFactory getJmsConnectionFactory(int node) { + if (protocol.equals("AMQP")) { + return new JmsConnectionFactory("amqp://localhost:" + (61616 + node)); + } else { + return new ActiveMQConnectionFactory("tcp://localhost:" + (61616 + node)); + } + } + + private void pauseClusteringBridges(ActiveMQServer server) throws Exception { + for (ClusterConnection clusterConnection : server.getClusterManager().getClusterConnections()) { + for (MessageFlowRecord record : ((ClusterConnectionImpl)clusterConnection).getRecords().values()) { + record.getBridge().pause(); + } + } + } + + @Test + public void testLoadBalancing() throws Exception { + + startServers(MessageLoadBalancingType.STRICT); + + ConnectionFactory[] factory = new ConnectionFactory[NUMBER_OF_SERVERS]; + Connection[] connection = new Connection[NUMBER_OF_SERVERS]; + Session[] session = new Session[NUMBER_OF_SERVERS]; + MessageConsumer[] consumer = new MessageConsumer[NUMBER_OF_SERVERS]; + + // this will pre create consumers to make sure messages are distributed evenly without redistribution + for (int node = 0; node < NUMBER_OF_SERVERS; node++) { + factory[node] = getJmsConnectionFactory(node); + connection[node] = factory[node].createConnection(); + session[node] = connection[node].createSession(false, Session.AUTO_ACKNOWLEDGE); + consumer[node] = session[node].createConsumer(session[node].createQueue(queueName.toString())); + } + + waitForBindings(0, "queues.0", 1, 1, true); + waitForBindings(1, "queues.0", 1, 1, true); + + waitForBindings(0, "queues.0", 1, 1, false); + waitForBindings(1, "queues.0", 1, 1, false); + + pauseClusteringBridges(servers[0]); + + + // sending Messages.. they should be load balanced + { + ConnectionFactory cf = getJmsConnectionFactory(0); + Connection cn = cf.createConnection(); + Session sn = cn.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer pd = sn.createProducer(sn.createQueue(queueName.toString())); + + for (int i = 0; i < NUMBER_OF_MESSAGES; i++) { + pd.send(sn.createTextMessage("hello " + i)); + } + + cn.close(); + } + + receiveMessages(connection[0], consumer[0], NUMBER_OF_MESSAGES / 2, true); + connection[1].start(); + Assert.assertNull(consumer[1].receiveNoWait()); + connection[1].stop(); + + servers[0].stop(); + clearServer(0); + + setupServer(0, isFileStorage(), isNetty()); + servers[0].addProtocolManagerFactory(new ProtonProtocolManagerFactory()); + + setupClusterConnection("cluster0", "queues", MessageLoadBalancingType.STRICT, 1, isNetty(), 0, 1); + + servers[0].start(); + + receiveMessages(connection[1], consumer[1], NUMBER_OF_MESSAGES / 2, true); + for (int node = 0; node < NUMBER_OF_SERVERS; node++) { + connection[node].close(); + } + + } + + @Test + public void testExpireRedistributed() throws Exception { + + startServers(MessageLoadBalancingType.ON_DEMAND); + + ConnectionFactory factory = getJmsConnectionFactory(1); + + + waitForBindings(0, "queues.0", 1, 0, true); + waitForBindings(1, "queues.0", 1, 0, true); + + waitForBindings(0, "queues.0", 1, 0, false); + waitForBindings(1, "queues.0", 1, 0, false); + + + // sending Messages.. + { + ConnectionFactory cf = getJmsConnectionFactory(0); + Connection cn = cf.createConnection(); + Session sn = cn.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer pd = sn.createProducer(sn.createQueue(queueName.toString())); + pd.setTimeToLive(200); + + for (int i = 0; i < NUMBER_OF_MESSAGES; i++) { + pd.send(sn.createTextMessage("hello " + i)); + } + + cn.close(); + } + + // time to let stuff expire + Thread.sleep(200); + + + Connection connection = factory.createConnection(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer = session.createConsumer(session.createQueue("queues.expiry")); + + receiveMessages(connection, consumer, NUMBER_OF_MESSAGES, true); + connection.close(); + } + + private void receiveMessages(Connection connection, + MessageConsumer messageConsumer, + int messageCount, + boolean exactCount) throws JMSException { + connection.start(); + + for (int i = 0; i < messageCount; i++) { + Message msg = messageConsumer.receive(5000); + Assert.assertNotNull(msg); + } + + // this means no more messages received + if (exactCount) { + Assert.assertNull(messageConsumer.receiveNoWait()); + } + } + + protected void setupCluster(final MessageLoadBalancingType messageLoadBalancingType) throws Exception { + setupClusterConnection("cluster0", "queues", messageLoadBalancingType, 1, isNetty(), 0, 1); + + setupClusterConnection("cluster1", "queues", messageLoadBalancingType, 1, isNetty(), 1, 0); + } + + protected void setRedistributionDelay(final long delay) { + } + + protected void setupServers() throws Exception { + setupServer(0, isFileStorage(), isNetty()); + setupServer(1, isFileStorage(), isNetty()); + + servers[0].addProtocolManagerFactory(new ProtonProtocolManagerFactory()); + servers[1].addProtocolManagerFactory(new ProtonProtocolManagerFactory()); + } + + protected void stopServers() throws Exception { + closeAllConsumers(); + + closeAllSessionFactories(); + + closeAllServerLocatorsFactories(); + + stopServers(0, 1); + + clearServer(0, 1); + } + + /** + * @param serverID + * @return + * @throws Exception + */ + @Override + protected ConfigurationImpl createBasicConfig(final int serverID) { + ConfigurationImpl configuration = super.createBasicConfig(serverID); + configuration.setMessageExpiryScanPeriod(100); + + return configuration; + } + + +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/package-info.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/package-info.java new file mode 100644 index 00000000000..e9b7c017e8a --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +/** + * This package contains tests about messages crossing over protocols + */ +package org.apache.activemq.artemis.tests.integration.cluster.crossprotocol; \ No newline at end of file From 0ea84ef9ffcd32356cdfa963c03f1fd0decb351c Mon Sep 17 00:00:00 2001 From: Johan Stenberg Date: Sat, 26 May 2018 20:12:26 +0200 Subject: [PATCH 008/207] [ARTEMIS-1890] Fix any-word wildcard matching in AddressImpl to match zero words --- .../core/postoffice/impl/AddressImpl.java | 151 +++++++++++------- .../core/postoffice/impl/AddressImplTest.java | 11 ++ 2 files changed, 106 insertions(+), 56 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/AddressImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/AddressImpl.java index ea78e4f748f..aef2c10caca 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/AddressImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/AddressImpl.java @@ -24,10 +24,12 @@ import org.apache.activemq.artemis.core.postoffice.Address; /** - * splits an address string into its hierarchical parts split by '.' + * Splits an address string into its hierarchical parts using {@link WildcardConfiguration#getDelimiter()} as delimiter. */ public class AddressImpl implements Address { + private static final WildcardConfiguration DEFAULT_WILDCARD_CONFIGURATION = new WildcardConfiguration(); + private final SimpleString address; private final SimpleString[] addressParts; @@ -39,10 +41,10 @@ public class AddressImpl implements Address { private final WildcardConfiguration wildcardConfiguration; public AddressImpl(final SimpleString address) { - this(address, new WildcardConfiguration()); + this(address, DEFAULT_WILDCARD_CONFIGURATION); } - public AddressImpl(final SimpleString address, WildcardConfiguration wildcardConfiguration) { + public AddressImpl(final SimpleString address, final WildcardConfiguration wildcardConfiguration) { this.address = address; this.wildcardConfiguration = wildcardConfiguration; addressParts = address.split(wildcardConfiguration.getDelimiter()); @@ -81,76 +83,113 @@ public void removeLinkedAddress(final Address actualAddress) { linkedAddresses.remove(actualAddress); } + /** + * This method should actually be called `isMatchedBy`. + * + * @return `true` if this equals otherAddr or this address is matched by a pattern represented by otherAddr + */ @Override - public boolean matches(final Address add) { - if (containsWildCard == add.containsWildCard()) { - return address.equals(add.getAddress()); - } - int pos = 0; - int matchPos = 0; - - SimpleString nextToMatch; - for (; matchPos < add.getAddressParts().length; ) { - if (pos >= addressParts.length) { - // test for # as last address part - return pos + 1 == add.getAddressParts().length && add.getAddressParts()[pos].equals(new SimpleString(wildcardConfiguration.getAnyWords())); + public boolean matches(final Address otherAddr) { + if (otherAddr == null) + return false; + + if (address.equals(otherAddr.getAddress())) + return true; + + final char sepAnyWords = wildcardConfiguration.getAnyWords(); + final char sepSingleWord = wildcardConfiguration.getSingleWord(); + + final int thisAddrPartsLen = addressParts.length; + final int thisAddrPartsLastIdx = thisAddrPartsLen - 1; + + final SimpleString[] otherAddrParts = otherAddr.getAddressParts(); + final int otherAddrPartsLen = otherAddrParts.length; + final int otherAddrPartsLastIdx = otherAddrPartsLen - 1; + + int thisIdx = 0; + int otherIdx = 0; + + // iterate through all parts of otherAddr + while (otherIdx < otherAddrPartsLen) { + + // check if we already tested the last part of this address + if (thisIdx > thisAddrPartsLastIdx) { + // check if last part of otherAddr is the any-words wildcard and report a match if so + if (otherIdx == otherAddrPartsLastIdx) { + final SimpleString otherAddrLastPart = otherAddrParts[otherAddrPartsLastIdx]; + return otherAddrLastPart.length() > 0 && otherAddrLastPart.charAt(0) == sepAnyWords; + } + return false; } - SimpleString curr = addressParts[pos]; - SimpleString next = addressParts.length > pos + 1 ? addressParts[pos + 1] : null; - SimpleString currMatch = add.getAddressParts()[matchPos]; - if (currMatch.equals(new SimpleString(wildcardConfiguration.getSingleWord()))) { - pos++; - matchPos++; - } else if (currMatch.equals(new SimpleString(wildcardConfiguration.getAnyWords()))) { - if (matchPos == addressParts.length - 1) { - pos++; - matchPos++; - } else if (next == null) { - return false; - } else if (matchPos == add.getAddressParts().length - 1) { + + SimpleString thisCurr = addressParts[thisIdx]; + final SimpleString otherCurr = otherAddrParts[otherIdx]; + final boolean otherCurrPartIsSingleChar = otherCurr.length() == 1; + + // handle single-word wildcard found in otherAddr + if (otherCurrPartIsSingleChar && otherCurr.charAt(0) == sepSingleWord) { + thisIdx++; + otherIdx++; + continue; + } + + // handle any-words wildcard found in otherAddr + if (otherCurrPartIsSingleChar && otherCurr.charAt(0) == sepAnyWords) { + + // if last part of otherAddr is any-words wildcard report a match + if (otherIdx == otherAddrPartsLastIdx) return true; + + SimpleString thisNext; + // check if this address has more parts to check + if (thisIdx < thisAddrPartsLastIdx) { + thisNext = addressParts[thisIdx + 1]; } else { - nextToMatch = add.getAddressParts()[matchPos + 1]; - while (curr != null) { - if (curr.equals(nextToMatch)) { - break; - } - pos++; - curr = next; - next = addressParts.length > pos + 1 ? addressParts[pos + 1] : null; - } - if (curr == null) { - return false; + // no more parts to check, thus check the current part against the next part of otherAddr + thisNext = thisCurr; + } + + final SimpleString otherNext = otherAddrParts[otherIdx + 1]; + // iterate through the remaining parts of this address until the part after the any-words wildcard of otherAddr is matched + while (thisCurr != null) { + if (thisCurr.equals(otherNext)) { + break; } - matchPos++; + thisIdx++; + thisCurr = thisNext; + thisNext = thisAddrPartsLastIdx > thisIdx ? addressParts[thisIdx + 1] : null; } - } else { - if (!curr.equals(currMatch)) { + // if no further part in this address matched the next part in otherAddr report a mismatch + if (thisCurr == null) return false; - } - pos++; - matchPos++; + otherIdx++; + continue; } + + // compare current parts of bothaddresses and report mismatch if they differ + if (!thisCurr.equals(otherCurr)) + return false; + + thisIdx++; + otherIdx++; } - return pos == addressParts.length; + + // report match if all parts of this address were checked + return thisIdx == thisAddrPartsLen; } @Override public boolean equals(final Object o) { - if (this == o) { + if (this == o) return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - AddressImpl address1 = (AddressImpl) o; - - if (!address.equals(address1.address)) { + if (o == null || getClass() != o.getClass()) return false; - } - return true; + if (address.equals(((AddressImpl) o).address)) + return true; + + return false; } @Override diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/AddressImplTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/AddressImplTest.java index 9c3b37912a6..b969d788f59 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/AddressImplTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/AddressImplTest.java @@ -274,4 +274,15 @@ public void testU() { Assert.assertFalse(a1.matches(w)); } + /** + * https://issues.apache.org/jira/browse/ARTEMIS-1890 + */ + @Test + public void testV() { + final SimpleString s1 = new SimpleString("a.b.d"); + final SimpleString s3 = new SimpleString("a.b.#.d"); + final Address a1 = new AddressImpl(s1); + final Address w = new AddressImpl(s3); + Assert.assertTrue(a1.matches(w)); + } } From cd9aed0826f943d73af294d3dc8495e56279ecb8 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Thu, 31 May 2018 21:50:53 -0400 Subject: [PATCH 009/207] NO-JIRA update release instruction with git-release-report --- RELEASING.md | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 4a27de5480c..d3a5e7ea1b4 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -165,13 +165,32 @@ svn commit Old staged releases can be cleaned out periodically. +## Generate the git-report and cleanup JIRA + +Please, include the git-commit-report as part of the release proces. To generate it follow these steps: + +- Download [git-release-report](https://github.com/clebertsuconic/git-release-report/releases) +- Execute the following command: +```bash +$ java -jar git-release-report.jar /activemq-artemis /artemis-website commit-report-.html true +``` + +real example used on [2.6.1](http://activemq.apache.org/artemis/commit-report-2.6.1.html): +```bash +$ java -jar git-release-report.jar /work/apache-checkout/activemq-artemis commit-report-2.6.1.html 2.6.0 2.6.1 true +``` +- This will parse all the git commits between the previous release, and current release tags while looking at current JIRA status. +- Use this report to do some JIRA cleanup making sure your commits and JIRA are accurate. +- Regenerate the report once you cleared JIRA. + ## Locate Release Notes -1. Go to the "Releases" page for the Artemis JIRA project: https://issues.apache.org/jira/projects/ARTEMIS?selectedItem=com.atlassian.jira.jira-projects-plugin:release-page -2. Click on the version being released. -3. Click the "Release Notes" link near the top of the page. -4. Grab the URL to put into the VOTE email. +1. Use the previous report to cleanup JIRA +2. Go to the "Releases" page for the Artemis JIRA project: https://issues.apache.org/jira/projects/ARTEMIS?selectedItem=com.atlassian.jira.jira-projects-plugin:release-page +3. Click on the version being released. +4. Click the "Release Notes" link near the top of the page. +5. Grab the URL to put into the VOTE email. ## Send Email @@ -190,6 +209,9 @@ We added these new features as part of : The release notes can be found here: https://issues.apache.org/jira/secure/ReleaseNote.jspa?version=&projectId=12315920 +Ths git commit report is here: +http://activemq.apache.org/artemis/commit-report-.html + Source and binary distributions can be found here: https://dist.apache.org/repos/dist/dev/activemq/activemq-artemis// From dde60b136a8ab2922bf9455abe6a78580ea0c442 Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Fri, 1 Jun 2018 16:33:18 +0100 Subject: [PATCH 010/207] ARTEMIS-1902 Ensure ServerConsumer close done once Calling close multiple times on ServerConsumer can result in multiple notifications being routed around the cluster. This causes cluster topology info to become skewed. Which affects a number of components such as message redistribution, metrics and can eventually cause OOM should multiple queues be redistributing at the same time. --- .../artemis/core/postoffice/QueueInfo.java | 22 ++++- .../core/postoffice/impl/PostOfficeImpl.java | 3 +- .../core/server/ActiveMQServerLogger.java | 4 + .../core/server/impl/ServerConsumerImpl.java | 9 +- .../ProtocolsMessageLoadBalancingTest.java | 92 ++++++++++++++++--- 5 files changed, 108 insertions(+), 22 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/QueueInfo.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/QueueInfo.java index a953fc2e71c..581d6482568 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/QueueInfo.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/QueueInfo.java @@ -18,9 +18,11 @@ import java.io.Serializable; import java.util.List; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle; +import org.apache.activemq.artemis.core.server.ActiveMQServerLogger; public class QueueInfo implements Serializable { @@ -38,7 +40,9 @@ public class QueueInfo implements Serializable { private List filterStrings; - private int numberOfConsumers; + private volatile int consumersCount = 0; + + private static final AtomicIntegerFieldUpdater consumerUpdater = AtomicIntegerFieldUpdater.newUpdater(QueueInfo.class, "consumersCount"); private final int distance; @@ -99,15 +103,23 @@ public void setFilterStrings(final List filterStrings) { } public int getNumberOfConsumers() { - return numberOfConsumers; + return consumerUpdater.get(this); } public void incrementConsumers() { - numberOfConsumers++; + consumerUpdater.incrementAndGet(this); } public void decrementConsumers() { - numberOfConsumers--; + + consumerUpdater.getAndUpdate(this, value -> { + if (value > 0) { + return value--; + } else { + ActiveMQServerLogger.LOGGER.consumerCountError("Tried to decrement consumer count below 0: " + this); + return value; + } + }); } public boolean matchesAddress(SimpleString address) { @@ -144,7 +156,7 @@ public String toString() { ", filterStrings=" + filterStrings + ", numberOfConsumers=" + - numberOfConsumers + + consumersCount + ", distance=" + distance + "]"; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index 0e21da2f16b..5fbb2d8dc92 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -376,7 +376,8 @@ public void onNotification(final Notification notification) { filterStrings.remove(filterString); } - if (info.getNumberOfConsumers() == 0) { + // The consumer count should never be < 0 but we should catch here just in case. + if (info.getNumberOfConsumers() <= 0) { if (!props.containsProperty(ManagementHelper.HDR_DISTANCE)) { logger.debug("PostOffice notification / CONSUMER_CLOSED: HDR_DISTANCE not defined"); return; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java index 677f1acb310..10111964f8f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java @@ -1946,4 +1946,8 @@ void slowConsumerDetected(String sessionID, @LogMessage(level = Logger.Level.TRACE) @Message(id = 224094, value = "Quorum vote result await is interrupted", format = Message.Format.MESSAGE_FORMAT) void quorumVoteAwaitInterrupted(); + + @LogMessage(level = Logger.Level.ERROR) + @Message(id = 224095, value = "Error updating Consumer Count: {0}", format = Message.Format.MESSAGE_FORMAT) + void consumerCountError(String reason); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java index c81105a7341..22bfdaf17e3 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java @@ -157,6 +157,8 @@ public String debug() { private boolean anycast = false; + private boolean isClosed = false; + // Constructors --------------------------------------------------------------------------------- public ServerConsumerImpl(final long id, @@ -477,7 +479,12 @@ public Filter getFilter() { } @Override - public void close(final boolean failed) throws Exception { + public synchronized void close(final boolean failed) throws Exception { + + // Close should only ever be done once per consumer. + if (isClosed) return; + isClosed = true; + if (logger.isTraceEnabled()) { logger.trace("ServerConsumerImpl::" + this + " being closed with failed=" + failed, new Exception("trace")); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java index f1d0906433f..1c981575085 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java @@ -29,6 +29,7 @@ import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.protocol.openwire.OpenWireProtocolManagerFactory; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.cluster.ClusterConnection; import org.apache.activemq.artemis.core.server.cluster.MessageFlowRecord; @@ -51,15 +52,13 @@ public class ProtocolsMessageLoadBalancingTest extends ClusterTestBase { private static final int NUMBER_OF_SERVERS = 2; private static final SimpleString queueName = SimpleString.toSimpleString("queues.0"); - // I'm taking any number that /2 = Odd // to avoid perfect roundings and making sure messages are evenly distributed private static final int NUMBER_OF_MESSAGES = 77 * 2; - @Parameterized.Parameters(name = "protocol={0}") public static Collection getParameters() { - return Arrays.asList(new Object[][]{{"AMQP"}, {"CORE"}}); + return Arrays.asList(new Object[][]{{"AMQP"}, {"CORE"}, {"OPENWIRE"}}); } @Parameterized.Parameter(0) @@ -103,14 +102,19 @@ protected boolean isNetty() { private ConnectionFactory getJmsConnectionFactory(int node) { if (protocol.equals("AMQP")) { return new JmsConnectionFactory("amqp://localhost:" + (61616 + node)); - } else { + } else if (protocol.equals("OPENWIRE")) { + return new org.apache.activemq.ActiveMQConnectionFactory("tcp://localhost:" + (61616 + node)); + } else if (protocol.equals("CORE")) { return new ActiveMQConnectionFactory("tcp://localhost:" + (61616 + node)); + } else { + Assert.fail("Protocol " + protocol + " unkown"); + return null; } } private void pauseClusteringBridges(ActiveMQServer server) throws Exception { for (ClusterConnection clusterConnection : server.getClusterManager().getClusterConnections()) { - for (MessageFlowRecord record : ((ClusterConnectionImpl)clusterConnection).getRecords().values()) { + for (MessageFlowRecord record : ((ClusterConnectionImpl) clusterConnection).getRecords().values()) { record.getBridge().pause(); } } @@ -123,8 +127,8 @@ public void testLoadBalancing() throws Exception { ConnectionFactory[] factory = new ConnectionFactory[NUMBER_OF_SERVERS]; Connection[] connection = new Connection[NUMBER_OF_SERVERS]; - Session[] session = new Session[NUMBER_OF_SERVERS]; - MessageConsumer[] consumer = new MessageConsumer[NUMBER_OF_SERVERS]; + Session[] session = new Session[NUMBER_OF_SERVERS]; + MessageConsumer[] consumer = new MessageConsumer[NUMBER_OF_SERVERS]; // this will pre create consumers to make sure messages are distributed evenly without redistribution for (int node = 0; node < NUMBER_OF_SERVERS; node++) { @@ -142,10 +146,9 @@ public void testLoadBalancing() throws Exception { pauseClusteringBridges(servers[0]); - // sending Messages.. they should be load balanced { - ConnectionFactory cf = getJmsConnectionFactory(0); + ConnectionFactory cf = getJmsConnectionFactory(0); Connection cn = cf.createConnection(); Session sn = cn.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer pd = sn.createProducer(sn.createQueue(queueName.toString())); @@ -180,23 +183,82 @@ public void testLoadBalancing() throws Exception { } @Test - public void testExpireRedistributed() throws Exception { - + public void testRedistributionStoppedWithNoRemoteConsumers() throws Exception { startServers(MessageLoadBalancingType.ON_DEMAND); ConnectionFactory factory = getJmsConnectionFactory(1); - + // Wait for cluster nodes to sync waitForBindings(0, "queues.0", 1, 0, true); waitForBindings(1, "queues.0", 1, 0, true); waitForBindings(0, "queues.0", 1, 0, false); waitForBindings(1, "queues.0", 1, 0, false); + // Create CFs + ConnectionFactory cf0 = getJmsConnectionFactory(0); + ConnectionFactory cf1 = getJmsConnectionFactory(1); + + // Create Consumers + Connection cn0 = cf0.createConnection(); + Session sn0 = cn0.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer c0 = sn0.createConsumer(sn0.createQueue(queueName.toString())); + cn0.start(); + + Connection cn1 = cf1.createConnection(); + Session sn1 = cn1.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer c1 = sn1.createConsumer(sn0.createQueue(queueName.toString())); + cn1.start(); + + MessageProducer pd = sn0.createProducer(sn0.createQueue(queueName.toString())); + + // Wait for cluster nodes to sync consumer count + waitForBindings(0, "queues.0", 1, 1, false); + waitForBindings(1, "queues.0", 1, 1, false); + + // Start queue redistributor + c0.close(); + + // Send Messages to node1. + for (int i = 0; i < NUMBER_OF_MESSAGES; i++) { + pd.send(sn0.createTextMessage("hello " + i)); + } + + // Ensure the messages are redistributed from node0 to node1. + for (int i = 0; i < NUMBER_OF_MESSAGES; i++) { + assertNotNull(c1.receive(1000)); + } + + // Close client on node1. This should make the node0 stop redistributing. + c1.close(); + sn1.close(); + cn1.close(); + + // Send more messages (these should stay in node0) + for (int i = 0; i < NUMBER_OF_MESSAGES; i++) { + pd.send(sn0.createTextMessage("hello " + i)); + } + + // Messages should stay in node 1 and note get redistributed. + assertEquals(NUMBER_OF_MESSAGES, servers[0].locateQueue(queueName).getMessageCount()); + assertEquals(0, servers[1].locateQueue(queueName).getMessageCount()); + } + + @Test + public void testExpireRedistributed() throws Exception { + startServers(MessageLoadBalancingType.ON_DEMAND); + + ConnectionFactory factory = getJmsConnectionFactory(1); + + waitForBindings(0, "queues.0", 1, 0, true); + waitForBindings(1, "queues.0", 1, 0, true); + + waitForBindings(0, "queues.0", 1, 0, false); + waitForBindings(1, "queues.0", 1, 0, false); // sending Messages.. { - ConnectionFactory cf = getJmsConnectionFactory(0); + ConnectionFactory cf = getJmsConnectionFactory(0); Connection cn = cf.createConnection(); Session sn = cn.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer pd = sn.createProducer(sn.createQueue(queueName.toString())); @@ -212,7 +274,6 @@ public void testExpireRedistributed() throws Exception { // time to let stuff expire Thread.sleep(200); - Connection connection = factory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer consumer = session.createConsumer(session.createQueue("queues.expiry")); @@ -253,6 +314,8 @@ protected void setupServers() throws Exception { servers[0].addProtocolManagerFactory(new ProtonProtocolManagerFactory()); servers[1].addProtocolManagerFactory(new ProtonProtocolManagerFactory()); + servers[0].addProtocolManagerFactory(new OpenWireProtocolManagerFactory()); + servers[1].addProtocolManagerFactory(new OpenWireProtocolManagerFactory()); } protected void stopServers() throws Exception { @@ -280,5 +343,4 @@ protected ConfigurationImpl createBasicConfig(final int serverID) { return configuration; } - } From 40ade11981135709e7299a295d1f6c8b9de0f4b3 Mon Sep 17 00:00:00 2001 From: "Christopher L. Shannon (cshannon)" Date: Wed, 30 May 2018 08:50:05 -0400 Subject: [PATCH 011/207] ARTEMIS-1895 - Add duplicate metadata failure callback to ActiveMQServerPlugin Add a callback on duplicate metadata which will allow extra functionality to be added. --- .../jms/client/ActiveMQConnection.java | 25 +++++----- .../core/server/impl/ServerSessionImpl.java | 3 ++ .../server/plugin/ActiveMQServerPlugin.java | 12 +++++ .../SessionMetadataAddExceptionTest.java | 48 +++++++++++++++++++ 4 files changed, 77 insertions(+), 11 deletions(-) diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java index bf0d236c8bb..357b7f6cfde 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java @@ -16,6 +16,14 @@ */ package org.apache.activemq.artemis.jms.client; +import java.lang.ref.WeakReference; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + import javax.jms.ConnectionConsumer; import javax.jms.ConnectionMetaData; import javax.jms.Destination; @@ -32,13 +40,6 @@ import javax.jms.Topic; import javax.jms.TopicConnection; import javax.jms.TopicSession; -import java.lang.ref.WeakReference; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; @@ -242,10 +243,9 @@ public void setClientID(final String clientID) throws JMSException { throw new IllegalStateException("setClientID can only be called directly after the connection is created"); } - validateClientID(initialSession, clientID); - - this.clientID = clientID; try { + validateClientID(initialSession, clientID); + this.clientID = clientID; this.addSessionMetaData(initialSession); } catch (ActiveMQException e) { JMSException ex = new JMSException("Internal error setting metadata jms-client-id"); @@ -257,12 +257,15 @@ public void setClientID(final String clientID) throws JMSException { justCreated = false; } - private void validateClientID(ClientSession validateSession, String clientID) throws InvalidClientIDException { + private void validateClientID(ClientSession validateSession, String clientID) + throws InvalidClientIDException, ActiveMQException { try { validateSession.addUniqueMetaData(ClientSession.JMS_SESSION_CLIENT_ID_PROPERTY, clientID); } catch (ActiveMQException e) { if (e.getType() == ActiveMQExceptionType.DUPLICATE_METADATA) { throw new InvalidClientIDException("clientID=" + clientID + " was already set into another connection"); + } else { + throw e; } } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java index 30570418271..f00fe1916a7 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java @@ -1523,6 +1523,9 @@ public boolean addUniqueMetaData(String key, String data) throws Exception { ServerSession sessionWithMetaData = server.lookupSession(key, data); if (sessionWithMetaData != null && sessionWithMetaData != this) { // There is a duplication of this property + if (server.hasBrokerPlugins()) { + server.callBrokerPlugins(plugin -> plugin.duplicateSessionMetadataFailure(this, key, data)); + } return false; } else { addMetaData(key, data); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java index db66bfe7725..97e23b48fe3 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java @@ -157,6 +157,18 @@ default void beforeSessionMetadataAdded(ServerSession session, String key, Strin } + /** + * Called when adding session metadata fails because the metadata is a duplicate + * + * @param session + * @param key + * @param data + * @throws ActiveMQException + */ + default void duplicateSessionMetadataFailure(ServerSession session, String key, String data) throws ActiveMQException { + + } + /** * After session metadata is added to the session * diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/SessionMetadataAddExceptionTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/SessionMetadataAddExceptionTest.java index 4bc052295e5..39a7cc013cd 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/SessionMetadataAddExceptionTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/SessionMetadataAddExceptionTest.java @@ -16,7 +16,10 @@ */ package org.apache.activemq.artemis.tests.integration.jms.client; +import java.util.concurrent.atomic.AtomicInteger; + import javax.jms.Connection; +import javax.jms.InvalidClientIDException; import javax.jms.JMSException; import org.apache.activemq.artemis.api.core.ActiveMQException; @@ -33,9 +36,12 @@ * is added */ public class SessionMetadataAddExceptionTest extends JMSTestBase { + private AtomicInteger duplicateCount = new AtomicInteger(); @Override protected Configuration createDefaultConfig(boolean netty) throws Exception { + duplicateCount.set(0); + Configuration config = super.createDefaultConfig(netty); config.registerBrokerPlugin(new ActiveMQServerPlugin() { @@ -50,6 +56,18 @@ public void beforeSessionMetadataAdded(ServerSession session, String key, String } } + @Override + public void duplicateSessionMetadataFailure(ServerSession session, String key, String data) + throws ActiveMQException { + + //count number of times method called + duplicateCount.incrementAndGet(); + + if (data.equals("valid2")) { + throw new ActiveMQException("failure"); + } + } + }); return config; @@ -75,6 +93,36 @@ public void testInvalidClientIdSetFactory() throws Exception { cf.createConnection(); } + @Test(timeout = 5000) + public void testDuplicateClientIdSet() throws Exception { + ActiveMQConnectionFactory activeMQConnectionFactory = (ActiveMQConnectionFactory) cf; + Connection con = cf.createConnection(); + Connection con2 = cf.createConnection(); + try { + con.setClientID("valid"); + con2.setClientID("valid"); + fail("Should have failed for duplicate clientId"); + } catch (InvalidClientIDException e) { + assertEquals(1, duplicateCount.get()); + } finally { + activeMQConnectionFactory.close(); + } + } + + @Test(timeout = 5000, expected = JMSException.class) + public void testDuplicateClientIdSetActiveMQException() throws Exception { + ActiveMQConnectionFactory activeMQConnectionFactory = (ActiveMQConnectionFactory) cf; + Connection con = cf.createConnection(); + Connection con2 = cf.createConnection(); + try { + con.setClientID("valid2"); + con2.setClientID("valid2"); + fail("Should have failed"); + } finally { + activeMQConnectionFactory.close(); + } + } + @Test(timeout = 5000) public void testValidIdSetConnection() throws Exception { Connection con = cf.createConnection(); From 9183172de348b1e376b46329da8bdcada8ff1bfc Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 1 Jun 2018 12:26:12 -0500 Subject: [PATCH 012/207] ARTEMIS-1892 allow whitespace in acceptor and connector URIs --- .../artemis/core/config/ConfigurationUtils.java | 7 ++++++- .../resources/ConfigurationTest-full-config.xml | 13 +++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java index 1076611e7ce..6d4661b4626 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java @@ -141,6 +141,9 @@ public static void validateConfiguration(Configuration configuration) { public static List parseAcceptorURI(String name, String uri) { try { + // remove all whitespace + uri = uri.replaceAll("\\s", ""); + AcceptorTransportConfigurationParser parser = new AcceptorTransportConfigurationParser(); List configurations = parser.newObject(parser.expandURI(uri), name); @@ -165,8 +168,10 @@ public static List parseAcceptorURI(String name, URI uri } public static List parseConnectorURI(String name, String uri) { - try { + // remove all whitespace + uri = uri.replaceAll("\\s", ""); + ConnectorTransportConfigurationParser parser = new ConnectorTransportConfigurationParser(); List configurations = parser.newObject(parser.expandURI(uri), name); diff --git a/artemis-server/src/test/resources/ConfigurationTest-full-config.xml b/artemis-server/src/test/resources/ConfigurationTest-full-config.xml index 491b0c2c419..eb561fcdfe6 100644 --- a/artemis-server/src/test/resources/ConfigurationTest-full-config.xml +++ b/artemis-server/src/test/resources/ConfigurationTest-full-config.xml @@ -71,11 +71,20 @@ true - tcp://localhost1:5678?localAddress=mylocal;localPort=99 + + tcp://localhost1:5678? + localAddress=mylocal; + localPort=99 + vm://5 - tcp://0.0.0.0:61616?tcpNoDelay=456;connectionTtl=44;connectionsAllowed=92 + + tcp://0.0.0.0:61616? + tcpNoDelay=456; + connectionTtl=44; + connectionsAllowed=92 + vm://0?e1=z1;e2=567;connectionsAllowed=87 From ef6e3948fd416996059fdd83568388c2eb44ab53 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 31 May 2018 10:20:09 -0500 Subject: [PATCH 013/207] ARTEMIS-1896 centralize authn failure logging Authentication failures are currently only logged for CORE clients. This change puts the logging in a central location which all protocols use for authentication so that authentication failures are logged for all protocols. --- .../core/protocol/core/impl/ActiveMQPacketHandler.java | 1 - .../artemis/core/security/impl/SecurityStoreImpl.java | 7 ++++++- .../activemq/artemis/core/server/ActiveMQServerLogger.java | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQPacketHandler.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQPacketHandler.java index ebd1843e2d1..e12ca0c4c5b 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQPacketHandler.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQPacketHandler.java @@ -175,7 +175,6 @@ private void handleCreateSession(final CreateSessionMessage request) { response = new CreateSessionResponseMessage(server.getVersion().getIncrementingVersion()); } catch (ActiveMQClusterSecurityException | ActiveMQSecurityException e) { - ActiveMQServerLogger.LOGGER.securityProblemWhileCreatingSession(e.getMessage()); response = new ActiveMQExceptionMessage(e); } catch (ActiveMQException e) { if (e.getType() == ActiveMQExceptionType.INCOMPATIBLE_CLIENT_SERVER_VERSIONS) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java index 4ef5d5f6225..3b94b13f3f2 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java @@ -30,6 +30,7 @@ import org.apache.activemq.artemis.core.security.SecurityAuth; import org.apache.activemq.artemis.core.security.SecurityStore; import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle; +import org.apache.activemq.artemis.core.server.ActiveMQServerLogger; import org.apache.activemq.artemis.core.server.management.Notification; import org.apache.activemq.artemis.core.server.management.NotificationService; import org.apache.activemq.artemis.core.settings.HierarchicalRepository; @@ -149,7 +150,11 @@ public String authenticate(final String user, certSubjectDN = certs[0].getSubjectDN().getName(); } - throw ActiveMQMessageBundle.BUNDLE.unableToValidateUser(connection.getRemoteAddress(), user, certSubjectDN); + Exception e = ActiveMQMessageBundle.BUNDLE.unableToValidateUser(connection.getRemoteAddress(), user, certSubjectDN); + + ActiveMQServerLogger.LOGGER.securityProblemWhileAuthenticating(e.getMessage()); + + throw e; } return validatedUser; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java index 10111964f8f..50e89a2f3ac 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java @@ -1361,8 +1361,8 @@ void slowConsumerDetected(String sessionID, void negativeGlobalAddressSize(long size); @LogMessage(level = Logger.Level.WARN) - @Message(id = 222216, value = "Security problem while creating session: {0}", format = Message.Format.MESSAGE_FORMAT) - void securityProblemWhileCreatingSession(String message); + @Message(id = 222216, value = "Security problem while authenticating: {0}", format = Message.Format.MESSAGE_FORMAT) + void securityProblemWhileAuthenticating(String message); @LogMessage(level = Logger.Level.WARN) @Message(id = 222217, value = "Cannot find connector-ref {0}. The cluster-connection {1} will not be deployed.", format = Message.Format.MESSAGE_FORMAT) From 45ebe4e63d2848e11ccfc2e697cea87216a0b81a Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 1 Jun 2018 08:42:22 -0500 Subject: [PATCH 014/207] NO-JIRA allow enabling security at runtime --- .../activemq/artemis/core/security/SecurityStore.java | 2 ++ .../artemis/core/security/impl/SecurityStoreImpl.java | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/SecurityStore.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/SecurityStore.java index 301356e1d78..b3ccc3444e7 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/SecurityStore.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/SecurityStore.java @@ -29,5 +29,7 @@ public interface SecurityStore { boolean isSecurityEnabled(); + void setSecurityEnabled(boolean securityEnabled); + void stop(); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java index 3b94b13f3f2..99dcc628e69 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java @@ -60,7 +60,7 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC private volatile long lastCheck; - private final boolean securityEnabled; + private boolean securityEnabled; private final String managementClusterUser; @@ -97,6 +97,11 @@ public boolean isSecurityEnabled() { return securityEnabled; } + @Override + public void setSecurityEnabled(boolean securityEnabled) { + this.securityEnabled = securityEnabled; + } + @Override public void stop() { securityRepository.unRegisterListener(this); From c1b0f1eb853c94e6a5bf31c0c0771025b7da7936 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 1 Jun 2018 08:42:47 -0500 Subject: [PATCH 015/207] ARTEMIS-1897 use core session for STOMP authn --- .../core/protocol/stomp/StompConnection.java | 46 ++++++------ .../protocol/stomp/StompProtocolManager.java | 57 ++++----------- .../stomp/VersionedStompFrameHandler.java | 22 ++++-- .../stomp/v10/StompFrameHandlerV10.java | 36 ++++------ .../stomp/v11/StompFrameHandlerV11.java | 70 +++++++++---------- .../integration/stomp/v11/StompV11Test.java | 2 +- .../integration/stomp/v12/StompV12Test.java | 2 +- 7 files changed, 104 insertions(+), 131 deletions(-) diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java index 171d7be59e3..78cd21b1417 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java @@ -16,6 +16,7 @@ */ package org.apache.activemq.artemis.core.protocol.stomp; +import javax.security.auth.Subject; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; @@ -31,6 +32,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQQueueExistsException; +import org.apache.activemq.artemis.api.core.ActiveMQSecurityException; import org.apache.activemq.artemis.api.core.ICoreMessage; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; @@ -53,8 +55,6 @@ import org.apache.activemq.artemis.utils.ExecutorFactory; import org.apache.activemq.artemis.utils.VersionLoader; -import javax.security.auth.Subject; - import static org.apache.activemq.artemis.core.protocol.stomp.ActiveMQStompProtocolMessageBundle.BUNDLE; public final class StompConnection implements RemotingConnection { @@ -269,9 +269,8 @@ public void checkDestination(String destination) throws ActiveMQStompException { } public void autoCreateDestinationIfPossible(String queue, RoutingType routingType) throws ActiveMQStompException { - ServerSession session = getSession().getCoreSession(); - try { + ServerSession session = getSession().getCoreSession(); SimpleString simpleQueue = SimpleString.toSimpleString(queue); AddressInfo addressInfo = manager.getServer().getAddressInfo(simpleQueue); AddressSettings addressSettings = manager.getServer().getAddressSettingsRepository().getMatch(queue); @@ -437,10 +436,18 @@ public String getLogin() { return login; } + public void setLogin(String login) { + this.login = login; + } + public String getPasscode() { return passcode; } + public void setPasscode(String passcode) { + this.passcode = passcode; + } + @Override public void setClientID(String clientID) { this.clientID = clientID; @@ -584,24 +591,15 @@ public void sendFrame(StompFrame frame, StompPostReceiptFunction function) { manager.sendReply(this, frame, function); } - public boolean validateUser(final String login, final String pass, final RemotingConnection connection) { - this.valid = manager.validateUser(login, pass, connection); - if (valid) { - this.login = login; - this.passcode = pass; - } - return valid; - } - public CoreMessage createServerMessage() { return manager.createServerMessage(); } - public StompSession getSession() throws ActiveMQStompException { + public StompSession getSession() throws ActiveMQStompException, ActiveMQSecurityException { return getSession(null); } - public StompSession getSession(String txID) throws ActiveMQStompException { + public StompSession getSession(String txID) throws ActiveMQStompException, ActiveMQSecurityException { StompSession session = null; try { if (txID == null) { @@ -609,6 +607,8 @@ public StompSession getSession(String txID) throws ActiveMQStompException { } else { session = manager.getTransactedSession(this, txID); } + } catch (ActiveMQSecurityException e) { + throw e; } catch (Exception e) { throw BUNDLE.errorGetSession(e).setHandler(frameHandler); } @@ -623,15 +623,15 @@ protected void validate() throws ActiveMQStompException { } protected void sendServerMessage(ICoreMessage message, String txID) throws ActiveMQStompException { - StompSession stompSession = getSession(txID); - - if (stompSession.isNoLocal()) { - message.putStringProperty(CONNECTION_ID_PROP, getID().toString()); - } - if (isEnableMessageID()) { - message.putStringProperty("amqMessageId", "STOMP" + message.getMessageID()); - } try { + StompSession stompSession = getSession(txID); + + if (stompSession.isNoLocal()) { + message.putStringProperty(CONNECTION_ID_PROP, getID().toString()); + } + if (isEnableMessageID()) { + message.putStringProperty("amqMessageId", "STOMP" + message.getMessageID()); + } if (minLargeMessageSize == -1 || (message.getBodyBuffer().writerIndex() < minLargeMessageSize)) { stompSession.sendInternal(message, false); } else { diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java index 888674c8524..72bc0f636c7 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java @@ -33,7 +33,6 @@ import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.core.io.IOCallback; import org.apache.activemq.artemis.core.message.impl.CoreMessage; -import org.apache.activemq.artemis.core.remoting.CertificateUtil; import org.apache.activemq.artemis.core.remoting.impl.netty.NettyServerConnection; import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; import org.apache.activemq.artemis.core.server.ActiveMQServer; @@ -45,9 +44,6 @@ import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.spi.core.remoting.Acceptor; import org.apache.activemq.artemis.spi.core.remoting.Connection; -import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager; -import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager2; -import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager3; import org.apache.activemq.artemis.utils.UUIDGenerator; import static org.apache.activemq.artemis.core.protocol.stomp.ActiveMQStompProtocolMessageBundle.BUNDLE; @@ -65,7 +61,7 @@ public class StompProtocolManager extends AbstractProtocolManager transactedSessions = new HashMap<>(); + private final Map transactedSessions = new HashMap<>(); // key => connection ID, value => Stomp session private final Map sessions = new HashMap<>(); @@ -212,33 +208,22 @@ public boolean send(final StompConnection connection, final StompFrame frame) { } } - // Package protected --------------------------------------------- - - // Protected ----------------------------------------------------- - - // Private ------------------------------------------------------- - public StompSession getSession(StompConnection connection) throws Exception { - StompSession stompSession = sessions.get(connection.getID()); - if (stompSession == null) { - stompSession = new StompSession(connection, this, server.getStorageManager().newContext(server.getExecutorFactory().getExecutor())); - String name = UUIDGenerator.getInstance().generateStringUUID(); - ServerSession session = server.createSession(name, connection.getLogin(), connection.getPasscode(), ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, connection, true, false, false, false, null, stompSession, true, server.newOperationContext(), getPrefixes()); - stompSession.setServerSession(session); - sessions.put(connection.getID(), stompSession); - } - server.getStorageManager().setContext(stompSession.getContext()); - return stompSession; + return internalGetSession(connection, sessions, connection.getID(), false); } public StompSession getTransactedSession(StompConnection connection, String txID) throws Exception { - StompSession stompSession = transactedSessions.get(txID); + return internalGetSession(connection, transactedSessions, txID, true); + } + + private StompSession internalGetSession(StompConnection connection, Map sessions, Object id, boolean transacted) throws Exception { + StompSession stompSession = sessions.get(id); if (stompSession == null) { - stompSession = new StompSession(connection, this, server.getStorageManager().newContext(executor)); + stompSession = new StompSession(connection, this, server.getStorageManager().newContext(server.getExecutorFactory().getExecutor())); String name = UUIDGenerator.getInstance().generateStringUUID(); - ServerSession session = server.createSession(name, connection.getLogin(), connection.getPasscode(), ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, connection, false, false, false, false, null, stompSession, true, server.newOperationContext(), getPrefixes()); + ServerSession session = server.createSession(name, connection.getLogin(), connection.getPasscode(), ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, connection, !transacted, false, false, false, null, stompSession, true, server.newOperationContext(), getPrefixes()); stompSession.setServerSession(session); - transactedSessions.put(txID, stompSession); + sessions.put(id, stompSession); } server.getStorageManager().setContext(stompSession.getContext()); return stompSession; @@ -263,9 +248,9 @@ public void run() { } // removed the transacted session belonging to the connection - Iterator> iterator = transactedSessions.entrySet().iterator(); + Iterator> iterator = transactedSessions.entrySet().iterator(); while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); + Map.Entry entry = iterator.next(); if (entry.getValue().getConnection() == connection) { ServerSession serverSession = entry.getValue().getCoreSession(); try { @@ -326,24 +311,6 @@ public String getVirtualHostName() { return "activemq"; } - public boolean validateUser(String login, String passcode, RemotingConnection remotingConnection) { - boolean validated = true; - - ActiveMQSecurityManager sm = server.getSecurityManager(); - - if (sm != null && server.getConfiguration().isSecurityEnabled()) { - if (sm instanceof ActiveMQSecurityManager3) { - validated = ((ActiveMQSecurityManager3) sm).validateUser(login, passcode, remotingConnection) != null; - } else if (sm instanceof ActiveMQSecurityManager2) { - validated = ((ActiveMQSecurityManager2) sm).validateUser(login, passcode, CertificateUtil.getCertsFromConnection(remotingConnection)); - } else { - validated = sm.validateUser(login, passcode); - } - } - - return validated; - } - public CoreMessage createServerMessage() { return new CoreMessage(server.getStorageManager().generateID(), 512); } diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/VersionedStompFrameHandler.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/VersionedStompFrameHandler.java index 023d8853788..75c1ea3cabb 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/VersionedStompFrameHandler.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/VersionedStompFrameHandler.java @@ -133,6 +133,8 @@ private StompFrame handleSubscribe(StompFrame request) { return null; } catch (ActiveMQStompException e) { return e.getFrame(); + } catch (Exception e) { + return new ActiveMQStompException(e.getMessage(), e).setHandler(this).getFrame(); } } @@ -256,7 +258,7 @@ public StompFrame onAbort(StompFrame request) { return response; } - public StompPostReceiptFunction onSubscribe(StompFrame frame) throws ActiveMQStompException { + public StompPostReceiptFunction onSubscribe(StompFrame frame) throws Exception { String destination = getDestination(frame); String selector = frame.getHeader(Stomp.Headers.Subscribe.SELECTOR); @@ -279,11 +281,11 @@ public StompPostReceiptFunction onSubscribe(StompFrame frame) throws ActiveMQSto return connection.subscribe(destination, selector, ack, id, durableSubscriptionName, noLocal, routingType); } - public String getDestination(StompFrame request) throws ActiveMQStompException { + public String getDestination(StompFrame request) throws Exception { return getDestination(request, Headers.Subscribe.DESTINATION); } - public String getDestination(StompFrame request, String header) throws ActiveMQStompException { + public String getDestination(StompFrame request, String header) throws Exception { String destination = request.getHeader(header); if (destination == null) { return null; @@ -291,7 +293,7 @@ public String getDestination(StompFrame request, String header) throws ActiveMQS return connection.getSession().getCoreSession().removePrefix(SimpleString.toSimpleString(destination)).toString(); } - public String getPrefix(StompFrame request) throws ActiveMQStompException { + public String getPrefix(StompFrame request) throws Exception { String destination = request.getHeader(Headers.Send.DESTINATION); if (destination == null) { return null; @@ -367,7 +369,7 @@ public void onError(ActiveMQStompException e) { connection.destroy(); } - private RoutingType getRoutingType(String typeHeader, String destination) throws ActiveMQStompException { + private RoutingType getRoutingType(String typeHeader, String destination) throws Exception { // null is valid to return here so we know when the user didn't provide any routing info RoutingType routingType; if (typeHeader != null) { @@ -378,4 +380,14 @@ private RoutingType getRoutingType(String typeHeader, String destination) throws return routingType; } + protected StompFrame getFailedAuthenticationResponse(String login) { + StompFrame response; + response = createStompFrame(Stomp.Responses.ERROR); + response.setNeedsDisconnect(true); + String responseText = "Security Error occurred: User name [" + login + "] or password is invalid"; + response.setBody(responseText); + response.addHeader(Stomp.Headers.Error.MESSAGE, responseText); + return response; + } + } diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/v10/StompFrameHandlerV10.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/v10/StompFrameHandlerV10.java index 2011e0504af..f171cb0f84b 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/v10/StompFrameHandlerV10.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/v10/StompFrameHandlerV10.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.concurrent.ScheduledExecutorService; +import org.apache.activemq.artemis.api.core.ActiveMQSecurityException; import org.apache.activemq.artemis.core.protocol.stomp.ActiveMQStompException; import org.apache.activemq.artemis.core.protocol.stomp.FrameEventListener; import org.apache.activemq.artemis.core.protocol.stomp.Stomp; @@ -54,32 +55,25 @@ public StompFrame onConnect(StompFrame frame) { try { connection.setClientID(clientID); - if (connection.validateUser(login, passcode, connection)) { - connection.setValid(true); + connection.setLogin(login); + connection.setPasscode(passcode); + // Create session which will validate user - this will cache the session in the protocol manager + connection.getSession(); + connection.setValid(true); - // Create session after validating user - this will cache the session in the - // protocol manager - connection.getSession(); + response = new StompFrameV10(Stomp.Responses.CONNECTED); - response = new StompFrameV10(Stomp.Responses.CONNECTED); - - if (frame.hasHeader(Stomp.Headers.ACCEPT_VERSION)) { - response.addHeader(Stomp.Headers.Connected.VERSION, StompVersions.V1_0.toString()); - } + if (frame.hasHeader(Stomp.Headers.ACCEPT_VERSION)) { + response.addHeader(Stomp.Headers.Connected.VERSION, StompVersions.V1_0.toString()); + } - response.addHeader(Stomp.Headers.Connected.SESSION, connection.getID().toString()); + response.addHeader(Stomp.Headers.Connected.SESSION, connection.getID().toString()); - if (requestID != null) { - response.addHeader(Stomp.Headers.Connected.RESPONSE_ID, requestID); - } - } else { - // not valid - response = new StompFrameV10(Stomp.Responses.ERROR); - String responseText = "Security Error occurred: User name [" + login + "] or password is invalid"; - response.setBody(responseText); - response.setNeedsDisconnect(true); - response.addHeader(Stomp.Headers.Error.MESSAGE, responseText); + if (requestID != null) { + response.addHeader(Stomp.Headers.Connected.RESPONSE_ID, requestID); } + } catch (ActiveMQSecurityException e) { + response = getFailedAuthenticationResponse(login); } catch (ActiveMQStompException e) { response = e.getFrame(); } diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/v11/StompFrameHandlerV11.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/v11/StompFrameHandlerV11.java index 67fb34b1e6b..9d3f912ab17 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/v11/StompFrameHandlerV11.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/v11/StompFrameHandlerV11.java @@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import org.apache.activemq.artemis.api.core.ActiveMQSecurityException; import org.apache.activemq.artemis.core.protocol.stomp.ActiveMQStompException; import org.apache.activemq.artemis.core.protocol.stomp.FrameEventListener; import org.apache.activemq.artemis.core.protocol.stomp.SimpleBytes; @@ -69,50 +70,42 @@ public StompFrame onConnect(StompFrame frame) { try { connection.setClientID(clientID); - if (connection.validateUser(login, passcode, connection)) { - connection.setValid(true); + connection.setLogin(login); + connection.setPasscode(passcode); + // Create session which will validate user - this will cache the session in the protocol manager + connection.getSession(); + connection.setValid(true); - // Create session after validating user - this will cache the session in the - // protocol manager - connection.getSession(); + response = this.createStompFrame(Stomp.Responses.CONNECTED); - response = this.createStompFrame(Stomp.Responses.CONNECTED); + // version + response.addHeader(Stomp.Headers.Connected.VERSION, connection.getVersion()); - // version - response.addHeader(Stomp.Headers.Connected.VERSION, connection.getVersion()); + // session + response.addHeader(Stomp.Headers.Connected.SESSION, connection.getID().toString()); - // session - response.addHeader(Stomp.Headers.Connected.SESSION, connection.getID().toString()); + // server + response.addHeader(Stomp.Headers.Connected.SERVER, connection.getActiveMQServerName()); - // server - response.addHeader(Stomp.Headers.Connected.SERVER, connection.getActiveMQServerName()); - - if (requestID != null) { - response.addHeader(Stomp.Headers.Connected.RESPONSE_ID, requestID); - } + if (requestID != null) { + response.addHeader(Stomp.Headers.Connected.RESPONSE_ID, requestID); + } - // heart-beat. We need to start after connected frame has been sent. - // otherwise the client may receive heart-beat before it receives - // connected frame. - String heartBeat = headers.get(Stomp.Headers.Connect.HEART_BEAT); + // heart-beat. We need to start after connected frame has been sent. + // otherwise the client may receive heart-beat before it receives + // connected frame. + String heartBeat = headers.get(Stomp.Headers.Connect.HEART_BEAT); - if (heartBeat != null) { - handleHeartBeat(heartBeat); - if (heartBeater == null) { - response.addHeader(Stomp.Headers.Connected.HEART_BEAT, "0,0"); - } else { - response.addHeader(Stomp.Headers.Connected.HEART_BEAT, heartBeater.serverPingPeriod + "," + heartBeater.clientPingResponse); - } + if (heartBeat != null) { + handleHeartBeat(heartBeat); + if (heartBeater == null) { + response.addHeader(Stomp.Headers.Connected.HEART_BEAT, "0,0"); + } else { + response.addHeader(Stomp.Headers.Connected.HEART_BEAT, heartBeater.serverPingPeriod + "," + heartBeater.clientPingResponse); } - } else { - // not valid - response = createStompFrame(Stomp.Responses.ERROR); - response.setNeedsDisconnect(true); - response.addHeader(Stomp.Headers.CONTENT_TYPE, "text/plain"); - String responseText = "Security Error occurred: User name [" + login + "] or password is invalid"; - response.setBody(responseText); - response.addHeader(Stomp.Headers.Error.MESSAGE, responseText); } + } catch (ActiveMQSecurityException e) { + response = getFailedAuthenticationResponse(login); } catch (ActiveMQStompException e) { response = e.getFrame(); } @@ -120,6 +113,13 @@ public StompFrame onConnect(StompFrame frame) { return response; } + @Override + protected StompFrame getFailedAuthenticationResponse(String login) { + StompFrame response = super.getFailedAuthenticationResponse(login); + response.addHeader(Stomp.Headers.CONTENT_TYPE, "text/plain"); + return response; + } + private void handleHeartBeat(String heartBeatHeader) throws ActiveMQStompException { String[] params = heartBeatHeader.split(","); if (params.length != 2) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v11/StompV11Test.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v11/StompV11Test.java index 6a3fae66003..0647ae8998e 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v11/StompV11Test.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v11/StompV11Test.java @@ -96,7 +96,7 @@ public void tearDown() throws Exception { @Test public void testConnection() throws Exception { - server.getActiveMQServer().getConfiguration().setSecurityEnabled(true); + server.getActiveMQServer().getSecurityStore().setSecurityEnabled(true); StompClientConnection connection = StompClientConnectionFactory.createClientConnection(v10Uri); connection.connect(defUser, defPass); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v12/StompV12Test.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v12/StompV12Test.java index d94d7c1421e..c91e5a8c837 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v12/StompV12Test.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v12/StompV12Test.java @@ -97,7 +97,7 @@ public void tearDown() throws Exception { @Test public void testConnection() throws Exception { - server.getActiveMQServer().getConfiguration().setSecurityEnabled(true); + server.getActiveMQServer().getSecurityStore().setSecurityEnabled(true); StompClientConnection connection = StompClientConnectionFactory.createClientConnection(v10Uri); connection.connect(defUser, defPass); From 5b7b84a1b3e1eb4a78cc2e391e0c77baa7e983f2 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 1 Jun 2018 11:16:10 -0500 Subject: [PATCH 016/207] ARTEMIS-1903 Log STOMP ERROR frames at WARN --- .../core/protocol/stomp/ActiveMQStompProtocolLogger.java | 8 ++++++-- .../artemis/core/protocol/stomp/StompConnection.java | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/ActiveMQStompProtocolLogger.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/ActiveMQStompProtocolLogger.java index e9dc4ee5ab5..e9c431b5b9d 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/ActiveMQStompProtocolLogger.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/ActiveMQStompProtocolLogger.java @@ -26,7 +26,7 @@ /** * Logger Code 22 * - * each message id must be 6 digits long starting with 10, the 3rd digit donates the level so + * each message id must be 6 digits long starting with 22, the 3rd digit donates the level so * * INF0 1 * WARN 2 @@ -35,7 +35,7 @@ * TRACE 5 * FATAL 6 * - * so an INFO message would be 101000 to 101999 + * so an INFO message would be 241000 to 246999 */ @MessageLogger(projectCode = "AMQ") @@ -50,6 +50,10 @@ public interface ActiveMQStompProtocolLogger extends BasicLogger { @Message(id = 222068, value = "connection closed {0}", format = Message.Format.MESSAGE_FORMAT) void connectionClosed(StompConnection connection); + @LogMessage(level = Logger.Level.WARN) + @Message(id = 222069, value = "Sent ERROR frame to STOMP client {0}: {1}", format = Message.Format.MESSAGE_FORMAT) + void sentErrorToClient(String address, String message); + @LogMessage(level = Logger.Level.ERROR) @Message(id = 224023, value = "Unable to send frame {0}", format = Message.Format.MESSAGE_FORMAT) void errorSendingFrame(@Cause Exception e, StompFrame frame); diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java index 78cd21b1417..c247585ac5a 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java @@ -778,6 +778,14 @@ public void physicalSend(StompFrame frame) throws Exception { stompListener.replySent(frame); } + if (frame.getCommand().equals(Stomp.Responses.ERROR)) { + String message = "no message header"; + if (frame.hasHeader(Stomp.Headers.Error.MESSAGE)) { + message = frame.getHeader(Stomp.Headers.Error.MESSAGE); + } + ActiveMQStompProtocolLogger.LOGGER.sentErrorToClient(getTransportConnection().getRemoteAddress(), message); + } + } public VersionedStompFrameHandler getFrameHandler() { From be36d3078ceafe3df06a1686426f5d948dfadc2c Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Mon, 4 Jun 2018 15:01:04 -0500 Subject: [PATCH 017/207] ARTEMIS-1902 decrement before returning --- .../org/apache/activemq/artemis/core/postoffice/QueueInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/QueueInfo.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/QueueInfo.java index 581d6482568..f271ebe8d92 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/QueueInfo.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/QueueInfo.java @@ -114,7 +114,7 @@ public void decrementConsumers() { consumerUpdater.getAndUpdate(this, value -> { if (value > 0) { - return value--; + return --value; } else { ActiveMQServerLogger.LOGGER.consumerCountError("Tried to decrement consumer count below 0: " + this); return value; From 1d89bc7dd279b6741b7ff276f2230aaab60f0db8 Mon Sep 17 00:00:00 2001 From: Andrey Arkaev Date: Wed, 6 Jun 2018 18:08:15 +0500 Subject: [PATCH 018/207] ARTEMIS-1914 Fix NPE on ConfigurationImpl.hashCode() --- .../artemis/core/config/impl/ConfigurationImpl.java | 2 +- .../artemis/core/config/impl/ConfigurationImplTest.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java index ae02dcd71cd..ae4a25f5d2e 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java @@ -1635,7 +1635,7 @@ public int hashCode() { result = prime * result + (logJournalWriteRate ? 1231 : 1237); result = prime * result + ((managementAddress == null) ? 0 : managementAddress.hashCode()); result = prime * result + ((managementNotificationAddress == null) ? 0 : managementNotificationAddress.hashCode()); - result = prime * result + (maskPassword ? 1231 : 1237); + result = prime * result + (maskPassword == null ? 0 : maskPassword.hashCode()); result = prime * result + maxConcurrentPageIO; result = prime * result + (int) (memoryMeasureInterval ^ (memoryMeasureInterval >>> 32)); result = prime * result + memoryWarningThreshold; diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImplTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImplTest.java index 58283b35e77..ca28e356cc2 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImplTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImplTest.java @@ -88,6 +88,13 @@ public void testDefaults() { Assert.assertEquals(ActiveMQDefaultConfiguration.getDefaultMemoryMeasureInterval(), conf.getMemoryMeasureInterval()); } + @Test + public void testNullMaskPassword() { + ConfigurationImpl impl = new ConfigurationImpl(); + impl.setMaskPassword(null); + impl.hashCode(); + } + @Test public void testSetGetAttributes() throws Exception { for (int j = 0; j < 100; j++) { From dfb41aed4ccdfb211a6c4529e05bef41d90046b6 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Mon, 4 Jun 2018 22:28:29 -0400 Subject: [PATCH 019/207] ARTEMIS-1915 Expiry Scanner is holding a lock on the queue No tests required as there is no semantic changes here. Current tests should validate this change. --- .../artemis/core/server/impl/QueueImpl.java | 96 ++++++++++--------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java index 94b764029a9..5da8dc1ef5b 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java @@ -906,7 +906,6 @@ public void addConsumer(final Consumer consumer) throws Exception { leaveCritical(CRITICAL_CONSUMER); } - } @Override @@ -1178,22 +1177,22 @@ public long getDurablePersistentSize() { } @Override - public synchronized int getScheduledCount() { + public int getScheduledCount() { return scheduledDeliveryHandler.getScheduledCount(); } @Override - public synchronized long getScheduledSize() { + public long getScheduledSize() { return scheduledDeliveryHandler.getScheduledSize(); } @Override - public synchronized int getDurableScheduledCount() { + public int getDurableScheduledCount() { return scheduledDeliveryHandler.getDurableScheduledCount(); } @Override - public synchronized long getDurableScheduledSize() { + public long getDurableScheduledSize() { return scheduledDeliveryHandler.getDurableScheduledSize(); } @@ -1788,6 +1787,12 @@ class ExpiryScanner implements Runnable { @Override public void run() { + + boolean expired = false; + boolean hasElements = false; + int elementsExpired = 0; + + LinkedList expiredMessages = new LinkedList<>(); synchronized (QueueImpl.this) { if (queueDestroyed) { return; @@ -1796,54 +1801,24 @@ public void run() { LinkedListIterator iter = iterator(); - boolean expired = false; - boolean hasElements = false; - - int elementsExpired = 0; try { - Transaction tx = null; - while (postOffice.isStarted() && iter.hasNext()) { hasElements = true; MessageReference ref = iter.next(); - try { - if (ref.getMessage().isExpired()) { - if (tx == null) { - tx = new TransactionImpl(storageManager); - } - incDelivering(ref); - expired = true; - expire(tx, ref); - iter.remove(); - refRemoved(ref); - - if (++elementsExpired >= MAX_DELIVERIES_IN_LOOP) { - logger.debug("Breaking loop of expiring"); - scannerRunning.incrementAndGet(); - getExecutor().execute(this); - break; - } + if (ref.getMessage().isExpired()) { + incDelivering(ref); + expired = true; + expiredMessages.add(ref); + iter.remove(); + + if (++elementsExpired >= MAX_DELIVERIES_IN_LOOP) { + logger.debug("Breaking loop of expiring"); + scannerRunning.incrementAndGet(); + getExecutor().execute(this); + break; } - - } catch (Exception e) { - ActiveMQServerLogger.LOGGER.errorExpiringReferencesOnQueue(e, ref); } } - - logger.debug("Expired " + elementsExpired + " references"); - - try { - if (tx != null) { - tx.commit(); - } - } catch (Exception e) { - ActiveMQServerLogger.LOGGER.unableToCommitTransaction(e); - } - - // If empty we need to schedule depaging to make sure we would depage expired messages as well - if ((!hasElements || expired) && pageIterator != null && pageIterator.hasNext()) { - scheduleDepage(true); - } } finally { try { iter.close(); @@ -1854,6 +1829,35 @@ public void run() { } } + + if (!expiredMessages.isEmpty()) { + Transaction tx = new TransactionImpl(storageManager); + for (MessageReference ref : expiredMessages) { + if (tx == null) { + tx = new TransactionImpl(storageManager); + } + try { + expire(tx, ref); + refRemoved(ref); + } catch (Exception e) { + ActiveMQServerLogger.LOGGER.errorExpiringReferencesOnQueue(e, ref); + } + } + + try { + tx.commit(); + } catch (Exception e) { + ActiveMQServerLogger.LOGGER.unableToCommitTransaction(e); + } + logger.debug("Expired " + elementsExpired + " references"); + + + } + + // If empty we need to schedule depaging to make sure we would depage expired messages as well + if ((!hasElements || expired) && pageIterator != null && pageIterator.hasNext()) { + scheduleDepage(true); + } } } From b6fba64d9ed843160a2f45e2631737ff15e6d40f Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 6 Jun 2018 12:57:45 -0400 Subject: [PATCH 020/207] ARTEMIS-1913 Dependency to netty tcpnative should be optional --- artemis-core-client/pom.xml | 5 ----- examples/features/standard/netty-openssl/pom.xml | 8 ++++++++ pom.xml | 7 ------- tests/integration-tests/pom.xml | 6 ++++++ tests/unit-tests/pom.xml | 6 ++++++ 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/artemis-core-client/pom.xml b/artemis-core-client/pom.xml index 092b8c399c5..47b72e23127 100644 --- a/artemis-core-client/pom.xml +++ b/artemis-core-client/pom.xml @@ -118,11 +118,6 @@ io.netty netty-common - - io.netty - netty-tcnative-boringssl-static - - diff --git a/examples/features/standard/netty-openssl/pom.xml b/examples/features/standard/netty-openssl/pom.xml index 5f61a20a12d..b146d280081 100644 --- a/examples/features/standard/netty-openssl/pom.xml +++ b/examples/features/standard/netty-openssl/pom.xml @@ -33,6 +33,7 @@ under the License. ${project.basedir}/../../../.. + 2.0.7.Final @@ -41,6 +42,12 @@ under the License. artemis-jms-client-all ${project.version} + + io.netty + netty-tcnative-boringssl-static + ${netty.tcnative.version} + + @@ -56,6 +63,7 @@ under the License. ${noServer} + io.netty:netty-tcnative-boringssl-static:${netty.tcnative.version} diff --git a/pom.xml b/pom.xml index 8c506109644..7249c2dfb3e 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,6 @@ 2.4 2.8.47 4.1.24.Final - 2.0.7.Final 0.27.1 3.0.19.Final 1.7.21 @@ -541,12 +540,6 @@ ${netty.version} - - io.netty - netty-tcnative-boringssl-static - ${netty.tcnative.version} - - org.apache.qpid proton-j diff --git a/tests/integration-tests/pom.xml b/tests/integration-tests/pom.xml index d1c2b334498..ad278643ec7 100644 --- a/tests/integration-tests/pom.xml +++ b/tests/integration-tests/pom.xml @@ -397,6 +397,12 @@ jgroups + + + io.netty + netty-tcnative-boringssl-static + 2.0.7.Final + diff --git a/tests/unit-tests/pom.xml b/tests/unit-tests/pom.xml index 5748163ddef..3f00b6ef107 100644 --- a/tests/unit-tests/pom.xml +++ b/tests/unit-tests/pom.xml @@ -160,6 +160,12 @@ 2.7.0-SNAPSHOT test + + io.netty + netty-tcnative-boringssl-static + 2.0.7.Final + test + From dc29a55e1b0106ddaf8e332d098cde4520bd639e Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Mon, 4 Jun 2018 14:27:31 -0500 Subject: [PATCH 021/207] ARTEMIS-1900 fix race in STOMP auto-create --- .../core/protocol/stomp/StompConnection.java | 7 +- .../protocol/stomp/StompProtocolManager.java | 4 +- .../core/protocol/stomp/StompSession.java | 10 +- .../stomp/StompTestMultiThreaded.java | 95 +++++++++++++++++++ 4 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestMultiThreaded.java diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java index c247585ac5a..f7accb1ea6b 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java @@ -275,7 +275,6 @@ public void autoCreateDestinationIfPossible(String queue, RoutingType routingTyp AddressInfo addressInfo = manager.getServer().getAddressInfo(simpleQueue); AddressSettings addressSettings = manager.getServer().getAddressSettingsRepository().getMatch(queue); RoutingType effectiveAddressRoutingType = routingType == null ? addressSettings.getDefaultAddressRoutingType() : routingType; - boolean checkAnycast = false; /** * If the address doesn't exist then it is created if possible. * If the address does exist but doesn't support the routing-type then the address is updated if possible. @@ -284,8 +283,6 @@ public void autoCreateDestinationIfPossible(String queue, RoutingType routingTyp if (addressSettings.isAutoCreateAddresses()) { session.createAddress(simpleQueue, effectiveAddressRoutingType, true); } - - checkAnycast = true; } else if (!addressInfo.getRoutingTypes().contains(effectiveAddressRoutingType)) { if (addressSettings.isAutoCreateAddresses()) { EnumSet routingTypes = EnumSet.noneOf(RoutingType.class); @@ -295,12 +292,10 @@ public void autoCreateDestinationIfPossible(String queue, RoutingType routingTyp routingTypes.add(effectiveAddressRoutingType); manager.getServer().updateAddressInfo(simpleQueue, routingTypes); } - - checkAnycast = true; } // only auto create the queue if the address is ANYCAST - if (checkAnycast && effectiveAddressRoutingType == RoutingType.ANYCAST && addressSettings.isAutoCreateQueues()) { + if (effectiveAddressRoutingType == RoutingType.ANYCAST && addressSettings.isAutoCreateQueues() && manager.getServer().locateQueue(simpleQueue) == null) { session.createQueue(simpleQueue, simpleQueue, routingType == null ? addressSettings.getDefaultQueueRoutingType() : routingType, null, false, true, true); } } catch (ActiveMQQueueExistsException e) { diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java index 72bc0f636c7..62df6e5a4e7 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java @@ -186,8 +186,8 @@ public List websocketSubprotocolIdentifiers() { // Public -------------------------------------------------------- public boolean send(final StompConnection connection, final StompFrame frame) { - if (ActiveMQServerLogger.LOGGER.isTraceEnabled()) { - ActiveMQServerLogger.LOGGER.trace("sent " + frame); + if (ActiveMQStompProtocolLogger.LOGGER.isTraceEnabled()) { + ActiveMQStompProtocolLogger.LOGGER.trace("sent " + frame); } invokeInterceptors(this.outgoingInterceptors, frame, connection); diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSession.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSession.java index 8a573e6c3af..e370c812f2f 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSession.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSession.java @@ -252,11 +252,13 @@ public StompPostReceiptFunction addSubscription(long consumerID, String destination, String selector, String ack) throws Exception { + SimpleString address = SimpleString.toSimpleString(destination); SimpleString queueName = SimpleString.toSimpleString(destination); + SimpleString selectorSimple = SimpleString.toSimpleString(selector); boolean pubSub = false; final int receiveCredits = ack.equals(Stomp.Headers.Subscribe.AckModeValues.AUTO) ? -1 : consumerCredits; - Set routingTypes = manager.getServer().getAddressInfo(getCoreSession().removePrefix(SimpleString.toSimpleString(destination))).getRoutingTypes(); + Set routingTypes = manager.getServer().getAddressInfo(getCoreSession().removePrefix(address)).getRoutingTypes(); boolean topic = routingTypes.size() == 1 && routingTypes.contains(RoutingType.MULTICAST); if (topic) { // subscribes to a topic @@ -267,14 +269,14 @@ public StompPostReceiptFunction addSubscription(long consumerID, } queueName = SimpleString.toSimpleString(clientID + "." + durableSubscriptionName); if (manager.getServer().locateQueue(queueName) == null) { - session.createQueue(SimpleString.toSimpleString(destination), queueName, SimpleString.toSimpleString(selector), false, true); + session.createQueue(address, queueName, selectorSimple, false, true); } } else { queueName = UUIDGenerator.getInstance().generateSimpleStringUUID(); - session.createQueue(SimpleString.toSimpleString(destination), queueName, SimpleString.toSimpleString(selector), true, false); + session.createQueue(address, queueName, selectorSimple, true, false); } } - final ServerConsumer consumer = topic ? session.createConsumer(consumerID, queueName, null, false, false, 0) : session.createConsumer(consumerID, queueName, SimpleString.toSimpleString(selector), false, false, 0); + final ServerConsumer consumer = session.createConsumer(consumerID, queueName, topic ? null : selectorSimple, false, false, 0); StompSubscription subscription = new StompSubscription(subscriptionID, ack, queueName, pubSub); subscriptions.put(consumerID, subscription); session.start(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestMultiThreaded.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestMultiThreaded.java new file mode 100644 index 00000000000..63dd35bcc6f --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestMultiThreaded.java @@ -0,0 +1,95 @@ +/* + * 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.artemis.tests.integration.stomp; + +import java.net.URI; +import java.util.UUID; + +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.protocol.stomp.Stomp; +import org.apache.activemq.artemis.core.settings.impl.AddressSettings; +import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; +import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnection; +import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnectionFactory; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class StompTestMultiThreaded extends StompTestBase { + + private static final transient IntegrationTestLogger log = IntegrationTestLogger.LOGGER; + private static final SimpleString QUEUE = new SimpleString("x"); + + class SomeConsumer extends Thread { + + private final StompClientConnection conn; + + boolean failed = false; + + SomeConsumer() throws Exception { + URI uri = createStompClientUri(scheme, "localhost", 61614); + this.conn = StompClientConnectionFactory.createClientConnection(uri); + } + + @Override + public void run() { + try { + conn.connect(defUser, defPass); + if (!subscribe(conn, UUID.randomUUID().toString(), Stomp.Headers.Subscribe.AckModeValues.AUTO, null, null, "/queue/" + QUEUE, true).getCommand().equals(Stomp.Responses.RECEIPT)) { + failed = true; + } + } catch (Throwable e) { + failed = true; + } finally { + try { + conn.disconnect(); + } catch (Exception e) { + } + } + } + } + + @Test + public void testTwoConcurrentSubscribers() throws Exception { + server.getActiveMQServer().getAddressSettingsRepository().addMatch("#", new AddressSettings().setAutoDeleteAddresses(false).setAutoDeleteQueues(false)); + server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://localhost:61614?protocols=STOMP&anycastPrefix=/queue/").start(); + + int nThreads = 2; + + SomeConsumer[] consumers = new SomeConsumer[nThreads]; + for (int j = 0; j < 1000; j++) { + + for (int i = 0; i < nThreads; i++) { + consumers[i] = new SomeConsumer(); + } + + for (int i = 0; i < nThreads; i++) { + consumers[i].start(); + } + + for (SomeConsumer consumer : consumers) { + consumer.join(); + Assert.assertFalse(consumer.failed); + } + + // delete queue here so it can be auto-created again during the next loop iteration + server.getActiveMQServer().locateQueue(QUEUE).deleteQueue(); + } + } +} From 2b5d8f3b808a681d9306625c0005a1f454570407 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 9 Mar 2018 09:07:38 -0600 Subject: [PATCH 022/207] ARTEMIS-1912 big doc refactor - Split protocols into individual chapters - Reorganize summary to flow more logically - Fill in missing parameters in configuration index - Normalize spaces for ordered and unordered lists - Re-wrap lots of text for readability - Fix incorrect XML snippets - Normalize table formatting - Improve internal links with anchors - Update content to reflect new address model - Resized architecture images to avoid excessive white-space - Update some JavaDoc - Update some schema elements - Disambiguate AIO & ASYNCIO where necessary - Use URIs instead of Objects in code examples --- .../api/core/client/ClientSession.java | 40 +- .../schema/artemis-configuration.xsd | 52 +- docs/user-manual/en/SUMMARY.md | 17 +- docs/user-manual/en/address-model.md | 891 ++++++---- docs/user-manual/en/amqp.md | 129 ++ docs/user-manual/en/architecture.md | 153 +- docs/user-manual/en/broker-plugins.md | 242 +-- docs/user-manual/en/client-classpath.md | 17 +- docs/user-manual/en/client-reconnection.md | 137 +- docs/user-manual/en/clusters.md | 842 ++++----- docs/user-manual/en/config-reload.md | 199 ++- docs/user-manual/en/configuration-index.md | 547 +++--- docs/user-manual/en/configuring-transports.md | 707 ++++---- docs/user-manual/en/connection-ttl.md | 44 +- docs/user-manual/en/core-bridges.md | 331 ++-- docs/user-manual/en/core.md | 228 +++ docs/user-manual/en/critical-analysis.md | 8 +- docs/user-manual/en/data-tools.md | 348 ++++ docs/user-manual/en/diverts.md | 6 +- docs/user-manual/en/duplicate-detection.md | 4 +- docs/user-manual/en/embedding-activemq.md | 84 +- docs/user-manual/en/examples.md | 1026 ++++++----- docs/user-manual/en/exclusive-queues.md | 45 +- docs/user-manual/en/filter-expressions.md | 42 +- docs/user-manual/en/flow-control.md | 12 +- docs/user-manual/en/graceful-shutdown.md | 30 +- docs/user-manual/en/ha.md | 334 ++-- docs/user-manual/en/images/architecture1.jpg | Bin 62657 -> 76604 bytes docs/user-manual/en/images/architecture2.jpg | Bin 20043 -> 27525 bytes docs/user-manual/en/images/architecture3.jpg | Bin 14069 -> 15699 bytes .../user-manual/en/intercepting-operations.md | 85 +- docs/user-manual/en/jms-bridge.md | 385 ++--- docs/user-manual/en/jms-core-mapping.md | 20 +- docs/user-manual/en/large-messages.md | 214 +-- docs/user-manual/en/last-value-queues.md | 37 +- docs/user-manual/en/libaio.md | 31 +- docs/user-manual/en/logging.md | 147 +- docs/user-manual/en/management-console.md | 23 +- docs/user-manual/en/management.md | 937 +++++----- docs/user-manual/en/masking-passwords.md | 267 +-- docs/user-manual/en/maven-plugin.md | 50 +- docs/user-manual/en/message-expiry.md | 66 +- docs/user-manual/en/message-grouping.md | 247 ++- docs/user-manual/en/messaging-concepts.md | 399 ++--- docs/user-manual/en/mqtt.md | 137 ++ docs/user-manual/en/network-isolation.md | 90 +- docs/user-manual/en/openwire.md | 112 ++ docs/user-manual/en/paging.md | 202 +-- docs/user-manual/en/perf-tuning.md | 517 +++--- docs/user-manual/en/persistence.md | 353 ++-- docs/user-manual/en/pre-acknowledge.md | 14 +- docs/user-manual/en/preface.md | 54 +- docs/user-manual/en/project-info.md | 19 +- .../en/protocols-interoperability.md | 663 +------- docs/user-manual/en/resource-limits.md | 6 +- docs/user-manual/en/rest.md | 1293 +++++++------- docs/user-manual/en/scheduled-messages.md | 2 +- docs/user-manual/en/security.md | 1509 ++++++++++------- docs/user-manual/en/send-guarantees.md | 18 +- docs/user-manual/en/slow-consumers.md | 2 +- docs/user-manual/en/spring-integration.md | 43 +- docs/user-manual/en/stomp.md | 293 ++++ docs/user-manual/en/syntax.md | 2 +- docs/user-manual/en/tools.md | 216 --- docs/user-manual/en/undelivered-messages.md | 30 +- docs/user-manual/en/unit-testing.md | 42 +- docs/user-manual/en/upgrading.md | 53 +- docs/user-manual/en/using-AMQP.md | 74 - docs/user-manual/en/using-core.md | 212 --- docs/user-manual/en/using-jms.md | 414 ++--- docs/user-manual/en/using-server.md | 240 +-- docs/user-manual/en/versions.md | 8 +- docs/user-manual/en/wildcard-routing.md | 2 +- .../src/main/resources/broker.xml | 13 +- .../src/main/resources/spring-jms-beans.xml | 9 +- 75 files changed, 8241 insertions(+), 7824 deletions(-) create mode 100644 docs/user-manual/en/amqp.md create mode 100644 docs/user-manual/en/core.md create mode 100644 docs/user-manual/en/data-tools.md create mode 100644 docs/user-manual/en/mqtt.md create mode 100644 docs/user-manual/en/openwire.md create mode 100644 docs/user-manual/en/stomp.md delete mode 100644 docs/user-manual/en/tools.md delete mode 100644 docs/user-manual/en/using-AMQP.md delete mode 100644 docs/user-manual/en/using-core.md diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java index ddc8168758a..dbc1e890df4 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java @@ -427,7 +427,7 @@ void createTemporaryQueue(SimpleString address, * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param durable whether the queue is durable or not * @throws ActiveMQException in an exception occurs while creating the queue @@ -440,7 +440,7 @@ void createTemporaryQueue(SimpleString address, * Notice: you will get an exception if the address or the filter doesn't match to an already existent queue * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param durable if the queue is durable * @throws ActiveMQException in an exception occurs while creating the queue @@ -453,7 +453,7 @@ void createTemporaryQueue(SimpleString address, * Notice: you will get an exception if the address or the filter doesn't match to an already existent queue * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter whether the queue is durable or not * @param durable if the queue is durable @@ -466,7 +466,7 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri * Creates Shared queue. A queue that will exist as long as there are consumers or is durable. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter whether the queue is durable or not * @param durable if the queue is durable @@ -483,7 +483,7 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param durable whether the queue is durable or not * @throws ActiveMQException in an exception occurs while creating the queue @@ -494,7 +494,7 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri * Creates a non-temporary queue non-durable queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @throws ActiveMQException in an exception occurs while creating the queue */ @@ -504,7 +504,7 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri * Creates a non-temporary queue non-durable queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @throws ActiveMQException in an exception occurs while creating the queue */ @@ -514,7 +514,7 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -527,7 +527,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporaryqueue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -539,7 +539,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -553,7 +553,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -569,7 +569,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -587,7 +587,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporaryqueue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -600,7 +600,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporaryqueue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -616,7 +616,7 @@ void createQueue(String address, RoutingType routingType, String queueName, Stri * Creates a non-temporaryqueue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -634,7 +634,7 @@ void createQueue(String address, RoutingType routingType, String queueName, Stri * Creates a temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @throws ActiveMQException in an exception occurs while creating the queue */ @@ -644,7 +644,7 @@ void createQueue(String address, RoutingType routingType, String queueName, Stri * Creates a temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @throws ActiveMQException in an exception occurs while creating the queue */ @@ -654,7 +654,7 @@ void createQueue(String address, RoutingType routingType, String queueName, Stri * Creates a temporary queue with a filter. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param maxConsumers how many concurrent consumers will be allowed on this queue @@ -670,7 +670,7 @@ void createTemporaryQueue(SimpleString address, RoutingType routingType, SimpleS * Creates a temporary queue with a filter. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @throws ActiveMQException in an exception occurs while creating the queue @@ -681,7 +681,7 @@ void createTemporaryQueue(SimpleString address, RoutingType routingType, SimpleS * Creates a temporary queue with a filter. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @throws ActiveMQException in an exception occurs while creating the queue diff --git a/artemis-server/src/main/resources/schema/artemis-configuration.xsd b/artemis-server/src/main/resources/schema/artemis-configuration.xsd index 6a7067141c2..831b4cb8ddd 100644 --- a/artemis-server/src/main/resources/schema/artemis-configuration.xsd +++ b/artemis-server/src/main/resources/schema/artemis-configuration.xsd @@ -69,9 +69,8 @@ If true then the ActiveMQ Artemis Server will make use of any Protocol Managers that are in available - on the - classpath. If false then only the core protocol will be available, unless in Embedded mode where users - can inject their own Protocol Managers. + on the classpath. If false then only the core protocol will be available, unless in Embedded mode + where users can inject their own Protocol Managers. @@ -216,7 +215,7 @@ - XXX + DEPRECATED: the name of the factory class to use for log delegation @@ -356,7 +355,7 @@ - a list of <class-name/> elements with the names of classes to use for interceptor incoming + a list of <class-name/> elements with the names of classes to use for intercepting incoming remoting packets @@ -365,7 +364,7 @@ - a list of <class-name/> elements with the names of classes to use for interceptor outcoming + a list of <class-name/> elements with the names of classes to use for intercepting outgoing remoting packets @@ -713,10 +712,10 @@ - + - the length of time to wait when opening a new Journal file before timing out and failing + the length of time in seconds to wait when opening a new Journal file before timing out and failing @@ -867,7 +866,7 @@ - regular expression for matching security roles against addresses + pattern for matching security roles against addresses; can use wildards @@ -1075,7 +1074,7 @@ - Wildcard addresses format + parameters to configure wildcard address matching format @@ -1103,9 +1102,20 @@ - - - + + + + a local address to which the datagram socket is bound + + + + + + + a local port to which the datagram socket is bound + + + @@ -1163,7 +1173,6 @@ - @@ -1650,8 +1659,7 @@ DEPRECATED: use message-load-balancing-type instead. Select STRICT to mimic - forward-when-no-consumers=true - and ON_DEMAND to mimic forward-when-no-consumers=false. + forward-when-no-consumers=true and ON_DEMAND to mimic forward-when-no-consumers=false. @@ -1750,7 +1758,7 @@ - XXX -- this is a duplicate... + name of discovery group used by this cluster-connection @@ -2961,7 +2969,7 @@ - + the maximum number of consumers allowed on this queue at any one time @@ -2990,7 +2998,7 @@ - XXX + pattern for matching settings against addresses; can use wildards @@ -3009,7 +3017,7 @@ - how many connections are allowed by the matched entity (-1 means no limit, default is -1) + how many connections are allowed by the matched user (-1 means no limit, default is -1) @@ -3017,7 +3025,7 @@ - how many queues can be created by the matched entity (-1 means no limit, default is -1) + how many queues can be created by the matched user (-1 means no limit, default is -1) @@ -3146,7 +3154,7 @@ - The address name to matches incoming message addresses + The address name to match incoming message addresses diff --git a/docs/user-manual/en/SUMMARY.md b/docs/user-manual/en/SUMMARY.md index c93f7591295..86273cc82f8 100644 --- a/docs/user-manual/en/SUMMARY.md +++ b/docs/user-manual/en/SUMMARY.md @@ -10,14 +10,18 @@ * [Using the Server](using-server.md) * [Upgrading](upgrading.md) * [Address Model](address-model.md) -* [Using JMS](using-jms.md) -* [Using Core](using-core.md) -* [Using AMQP](using-AMQP.md) +* [Protocols and Interoperability](protocols-interoperability.md) +* [AMQP](amqp.md) +* [MQTT](mqtt.md) +* [STOMP](stomp.md) +* [OpenWire](openwire.md) +* [Core](core.md) * [Mapping JMS Concepts to the Core API](jms-core-mapping.md) +* [Using JMS](using-jms.md) * [The Client Classpath](client-classpath.md) * [Examples](examples.md) * [Routing Messages With Wild Cards](wildcard-routing.md) -* [Understanding the Apache ActiveMQ Artemis Wildcard Syntax](wildcard-syntax.md) +* [Wildcard Syntax](wildcard-syntax.md) * [Filter Expressions](filter-expressions.md) * [Persistence](persistence.md) * [Configuring Transports](configuring-transports.md) @@ -56,14 +60,13 @@ * [Thread management](thread-pooling.md) * [Logging](logging.md) * [REST Interface](rest.md) -* [Embedding Apache ActiveMQ Artemis](embedding-activemq.md) +* [Embedding the Broker](embedding-activemq.md) * [Apache Karaf](karaf.md) * [Apache Tomcat](tomcat.md) * [Spring Integration](spring-integration.md) * [CDI Integration](cdi-integration.md) * [Intercepting Operations](intercepting-operations.md) -* [Protocols and Interoperability](protocols-interoperability.md) -* [Tools](tools.md) +* [Data Tools](data-tools.md) * [Maven Plugin](maven-plugin.md) * [Unit Testing](unit-testing.md) * [Troubleshooting and Performance Tuning](perf-tuning.md) diff --git a/docs/user-manual/en/address-model.md b/docs/user-manual/en/address-model.md index 1673e21e89c..f73adf24a1b 100644 --- a/docs/user-manual/en/address-model.md +++ b/docs/user-manual/en/address-model.md @@ -1,33 +1,66 @@ # Addressing Model -Apache ActiveMQ Artemis has a unique addressing model that is both powerful and flexible and that offers great performance. The addressing model comprises three main concepts: addresses, queues and routing types. +Apache ActiveMQ Artemis has a unique addressing model that is both powerful and +flexible and that offers great performance. The addressing model comprises +three main concepts: **addresses**, **queues** and **routing types**. -An address represents a messaging endpoint. Within the configuration, a typical address is given a unique name, 0 or more queues, and a routing type. +### Address -A queue is associated with an address. There can be multiple queues per address. Once an incoming message is matched to an address, the message will be sent on to one or more of its queues, depending on the routing type configured. Queues can be configured to be automatically created and deleted. +An address represents a messaging endpoint. Within the configuration, a typical +address is given a unique name, 0 or more queues, and a routing type. -A routing type determines how messages are sent to the queues associated with an address. An Apache ActiveMQ Artemis address can be configured with two different routing types. +### Queue + +A queue is associated with an address. There can be multiple queues per +address. Once an incoming message is matched to an address, the message will be +sent on to one or more of its queues, depending on the routing type configured. +Queues can be configured to be automatically created and deleted. + +### Routing Types + +A routing type determines how messages are sent to the queues associated with +an address. An Apache ActiveMQ Artemis address can be configured with two +different routing types. Table 1. Routing Types -| If you want your messages routed to…​ | Use this routing type …​ | -| :----------------------------------------------------------------------: | :---------------------: | -| A single queue within the matching address, in a point-to-point manner. | Anycast | -| Every queue within the matching address, in a publish-subscribe manner. | Multicast | +If you want your messages routed to...|Use this routing type... +---|--- +A single queue within the matching address, in a point-to-point manner.|Anycast +Every queue within the matching address, in a publish-subscribe manner.|Multicast + --------------------------------------------------------------------------------------------- -**Note:** It is possible to define more than one routing type per address, but this typically results in an anti-pattern and is therefore not recommended. If an address does use both routing types, however, and the client does not show a preference for either one, the broker typically defaults to the anycast routing type. -The one exception is when the client uses the MQTT protocol. In that case, the default routing type is multicast. | +**Note:** It is possible to define more than one routing type per address, but +this typically results in an anti-pattern and is therefore not recommended. If +an address does use both routing types, however, and the client does not show a +preference for either one, the broker typically defaults to the anycast routing +type. + +The one exception is when the client uses the MQTT protocol. In that case, the +default routing type is multicast. + +For additional details about these concepts refer to [the core](core.md) chapter. ## Basic Address Configuration -The following examples show how to configure basic point to point and publish subscribe addresses. +The following examples show how to configure basic point to point and publish +subscribe addresses. ### Point-to-Point Messaging -Point-to-point messaging is a common scenario in which a message sent by a producer has only one consumer. AMQP and JMS message producers and consumers can make use of point-to-point messaging queues, for example. Define an anycast routing type for an address so that its queues receive messages in a point-to-point manner. +Point-to-point messaging is a common scenario in which a message sent by a +producer has only one consumer. AMQP and JMS message producers and consumers +can make use of point-to-point messaging queues, for example. Define an anycast +routing type for an address so that its queues receive messages in a +point-to-point manner. -When a message is received on an address using anycast, Apache ActiveMQ Artemis locates the queue associated with the address and routes the message to it. When consumers request to consume from the address, the broker locates the relevant queue and associates this queue with the appropriate consumers. If multiple consumers are connected to the same queue, messages are distributed amongst each consumer equally, providing the consumers are equally able to handle them. +When a message is received on an address using anycast, Apache ActiveMQ Artemis +locates the queue associated with the address and routes the message to it. +When consumers request to consume from the address, the broker locates the +relevant queue and associates this queue with the appropriate consumers. If +multiple consumers are connected to the same queue, messages are distributed +amongst each consumer equally, providing the consumers are equally able to +handle them. ![Point to Point](images/addressing-model-p2p.png) Figure 1. Point to Point Messaging @@ -36,180 +69,207 @@ Figure 1. Point to Point Messaging Open the file `/etc/broker.xml` for editing. -Add an address configuration element and its associated queue if they do not exist already. +Add an address configuration element and its associated queue if they do not +exist already. -**Note** For normal Point to Point semantics, the queue name **MUST** match the address name. +**Note:** For normal Point to Point semantics, the queue name **MUST** match the +address name. ```xml - - - ... -
+ +
- + -
- - +
+
``` ### Publish-Subscribe Messaging -In a publish-subscribe scenario, messages are sent to every consumer subscribed to an address. JMS topics and MQTT subscriptions are two examples of publish-subscribe messaging. +In a publish-subscribe scenario, messages are sent to every consumer subscribed +to an address. JMS topics and MQTT subscriptions are two examples of +publish-subscribe messaging. -To configure an address with publish-subscribe semantics, create an address with the multicast routing type. +To configure an address with publish-subscribe semantics, create an address +with the multicast routing type. ![Publish Subscribe](images/addressing-model-pubsub.png) Figure 2. Publish-Subscribe #### Using the Multicast Routing Type -Open the file /etc/broker.xml for editing. +Open the file `/etc/broker.xml` for editing. Add an address configuration element with multicast routing type. ```xml - - - ... -
+ +
-
- - +
+ ``` -When clients connect to an address with the multicast element, a subscription queue for the client will be automatically created for the client. It is also possible to pre-configure subscription queues and connect to them directly using the queue's [Fully Qualified Queue names](#fully-qualified-queue-names). +When clients connect to an address with the multicast element, a subscription +queue for the client will be automatically created for the client. It is also +possible to pre-configure subscription queues and connect to them directly +using the queue's [Fully Qualified Queue names](#fully-qualified-queue-names). -Optionally add one or more queue elements to the address and wrap the multicast element around them. This step is typically not needed since the broker will automatically create a queue for each subscription requested by a client. +Optionally add one or more queue elements to the address and wrap the multicast +element around them. This step is typically not needed since the broker will +automatically create a queue for each subscription requested by a client. ```xml - - - ... -
+ +
- - + + -
- - +
+ ``` Figure 3. Point-to-Point with Two Queues ### Point-to-Point Address multiple Queues -It is actually possible to define more than one queue on an address with an anycast routing type. When messages are received on such an address, they are firstly distributed evenly across all the defined queues. Using [Fully Qualified Queue names](#fully-qualified-queue-names), clients are able to select the queue that they would like to subscribe to. Should more than one consumer connect direct to a single queue, Apache ActiveMQ Artemis will take care of distributing messages between them, as in the example above. +It is actually possible to define more than one queue on an address with an +anycast routing type. When messages are received on such an address, they are +firstly distributed evenly across all the defined queues. Using [Fully +Qualified Queue names](#fully-qualified-queue-names), clients are able to +select the queue that they would like to subscribe to. Should more than one +consumer connect direct to a single queue, Apache ActiveMQ Artemis will take +care of distributing messages between them, as in the example above. ![Point to Point](images/addressing-model-p2p2.png) Figure 3. Point-to-Point with Two Queues --------------------------------------------------------------------------------------------- -**Note:** This is how Apache ActiveMQ Artemis handles load balancing of queues across multiple nodes in a cluster. -Configuring a Point-to-Point Address with two queues, open the file /etc/broker.xml for editing. +**Note:** This is how Apache ActiveMQ Artemis handles load balancing of queues +across multiple nodes in a cluster. Configuring a Point-to-Point Address with +two queues, open the file `/etc/broker.xml` for editing. -Add an address configuration with Anycast routing type element and its associated queues. +Add an address configuration with Anycast routing type element and its +associated queues. ```xml - - - ... -
+ +
- - + + -
- - +
+ ``` ### Point-to-Point and Publish-Subscribe Addresses -It is possible to define an address with both point-to-point and publish-subscribe semantics enabled. While not typically recommend, this can be useful when you want, for example, a JMS Queue say orders and a JMS Topic named orders. The different routing types make the addresses appear to be distinct. +It is possible to define an address with both point-to-point and +publish-subscribe semantics enabled. While not typically recommend, this can be +useful when you want, for example, a JMS Queue say orders and a JMS Topic named +orders. The different routing types make the addresses appear to be distinct. -Using an example of JMS Clients, the messages sent by a JMS message producer will be routed using the anycast routing type. Messages sent by a JMS topic producer will use the multicast routing type. In addition when a JMS topic consumer attaches, it will be attached to it’s own subscription queue. JMS queue consumer will be attached to the anycast queue. +Using an example of JMS Clients, the messages sent by a JMS message producer +will be routed using the anycast routing type. Messages sent by a JMS topic +producer will use the multicast routing type. In addition when a JMS topic +consumer attaches, it will be attached to it’s own subscription queue. JMS +queue consumer will be attached to the anycast queue. ![Point to Point](images/addressing-model-p2p-pubsub.png) -Figure 4. [Point-to-Point and Publish-Subscribe - --------------------------------------------------------------------------------------------- -**Note:** The behavior in this scenario is dependent on the protocol being used. For JMS there is a clear distinction between topic and queue producers and consumers, which make the logic straight forward. Other protocols like AMQP do not make this distinction. A message being sent via AMQP will be routed by both anycast and multicast and consumers will default to anycast. For more information, please check the behavior of each protocol in the sections on protocols. - -The XML snippet below is an example of what the configuration for an address using both anycast and multicast would look like in /etc/broker.xml. Note that subscription queues are typically created on demand, so there is no need to list specific queue elements inside the multicast routing type. +Figure 4. Point-to-Point and Publish-Subscribe + +**Note:** The behavior in this scenario is dependent on the protocol being +used. For JMS there is a clear distinction between topic and queue producers +and consumers, which make the logic straight forward. Other protocols like AMQP +do not make this distinction. A message being sent via AMQP will be routed by +both anycast and multicast and consumers will default to anycast. For more +information, please check the behavior of each protocol in the sections on +protocols. + +The XML snippet below is an example of what the configuration for an address +using both anycast and multicast would look like in +`/etc/broker.xml`. Note that subscription queues are typically +created on demand, so there is no need to list specific queue elements inside +the multicast routing type. ```xml - - - ... -
+ +
- + -
- - +
+ ``` ## How to filter messages -Apache ActiveMQ Artemis supports the ability to filter messages using Apache Artemis [Filter Expressions](filter-expressions.md). +Apache ActiveMQ Artemis supports the ability to filter messages using Apache +Artemis [Filter Expressions](filter-expressions.md). Filters can be applied in two places, on a queue and on a consumer. ### Queue Filter -When a filter is applied to a queue, messages are filter before they sent to the queue. To add a queue filter use the -filter element when configuring a queue. Open up the broker.xml and add an address with a queue, using the filter element -to configure a filter on this queue. +When a filter is applied to a queue, messages are filter before they sent to +the queue. To add a queue filter use the filter element when configuring a +queue. Open up `/etc/broker.xml` and add an address with a +queue, using the filter element to configure a filter on this queue. ```xml -
- - - -
+ +
+ + + +
+
``` -The filter defined above ensures that only messages with an attribute "color='red'" is sent to this queue. +The filter defined above ensures that only messages with an attribute +`"color='red'"` is sent to this queue. ### Consumer Filters -Consumer filters are applied after messages have reached a queue and are defined using the appropriate client APIs. The -follow JMS example shows how to consumer filters work. +Consumer filters are applied after messages have reached a queue and are +defined using the appropriate client APIs. The follow JMS example shows how to +consumer filters work. 1. Define an address with a single queue, with no filter applied. ```xml -
- -
+ +
+ +
+
``` ```java - ... - // Send some messages - for (int i = 0; i < 3; i ++) { - TextMessage redMessage = senderSession.createTextMessage("Red"); - redMessage.setStringProperty("color", "red"); - producer.send(redMessage) - - TextMessage greenMessage = senderSession.createTextMessage("Green"); - greenMessage.setStringProperty("color", "green"); - producer.send(greenMessage) - } +... +// Send some messages +for (int i = 0; i < 3; i ++) { + TextMessage redMessage = senderSession.createTextMessage("Red"); + redMessage.setStringProperty("color", "red"); + producer.send(redMessage) + + TextMessage greenMessage = senderSession.createTextMessage("Green"); + greenMessage.setStringProperty("color", "green"); + producer.send(greenMessage) +} ``` At this point the queue would have 6 messages: red,green,red,green,red,green ```java - MessageConsumer redConsumer = redSession.createConsumer(queue, "color='red'"); +MessageConsumer redConsumer = redSession.createConsumer(queue, "color='red'"); ``` -The redConsumer has a filter that only matches "red" messages. The redConsumer will receive 3 messages. +The redConsumer has a filter that only matches "red" messages. The redConsumer +will receive 3 messages. ``` red, red, red @@ -223,92 +283,100 @@ green, green, green ## Automatic Address/Queue Management -You can configure Apache ActiveMQ Artemis to automatically create addresses and queues, and then delete them when they are no longer in use. This saves you from having to preconfigure each address and queue before a client can connect to it. Automatic creation and deletion is configured on a per address basis and is controlled by following: +You can configure Apache ActiveMQ Artemis to automatically create addresses and +queues, and then delete them when they are no longer in use. This saves you +from having to preconfigure each address and queue before a client can connect +to it. Automatic creation and deletion is configured on a per address basis and +is controlled by following: -| Parameter | Description | -|-----------|-------------| -| auto-create-addresses | When set to true, the broker will create the address requested by the client if it does not exist already. The default is true.| -| auto-delete-addresses | When set to true, the broker will be delete any **auto-created** adddress once all of it’s queues have been deleted. The default is true | -|default-address-routing-type | The routing type to use if the client does not specify one. Possible values are MULTICAST and ANYCAST. See earlier in this chapter for more information about routing types. The default value is MULTICAST. | +Parameter|Description +---|--- +`auto-create-addresses`|When set to true, the broker will create the address requested by the client if it does not exist already. The default is `true`. +`auto-delete-addresses`|When set to true, the broker will be delete any **auto-created** adddress once all of it’s queues have been deleted. The default is `true` +`default-address-routing-type`|The routing type to use if the client does not specify one. Possible values are `MULTICAST` and `ANYCAST`. See earlier in this chapter for more information about routing types. The default value is `MULTICAST`. ### Auto Address Creation -- Edit the file `/etc/broker.xml` and add the `auto-create-addresses` element to the `address-setting` you want the broker to automatically create. +- Edit the file `/etc/broker.xml` and add the + `auto-create-addresses` element to the `address-setting` you want the broker + to automatically create. -- (Optional) Add the `address-setting` if it does not exits. Use the match parameter and the [wildcard syntax](wildcard-syntax.md) to match more than one specific address. +- (Optional) Add the `address-setting` if it does not exits. Use the match + parameter and the [wildcard syntax](wildcard-syntax.md) to match more than + one specific address. - Set `auto-create-addresses` to `true` -- (Optional) Assign `MULTICAST` or `ANYCAST` as the default routing type for the address. +- (Optional) Assign `MULTICAST` or `ANYCAST` as the default routing type for + the address. -The example below configures an `address-setting` to be automatically created by the broker. The default routing type to be used if not specified by the client is MULTICAST. Note that wildcard syntax is used. Any address starting with /news/politics/ will be automatically created by the broker. +The example below configures an `address-setting` to be automatically created +by the broker. The default routing type to be used if not specified by the +client is MULTICAST. Note that wildcard syntax is used. Any address starting +with `/news/politics/` will be automatically created by the broker. ```xml - - - ... - - - true - MULTICAST - - - ... - - + + true + MULTICAST + ``` ### Auto Address Deletion -Edit the file `/etc/broker.xml` and add the `auto-delete-addresses` element to the `address-setting` you want the broker to automatically create. +- Edit the file `/etc/broker.xml` and add the + `auto-delete-addresses` element to the `address-setting` you want the broker to + automatically create. -(Optional) Add the `address-setting` if it does not exits. Use the match parameter and the [wildcard syntax](wildcard-syntax.md) to match more than one specific address. +- (Optional) Add the `address-setting` if it does not exits. Use the match + parameter and the [wildcard syntax](wildcard-syntax.md) to match more than one + specific address. -Set `auto-delete-addresses` to `true` +- Set `auto-delete-addresses` to `true` -The example below configures an `address-setting` to be automatically deleted by the broker. Note that wildcard syntax is used. Any address request by the client that starts with `/news/politics/` is configured to be automatically deleted by the broker. +The example below configures an `address-setting` to be automatically deleted +by the broker. Note that wildcard syntax is used. Any address request by the +client that starts with `/news/politics/` is configured to be automatically +deleted by the broker. ```xml - - - ... - - - true - MULTICAST - - - ... - - + + true + MULTICAST + ``` ## "Fully Qualified" Queue Names -Internally the broker maps a client’s request for an address to specific queues. The broker decides on behalf of the client which queues to send messages to or from which queue to receive messages. However, more advanced use cases might require that the client specify a queue directly. In these situations the client and use a fully qualified queue name, by specifying both the address name and the queue name, separated by a ::. +Internally the broker maps a client’s request for an address to specific +queues. The broker decides on behalf of the client which queues to send +messages to or from which queue to receive messages. However, more advanced use +cases might require that the client specify a queue directly. In these +situations the client and use a fully qualified queue name, by specifying both +the address name and the queue name, separated by a ::. -Currently Artemis supports fully qualified queue names on Core, AMQP, JMS, OpenWire, MQTT and Stomp protocols for receiving messages only. +Currently Artemis supports fully qualified queue names on Core, AMQP, JMS, +OpenWire, MQTT and Stomp protocols for receiving messages only. ### Specifying a Fully Qualified Queue Name -In this example, the address foo is configured with two queues q1, q2 as shown in the configuration below. + +In this example, the address foo is configured with two queues q1, q2 as shown +in the configuration below. ```xml - - - ... - -
- - - - -
-
-
-
+ +
+ + + + +
+
``` -In the client code, use both the address name and the queue name when requesting a connection from the broker. Remember to use two colons, ::, to separate the names, as in the example Java code below. +In the client code, use both the address name and the queue name when +requesting a connection from the broker. Remember to use two colons, `::`, to +separate the names, as in the example Java code below. ```java String FQQN = "foo::q1"; @@ -318,302 +386,407 @@ MessageConsumer consumer = session.createConsumer(q1); ## Using Prefixes to Determine Routing Type -Normally, if the broker receives a message sent to a particular address, that has both `ANYCAST` and `MULTICAST` routing types enable, it will route a copy of the message to **one** of the `ANYCAST` queues and to **all** of the `MULTICAST` queues. +Normally, if the broker receives a message sent to a particular address, that +has both `ANYCAST` and `MULTICAST` routing types enable, it will route a copy +of the message to **one** of the `ANYCAST` queues and to **all** of the +`MULTICAST` queues. -However, clients can specify a special prefix when connecting to an address to indicate which kind of routing type to use. The prefixes are custom values that are designated using the anycastPrefix and multicastPrefix parameters within the URL of an acceptor. +However, clients can specify a special prefix when connecting to an address to +indicate which kind of routing type to use. The prefixes are custom values that +are designated using the anycastPrefix and multicastPrefix parameters within +the URL of an acceptor. ### Configuring an Anycast Prefix -In `/etc/broker.xml`, add the `anycastPrefix` to the URL of the desired acceptor. In the example below, the acceptor is configured to use `anycast://` for the `anycastPrefix`. Client code can specify `anycast://foo/` if the client needs to send a message to only one of the `ANYCAST` queues. +In `/etc/broker.xml`, add the `anycastPrefix` to the URL of +the desired acceptor. In the example below, the acceptor is configured to use +`anycast://` for the `anycastPrefix`. Client code can specify `anycast://foo/` +if the client needs to send a message to only one of the `ANYCAST` queues. ```xml - - - ... - - tcp://0.0.0.0:61616?protocols=AMQP;anycastPrefix=anycast:// - - ... - - +tcp://0.0.0.0:61616?protocols=AMQP;anycastPrefix=anycast:// ``` ### Configuring a Multicast Prefix -In `/etc/broker.xml`, add the `multicastPrefix` to the URL of the desired acceptor. In the example below, the acceptor is configured to use `multicast://` for the `multicastPrefix`. Client code can specify `multicast://foo/` if the client needs to send a message to only one of the `MULTICAST` queues. +In `/etc/broker.xml`, add the `multicastPrefix` to the URL of +the desired acceptor. In the example below, the acceptor is configured to use +`multicast://` for the `multicastPrefix`. Client code can specify +`multicast://foo/` if the client needs to send a message to only one of the +`MULTICAST` queues. ```xml - - - ... - - tcp://0.0.0.0:61616?protocols=AMQP;multicastPrefix=multicast:// - - ... - - +tcp://0.0.0.0:61616?protocols=AMQP;multicastPrefix=multicast:// ``` ## Advanced Address Configuration ### Static Subscription Queues -In most cases it’s not necessary to statically configure subscription queues. The relevant protocol managers take care of dynamically creating subscription queues when clients request to subscribe to an address. The type of subscription queue created depends on what properties the client request. For example, durable, non-shared, shared etc. Protocol managers use special queue naming conventions to identify which queues belong to which consumers and users need not worry about the details. - -However, there are scenarios where a user may want to use broker side configuration to statically configure a subscription and later connect to that queue directly using a [Fully Qualified Queue name](#fully-qualified-queue-names). The examples below show how to use broker side configuration to statically configure a queue with publish subscribe behavior for shared, non-shared, durable and non-durable subscription behavior. +In most cases it’s not necessary to statically configure subscription queues. +The relevant protocol managers take care of dynamically creating subscription +queues when clients request to subscribe to an address. The type of +subscription queue created depends on what properties the client request. For +example, durable, non-shared, shared etc. Protocol managers use special queue +naming conventions to identify which queues belong to which consumers and users +need not worry about the details. + +However, there are scenarios where a user may want to use broker side +configuration to statically configure a subscription and later connect to that +queue directly using a [Fully Qualified Queue +name](#fully-qualified-queue-names). The examples below show how to use broker +side configuration to statically configure a queue with publish subscribe +behavior for shared, non-shared, durable and non-durable subscription behavior. #### Shared, Durable Subscription Queue using max-consumers -The default behavior for queues is to not limit the number connected queue consumers. The **max-consumers** parameter of the queue element can be used to limit the number of connected consumers allowed at any one time. +The default behavior for queues is to not limit the number connected queue +consumers. The **max-consumers** parameter of the queue element can be used to +limit the number of connected consumers allowed at any one time. Open the file `/etc/broker.xml` for editing. ```xml - - - ... -
+ +
- - - true - + + + true + -
- - +
+ ``` #### Non-shared, Durable Subscription Queue -The broker can be configured to prevent more than one consumer from connecting to a queue at any one time. The subscriptions to queues configured this way are therefore "non-shared". To do this simply set the **max-consumers** parameter to "1" +The broker can be configured to prevent more than one consumer from connecting +to a queue at any one time. The subscriptions to queues configured this way are +therefore "non-shared". To do this simply set the **max-consumers** parameter +to `1`: ```xml - - - ... -
+ +
- - - true - + + + true + -
- - +
+ ``` #### Non-durable Subscription Queue -Non-durable subscriptions are again usually managed by the relevant protocol manager, by creating and deleting temporary queues. +Non-durable subscriptions are again usually managed by the relevant protocol +manager, by creating and deleting temporary queues. -If a user requires to pre-create a queue that behaves like a non-durable subscription queue the **purge-on-no-consumers** flag can be enabled on the queue. When **purge-on-no-consumers** is set to **true**. The queue will not start receiving messages until a consumer is attached. When the last consumer is detached from the queue. The queue is purged (it's messages are removed) and will not receive any more messages until a new consumer is attached. +If a user requires to pre-create a queue that behaves like a non-durable +subscription queue the **purge-on-no-consumers** flag can be enabled on the +queue. When **purge-on-no-consumers** is set to **true**. The queue will not +start receiving messages until a consumer is attached. When the last consumer +is detached from the queue. The queue is purged (it's messages are removed) +and will not receive any more messages until a new consumer is attached. Open the file `/etc/broker.xml` for editing. ```xml - - - ... -
+ +
- + -
- - +
+ ``` #### Exclusive Consumer Queue -If a user requires to statically configure a queue that routes exclusively to one active consumer the **exclusive** flag can be enabled on the queue. +If a user requires to statically configure a queue that routes exclusively to +one active consumer the **exclusive** flag can be enabled on the queue. -When **exclusive** is set to **true** the queue will route messages to the a single active consumer. When the active consumer that is being routed to is detached from the queue, if another active consumer exist, one will be chosen and routing will now be exclusive to it. +When **exclusive** is set to **true** the queue will route messages to the a +single active consumer. When the active consumer that is being routed to is +detached from the queue, if another active consumer exist, one will be chosen +and routing will now be exclusive to it. See [Exclusive Queue](exclusive-queues.md) for further information. Open the file `/etc/broker.xml` for editing. ```xml - - - ... -
+ +
- + -
- - +
+ ``` ## Protocol Managers -A "protocol manager" maps protocol-specific concepts down to the core addressing model (using addresses, queues and routing types). For example, when a client sends a MQTT subscription packet with the addresses: +A "protocol manager" maps protocol-specific concepts down to the core +addressing model (using addresses, queues and routing types). For example, when +a client sends a MQTT subscription packet with the addresses: ``` /house/room1/lights /house/room2/lights ``` -The MQTT protocol manager understands that the two addresses require `MULTICAST` semantics. The protocol manager will therefore first look to ensure that `MULTICAST` is enabled for both addresses. If not, it will attempt to dynamically create them. If successful, the protocol manager will then create special subscription queues with special names, for each subscription requested by the client. +The MQTT protocol manager understands that the two addresses require +`MULTICAST` semantics. The protocol manager will therefore first look to ensure +that `MULTICAST` is enabled for both addresses. If not, it will attempt to +dynamically create them. If successful, the protocol manager will then create +special subscription queues with special names, for each subscription requested +by the client. -The special name allows the protocol manager to quickly identify the required client subscription queues should the client disconnect and reconnect at a later date. If the subscription is temporary the protocol manager will delete the queue once the client disconnects. +The special name allows the protocol manager to quickly identify the required +client subscription queues should the client disconnect and reconnect at a +later date. If the subscription is temporary the protocol manager will delete +the queue once the client disconnects. -When a client requests to subscribe to a point to point address. The protocol manager will look up the queue associated with the point to point address. This queue should have the same name as the addresss. +When a client requests to subscribe to a point to point address. The protocol +manager will look up the queue associated with the point to point address. +This queue should have the same name as the addresss. -**Note:** If the queue is auto created, it will be auto deleted once there are no consumers and no messages in it. For more information on auto create see the next section [Configuring Addresses and Queues via Address Settings](#configuring-addresses-and-queues-via-address-settings) +**Note:** If the queue is auto created, it will be auto deleted once there are +no consumers and no messages in it. For more information on auto create see +the next section [Configuring Addresses and Queues via Address +Settings](#configuring-addresses-and-queues-via-address-settings) ## Configuring Addresses and Queues via Address Settings -There are some attributes that are defined against an address wildcard -rather than a specific address/queue. Here an example of an `address-setting` -entry that would be found in the `broker.xml` file. +There are some attributes that are defined against an address wildcard rather +than a specific address/queue. Here an example of an `address-setting` entry +that would be found in the `broker.xml` file. ```xml DLA - 3 - 5000 ExpiryQueue - true + 123 + 5000 + 1.0 + 10000 + 3 100000 + -1 20000 + + PAGE + + true + true + 0 true - PAGE -1 NOTIFY 5 + true + true + true + true + true + true + OFF + true + true + OFF + 200 + false + -1 + + ``` -The idea with address settings, is you can provide a block of settings -which will be applied against any addresses that match the string in the -`match` attribute. In the above example the settings would only be -applied to the address "order.foo" address but you can also use wildcards -to apply settings. See: [The chapter on the wild card syntax](wildcard-syntax.md). - -For example, if you used the `match` string `queue.#` the settings -would be applied to all addresses which start with `queue.` - -The meaning of the specific settings are explained fully throughout the -user manual, however here is a brief description with a link to the -appropriate chapter if available. +The idea with address settings, is you can provide a block of settings which +will be applied against any addresses that match the string in the `match` +attribute. In the above example the settings would only be applied to the +address "order.foo" address but you can also use +[wildcards](wildcard-syntax.md) to apply settings. + +For example, if you used the `match` string `queue.#` the settings would be +applied to all addresses which start with `queue.` + +The meaning of the specific settings are explained fully throughout the user +manual, however here is a brief description with a link to the appropriate +chapter if available. + +`dead-letter-address` is the address to which messages are sent when they +exceed `max-delivery-attempts`. If no address is defined here then such +messages will simply be discarded. Read more about [undelivered +messages](undelivered-messages.md#configuring-dead-letter-addresses). + +`expiry-address` defines where to send a message that has expired. If no +address is defined here then such messages will simply be discarded. Read more +about [message expiry](message-expiry.md#configuring-expiry-addresses). + +`expiry-delay` defines the expiration time that will be used for messages which +are using the default expiration time (i.e. 0). For example, if `expiry-delay` +is set to "10" and a message which is using the default expiration time (i.e. +0) arrives then its expiration time of "0" will be changed to "10." However, if +a message which is using an expiration time of "20" arrives then its expiration +time will remain unchanged. Setting `expiry-delay` to "-1" will disable this +feature. The default is "-1". Read more about [message +expiry](message-expiry.md#configuring-expiry-addresses). `max-delivery-attempts` defines how many time a cancelled message can be -redelivered before sending to the `dead-letter-address`. A full -explanation can be found [here](undelivered-messages.md#configuring-dead-letter-addresses). - -`redelivery-delay` defines how long to wait before attempting redelivery -of a cancelled message. see [here](undelivered-messages.md#configuring-delayed-redelivery). - -`expiry-address` defines where to send a message that has expired. see -[here](message-expiry.md#configuring-expiry-addresses). - -`expiry-delay` defines the expiration time that will be used for -messages which are using the default expiration time (i.e. 0). For -example, if `expiry-delay` is set to "10" and a message which is using -the default expiration time (i.e. 0) arrives then its expiration time of -"0" will be changed to "10." However, if a message which is using an -expiration time of "20" arrives then its expiration time will remain -unchanged. Setting `expiry-delay` to "-1" will disable this feature. The -default is "-1". - -`last-value-queue` defines whether a queue only uses last values or not. -see [here](last-value-queues.md). - -`max-size-bytes` and `page-size-bytes` are used to set paging on an -address. This is explained [here](paging.md#configuration). - -`redistribution-delay` defines how long to wait when the last consumer -is closed on a queue before redistributing any messages. see -[here](clusters.md#message-redistribution). - -`send-to-dla-on-no-route`. If a message is sent to an address, but the -server does not route it to any queues, for example, there might be no -queues bound to that address, or none of the queues have filters that -match, then normally that message would be discarded. However if this -parameter is set to true for that address, if the message is not routed -to any queues it will instead be sent to the dead letter address (DLA) -for that address, if it exists. - -`address-full-policy`. This attribute can have one of the following -values: PAGE, DROP, FAIL or BLOCK and determines what happens when an -address where `max-size-bytes` is specified becomes full. The default -value is PAGE. If the value is PAGE then further messages will be paged -to disk. If the value is DROP then further messages will be silently -dropped. If the value is FAIL then further messages will be dropped and -an exception will be thrown on the client-side. If the value is BLOCK -then client message producers will block when they try and send further -messages. See the following chapters for more info [Flow Control](flow-control.md), [Paging](paging.md). - -`slow-consumer-threshold`. The minimum rate of message consumption -allowed before a consumer is considered "slow." Measured in -messages-per-second. Default is -1 (i.e. disabled); any other valid -value must be greater than 0. - -`slow-consumer-policy`. What should happen when a slow consumer is -detected. `KILL` will kill the consumer's connection (which will -obviously impact any other client threads using that same connection). -`NOTIFY` will send a CONSUMER\_SLOW management notification which an -application could receive and take action with. See [slow consumers](slow-consumers.md) for more details -on this notification. +redelivered before sending to the `dead-letter-address`. Read more about +[undelivered +messages](undelivered-messages.md#configuring-dead-letter-addresses). + +`redelivery-delay` defines how long to wait before attempting redelivery of a +cancelled message. Default is `0`. Read more about [undelivered +messages](undelivered-messages.md#configuring-delayed-redelivery). + +`redelivery-delay-multiplier` defines the number by which the +`redelivery-delay` will be multiplied on each subsequent redelivery attempt. +Default is `1.0`. Read more about [undelivered +messages](undelivered-messages.md#configuring-delayed-redelivery). + +`max-size-bytes`, `page-size-bytes`, & `page-max-cache-size` are used to +configure paging on an address. This is explained +[here](paging.md#configuration). + +`max-size-bytes-reject-threshold` is used with the address full `BLOCK` policy, +the maximum size (in bytes) an address can reach before messages start getting +rejected. Works in combination with `max-size-bytes` **for AMQP clients only**. +Default is `-1` (i.e. no limit). + +`address-full-policy`. This attribute can have one of the following values: +`PAGE`, `DROP`, `FAIL` or `BLOCK` and determines what happens when an address +where `max-size-bytes` is specified becomes full. The default value is `PAGE`. +If the value is `PAGE` then further messages will be paged to disk. If the +value is `DROP` then further messages will be silently dropped. If the value is +`FAIL` then further messages will be dropped and an exception will be thrown on +the client-side. If the value is `BLOCK` then client message producers will +block when they try and send further messages. See the [Flow +Control](flow-control.md) and [Paging](paging.md) chapters for more info. + +`message-counter-history-day-limit` is the number of days to keep message +counter history for this address assuming that `message-counter-enabled` is +`true`. Default is `0`. + +`last-value-queue` is **deprecated**. See `default-last-value-queue`. It +defines whether a queue only uses last values or not. Default is `false`. Read +more about [last value queues](last-value-queues.md). + +`default-last-value-queue` defines whether a queue only uses last values or +not. Default is `false`. This value can be overridden at the queue level using +the `last-value` boolean. Read more about [last value +queues](last-value-queues.md). + +`default-exclusive-queue` defines whether a queue will serve only a single +consumer. Default is `false`. This value can be overridden at the queue level +using the `exclusive` boolean. Read more about [exclusive +queues](exclusive-queues.md). + +`redistribution-delay` defines how long to wait when the last consumer is +closed on a queue before redistributing any messages. Read more about +[clusters](clusters.md#message-redistribution). + +`send-to-dla-on-no-route`. If a message is sent to an address, but the server +does not route it to any queues (e.g. there might be no queues bound to that +address, or none of the queues have filters that match) then normally that +message would be discarded. However, if this parameter is `true` then such a +message will instead be sent to the `dead-letter-address` (DLA) for that +address, if it exists. + +`slow-consumer-threshold`. The minimum rate of message consumption allowed +before a consumer is considered "slow." Measured in messages-per-second. +Default is `-1` (i.e. disabled); any other valid value must be greater than 0. +Read more about [slow consumers](slow-consumers.md). + +`slow-consumer-policy`. What should happen when a slow consumer is detected. +`KILL` will kill the consumer's connection (which will obviously impact any +other client threads using that same connection). `NOTIFY` will send a +CONSUMER\_SLOW management notification which an application could receive and +take action with. Read more about [slow consumers](slow-consumers.md). `slow-consumer-check-period`. How often to check for slow consumers on a -particular queue. Measured in seconds. Default is 5. See [slow consumers](slow-consumers.md) -for more information about slow consumer detection. - -`auto-create-jms-queues`. Whether or not the broker should automatically -create a JMS queue when a JMS message is sent to a queue whose name fits -the address `match` (remember, a JMS queue is just a core queue which has -the same address and queue name) or a JMS consumer tries to connect to a -queue whose name fits the address `match`. Queues which are auto-created -are durable, non-temporary, and non-transient. Default is `true`. This is -_DEPRECATED_. See `auto-create-queues`. - -`auto-delete-jms-queues`. Whether or not the broker should automatically -delete auto-created JMS queues when they have both 0 consumers and 0 messages. -Default is `true`. This is _DEPRECATED_. See `auto-delete-queues`. - -`auto-create-jms-topics`. Whether or not the broker should automatically -create a JMS topic when a JMS message is sent to a topic whose name fits -the address `match` (remember, a JMS topic is just a core address which has -one or more core queues mapped to it) or a JMS consumer tries to subscribe -to a topic whose name fits the address `match`. Default is `true`. This is -_DEPRECATED_. See `auto-create-addresses`. - -`auto-delete-jms-topics`. Whether or not the broker should automatically -delete auto-created JMS topics once the last subscription on the topic has -been closed. Default is `true`. This is _DEPRECATED_. See `auto-delete-addresses`. - -`auto-create-queues`. Whether or not the broker should automatically -create a queue when a message is sent or a consumer tries to connect to a -queue whose name fits the address `match`. Queues which are auto-created -are durable, non-temporary, and non-transient. Default is `true`. - -`auto-delete-queues`. Whether or not the broker should automatically -delete auto-created queues when they have both 0 consumers and 0 messages. -Default is `true`. - -`config-delete-queues`. How the broker should handle queues deleted -on config reload, by delete policy: `OFF` or `FORCE`. -See [config-reload](config-reload.md) for more details. -Default is `OFF`. - -`auto-create-addresses`. Whether or not the broker should automatically -create an address when a message is sent to or a consumer tries to consume -from a queue which is mapped to an address whose name fits the address `match`. -Default is `true`. - -`auto-delete-addresses`. Whether or not the broker should automatically -delete auto-created addresses once the address no longer has any queues. +particular queue. Measured in *seconds*. Default is `5`. Read more about [slow +consumers](slow-consumers.md). + +`auto-create-jms-queues` is **deprecated**. See `auto-create-queues`. Whether +or not the broker should automatically create a JMS queue when a JMS message is +sent to a queue whose name fits the address `match` (remember, a JMS queue is +just a core queue which has the same address and queue name) or a JMS consumer +tries to connect to a queue whose name fits the address `match`. Queues which +are auto-created are durable, non-temporary, and non-transient. Default is +`true`. + +`auto-delete-jms-queues` is **deprecated**. See `auto-delete-queues`. Whether +or not the broker should automatically delete auto-created JMS queues when they +have both 0 consumers and 0 messages. Default is `true`. + +`auto-create-jms-topics` is **deprecated**. See `auto-create-addresses`. +Whether or not the broker should automatically create a JMS topic when a JMS +message is sent to a topic whose name fits the address `match` (remember, a JMS +topic is just a core address which has one or more core queues mapped to it) or +a JMS consumer tries to subscribe to a topic whose name fits the address +`match`. Default is `true`. + +`auto-delete-jms-topics` is **deprecated**. See `auto-delete-addresses`. +Whether or not the broker should automatically delete auto-created JMS topics +once the last subscription on the topic has been closed. Default is `true`. + +`auto-create-queues`. Whether or not the broker should automatically create a +queue when a message is sent or a consumer tries to connect to a queue whose +name fits the address `match`. Queues which are auto-created are durable, +non-temporary, and non-transient. Default is `true`. + +`auto-delete-queues`. Whether or not the broker should automatically delete +auto-created queues when they have both 0 consumers and 0 messages. Default is +`true`. + +`config-delete-queues`. How the broker should handle queues deleted on config +reload, by delete policy: `OFF` or `FORCE`. Default is `OFF`. Read more about +[configuration reload](config-reload.md). + +`auto-create-addresses`. Whether or not the broker should automatically create +an address when a message is sent to or a consumer tries to consume from a +queue which is mapped to an address whose name fits the address `match`. Default is `true`. -`config-delete-addresses`. How the broker should handle addresses deleted -on config reload, by delete policy: `OFF` or `FORCE`. -See [config-reload](config-reload.md) for more details. -Default is `OFF`. \ No newline at end of file +`auto-delete-addresses`. Whether or not the broker should automatically delete +auto-created addresses once the address no longer has any queues. Default is +`true`. + +`config-delete-addresses`. How the broker should handle addresses deleted on +config reload, by delete policy: `OFF` or `FORCE`. Default is `OFF`. Read more +about [configuration reload](config-reload.md). + +`management-browse-page-size` is the number of messages a management resource +can browse. This is relevant for the "browse" management method exposed on the +queue control. Default is `200`. + +`default-purge-on-no-consumers` defines a queue's default +`purge-on-no-consumers` setting if none is provided on the queue itself. +Default is `false`. This value can be overridden at the queue level using the +`purge-on-no-consumers` boolean. Read more about [this +functionality](#non-durable-subscription-queue). + +`default-max-consumers` defines a queue's default `max-consumers` setting if +none is provided on the queue itself. Default is `-1` (i.e. no limit). This +value can be overridden at the queue level using the `max-consumers` boolean. +Read more about [this +functionality](#shared-durable-subscription-queue-using-max-consumers). + +`default-queue-routing-type` defines the routing-type for an auto-created queue +if the broker is unable to determine the routing-type based on the client +and/or protocol semantics. Default is `MULTICAST`. Read more about [routing +types](#routing-type). + +`default-address-routing-type` defines the routing-type for an auto-created +address if the broker is unable to determine the routing-type based on the +client and/or protocol semantics. Default is `MULTICAST`. Read more about +[routing types](#routing-type). diff --git a/docs/user-manual/en/amqp.md b/docs/user-manual/en/amqp.md new file mode 100644 index 00000000000..a201fbd998b --- /dev/null +++ b/docs/user-manual/en/amqp.md @@ -0,0 +1,129 @@ +# AMQP + +Apache ActiveMQ Artemis supports the [AMQP +1.0](https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=amqp) +specification. By default there are `acceptor` elements configured to accept +AMQP connections on ports `61616` and `5672`. + +See the general [Protocols and Interoperability](protocols-interoperability.md) +chapter for details on configuring an `acceptor` for AMQP. + +You can use *any* AMQP 1.0 compatible clients. + +A short list includes: + +- [qpid clients](https://qpid.apache.org/download.html) +- [.NET Clients](https://blogs.apache.org/activemq/entry/using-net-libraries-with-activemq) +- [Javascript NodeJS](https://github.com/noodlefrenzy/node-amqp10) +- [Java Script RHEA](https://github.com/grs/rhea) +- ... and many others. + +## Examples + +We have a few examples as part of the Artemis distribution: + +- .NET: + - ./examples/protocols/amqp/dotnet +- ProtonCPP + - ./examples/protocols/amqp/proton-cpp + - ./examples/protocols/amqp/proton-clustered-cpp +- Ruby + - ./examples/protocols/amqp/proton-ruby +- Java (Using the qpid JMS Client) + - ./examples/protocols/amqp/queue +- Interceptors + - ./examples/features/standard/interceptor-amqp + - ./examples/features/standard/broker-plugin + +## Message Conversions + +The broker will not perform any message conversion to any other protocols when +sending AMQP and receiving AMQP. + +However if you intend your message to be received by an AMQP JMS Client, you +must follow the [JMS Mapping +Conventions](https://www.oasis-open.org/committees/download.php/53086/amqp-bindmap-jms-v1.0-wd05.pdf). +If you send a body type that is not recognized by this specification the +conversion between AMQP and any other protocol will make it a Binary Message. +Make sure you follow these conventions if you intend to cross protocols or +languages. Especially on the message body. + +A compatibility setting allows aligning the naming convention of AMQP queues +(JMS Durable and Shared Subscriptions) with CORE. For backwards compatibility +reasons, you need to explicitly enable this via broker configuration: + +- `amqp-use-core-subscription-naming` + - `true` - use queue naming convention that is aligned with CORE. + - `false` (default) - use older naming convention. + +## Intercepting and changing messages + +We don't recommend changing messages at the server's side for a few reasons: + +- AMQP messages are meant to be immutable +- The message won't be the original message the user sent +- AMQP has the possibility of signing messages. The signature would be broken. +- For performance reasons. We try not to re-encode (or even decode) messages. + +If regardless these recommendations you still need and want to intercept and +change AMQP messages, look at the aforementioned interceptor examples. + +## AMQP and security + +The Apache ActiveMQ Artemis Server accepts the PLAIN, ANONYMOUS, and GSSAPI +SASL mechanism. These are implemented on the broker's [security](security.md) +infrastructure. + +## AMQP and destinations + +If an AMQP Link is dynamic then a temporary queue will be created and either +the remote source or remote target address will be set to the name of the +temporary queue. If the Link is not dynamic then the the address of the remote +target or source will used for the queue. If this does not exist then it will +be auto-created if the settings allow. + +## AMQP and Multicast Addresses (Topics) + +Although AMQP has no notion of "topics" it is still possible to treat AMQP +consumers or receivers as subscriptions rather than just consumers on a queue. +By default any receiving link that attaches to an address that has only +`multicast` enabled will be treated as a subscription and a corresponding +subscription queue will be created. If the Terminus Durability is either +`UNSETTLED_STATE` or `CONFIGURATION` then the queue will be made durable +(similar to a JMS durable subscription) and given a name made up from the +container id and the link name, something like `my-container-id:my-link-name`. +If the Terminus Durability is configured as `NONE` then a volatile `multicast` +queue will be created. + +## AMQP and Coordinations - Handling Transactions + +An AMQP links target can also be a Coordinator. A Coordinator is used to handle +transactions. If a coordinator is used then the underlying server session will +be transacted and will be either rolled back or committed via the coordinator. + +> **Note:** +> +> AMQP allows the use of multiple transactions per session, +> `amqp:multi-txns-per-ssn`, however in this version of Apache ActiveMQ Artemis +> will only support single transactions per session. + +## AMQP scheduling message delivery + +An AMQP message can provide scheduling information that controls the time in +the future when the message will be delivered at the earliest. This +information is provided by adding a message annotation to the sent message. + +There are two different message annotations that can be used to schedule a +message for later delivery: + +- `x-opt-delivery-time` + The specified value must be a positive long corresponding to the time the + message should be made available for delivery (in milliseconds). + +- `x-opt-delivery-delay` + The specified value must be a positive long corresponding to the amount of + milliseconds after the broker receives the given message before it should be + made available for delivery. + +If both annotations are present in the same message then the broker will prefer +the more specific `x-opt-delivery-time` value. diff --git a/docs/user-manual/en/architecture.md b/docs/user-manual/en/architecture.md index 80d70b2bf5e..6a73a64cab9 100644 --- a/docs/user-manual/en/architecture.md +++ b/docs/user-manual/en/architecture.md @@ -1,125 +1,126 @@ # Core Architecture -Apache ActiveMQ Artemis core is designed simply as set of Plain Old Java Objects -(POJOs) - we hope you like its clean-cut design. +Apache ActiveMQ Artemis core is designed simply as set of Plain Old Java +Objects (POJOs) - we hope you like its clean-cut design. -Each Apache ActiveMQ Artemis server has its own ultra high performance persistent -journal, which it uses for message and other persistence. +Each Apache ActiveMQ Artemis server has its own ultra high performance +persistent journal, which it uses for message and other persistence. Using a high performance journal allows outrageous persistence message -performance, something not achievable when using a relational database -for persistence. +performance, something not achievable when using a relational database for +persistence (although JDBC is still an option if necessary). -Apache ActiveMQ Artemis clients, potentially on different physical machines interact -with the Apache ActiveMQ Artemis server. Apache ActiveMQ Artemis currently provides two APIs for -messaging at the client side: +Apache ActiveMQ Artemis clients, potentially on different physical machines, +interact with the Apache ActiveMQ Artemis broker. Apache ActiveMQ Artemis +currently ships two API implementations for messaging at the client side: -1. Core client API. This is a simple intuitive Java API that is aligned with the Artemis internal Core. Allowing more - control of broker objects, like for example, direct creation of addresses and queues. The Core API also offers a - full set of messaging functionality without some of the complexities of JMS. +1. Core client API. This is a simple intuitive Java API that is aligned with + the Artemis internal Core. Allowing more control of broker objects (e.g + direct creation of addresses and queues). The Core API also offers a full set + of messaging functionality without some of the complexities of JMS. -2. JMS client API. The standard JMS API is available at the client side. +2. JMS 2.0 client API. The standard JMS API is available at the client side. -Apache ActiveMQ Artemis also provides different protocol implementations on the server so you can use respective clients for these protocols: - -1. AMQP -2. OpenWire -3. MQTT -4. STOMP -5. HornetQ (for use with HornetQ clients). -6. CORE (Artemis CORE protocol) +Apache ActiveMQ Artemis also provides different protocol implementations on the +server so you can use respective clients for these protocols: +- AMQP +- OpenWire +- MQTT +- STOMP +- HornetQ (for use with HornetQ clients). +- Core (Artemis CORE protocol) JMS semantics are implemented by a JMS facade layer on the client side. -The Apache ActiveMQ Artemis server does not speak JMS and in fact does not know -anything about JMS, it is a protocol agnostic messaging server designed -to be used with multiple different protocols. +The Apache ActiveMQ Artemis broker does not speak JMS and in fact does not know +anything about JMS, it is a protocol agnostic messaging server designed to be +used with multiple different protocols. -When a user uses the JMS API on the client side, all JMS interactions -are translated into operations on the Apache ActiveMQ Artemis core client API before -being transferred over the wire using the Apache ActiveMQ Artemis wire format. +When a user uses the JMS API on the client side, all JMS interactions are +translated into operations on the Apache ActiveMQ Artemis core client API +before being transferred over the wire using the core protocol. -The server always just deals with core API interactions. +The broker always just deals with core API interactions. A schematic illustrating this relationship is shown in figure 3.1 below: ![ActiveMQ Artemis architecture1](images/architecture1.jpg) -Figure 3.1 shows two user applications interacting with an Apache ActiveMQ Artemis -server. User Application 1 is using the JMS API, while User Application +Figure 3.1 shows two user applications interacting with an Apache ActiveMQ +Artemis server. User Application 1 is using the JMS API, while User Application 2 is using the core client API directly. -You can see from the diagram that the JMS API is implemented by a thin -facade layer on the client side. +You can see from the diagram that the JMS API is implemented by a thin facade +layer on the client side. ## Stand-alone Broker The normal stand-alone messaging broker configuration comprises a core -messaging broker and a number of protocol managers that provide support for -the various protocol mentioned earlier. Protocol managers are pluggable -if you +messaging broker and a number of protocol managers that provide support for the +various protocol mentioned earlier. -The stand-alone broker configuration uses [Airline](https://github.com/airlift/airline) -for bootstrapping the Broker. +The stand-alone broker configuration uses +[Airline](https://github.com/airlift/airline) for bootstrapping the Broker. The stand-alone broker architecture is shown in figure 3.3 below: ![ActiveMQ Artemis architecture3](images/architecture3.jpg) -For more information on server configuration files see [Server Configuration](configuration-index.md) +For more information on server configuration files see [Server +Configuration](configuration-index.md) ## Embedded Broker -Apache ActiveMQ Artemis core is designed as a set of simple POJOs so if you have an -application that requires messaging functionality internally but you +Apache ActiveMQ Artemis core is designed as a set of simple POJOs so if you +have an application that requires messaging functionality internally but you don't want to expose that as an Apache ActiveMQ Artemis broker you can directly -instantiate and embed Apache ActiveMQ Artemis brokers in your own application. +instantiate and embed brokers in your own application. -For more information on embedding Apache ActiveMQ Artemis, see [Embedding Apache ActiveMQ Artemis](embedding-activemq.md). +Read more about [embedding Apache ActiveMQ Artemis](embedding-activemq.md). ## Integrated with a Java EE application server -Apache ActiveMQ Artemis provides its own fully functional Java Connector Architecture -(JCA) adaptor which enables it to be integrated easily into any Java EE -compliant application server or servlet engine. +Apache ActiveMQ Artemis provides its own fully functional Java Connector +Architecture (JCA) adaptor which enables it to be integrated easily into any +Java EE compliant application server or servlet engine. Java EE application servers provide Message Driven Beans (MDBs), which are a -special type of Enterprise Java Beans (EJBs) that can process messages -from sources such as JMS systems or mail systems. +special type of Enterprise Java Beans (EJBs) that can process messages from +sources such as JMS systems or mail systems. Probably the most common use of an MDB is to consume messages from a JMS messaging system. According to the Java EE specification, a Java EE application server uses a JCA -adapter to integrate with a JMS messaging system so it can consume -messages for MDBs. - -However, the JCA adapter is not only used by the Java EE application server -for *consuming* messages via MDBs, it is also used when sending message -to the JMS messaging system e.g. from inside an EJB or servlet. - -When integrating with a JMS messaging system from inside a Java EE -application server it is always recommended that this is done via a JCA -adaptor. In fact, communicating with a JMS messaging system directly, -without using JCA would be illegal according to the Java EE specification. - -The application server's JCA service provides extra functionality such -as connection pooling and automatic transaction enlistment, which are -desirable when using messaging, say, from inside an EJB. It is possible -to talk to a JMS messaging system directly from an EJB, MDB or servlet -without going through a JCA adapter, but this is not recommended since -you will not be able to take advantage of the JCA features, such as -caching of JMS sessions, which can result in poor performance. - -Figure 3.2 below shows a Java EE application server integrating with a -Apache ActiveMQ Artemis server via the Apache ActiveMQ Artemis JCA adaptor. Note that all -communication between EJB sessions or entity beans and Message Driven -beans go through the adaptor and not directly to Apache ActiveMQ Artemis. - -The large arrow with the prohibited sign shows an EJB session bean -talking directly to the Apache ActiveMQ Artemis server. This is not recommended as -you'll most likely end up creating a new connection and session every -time you want to interact from the EJB, which is an anti-pattern. +adapter to integrate with a JMS messaging system so it can consume messages for +MDBs. + +However, the JCA adapter is not only used by the Java EE application server for +*consuming* messages via MDBs, it is also used when sending message to the JMS +messaging system e.g. from inside an EJB or servlet. + +When integrating with a JMS messaging system from inside a Java EE application +server it is always recommended that this is done via a JCA adaptor. In fact, +communicating with a JMS messaging system directly, without using JCA would be +illegal according to the Java EE specification. + +The application server's JCA service provides extra functionality such as +connection pooling and automatic transaction enlistment, which are desirable +when using messaging, say, from inside an EJB. It is possible to talk to a JMS +messaging system directly from an EJB, MDB or servlet without going through a +JCA adapter, but this is not recommended since you will not be able to take +advantage of the JCA features, such as caching of JMS sessions, which can +result in poor performance. + +Figure 3.2 below shows a Java EE application server integrating with a Apache +ActiveMQ Artemis server via the Apache ActiveMQ Artemis JCA adaptor. Note that +all communication between EJB sessions or entity beans and Message Driven beans +go through the adaptor and not directly to Apache ActiveMQ Artemis. + +The large arrow with the prohibited sign shows an EJB session bean talking +directly to the Apache ActiveMQ Artemis server. This is not recommended as +you'll most likely end up creating a new connection and session every time you +want to interact from the EJB, which is an anti-pattern. ![ActiveMQ Artemis architecture2](images/architecture2.jpg) diff --git a/docs/user-manual/en/broker-plugins.md b/docs/user-manual/en/broker-plugins.md index 6fd03a810ba..442339a985c 100644 --- a/docs/user-manual/en/broker-plugins.md +++ b/docs/user-manual/en/broker-plugins.md @@ -1,50 +1,46 @@ # Apache ActiveMQ Artemis Plugin Support Apache ActiveMQ Artemis is designed to allow extra functionality to be added by -creating a plugin. Multiple plugins can be registered at the same time and they will be chained -together and executed in the order they are registered. (i.e. the first plugin registered -is always executed first). +creating a plugin. Multiple plugins can be registered at the same time and they +will be chained together and executed in the order they are registered (i.e. +the first plugin registered is always executed first). -Creating a plugin is very simple. It requires implementing the [`ActiveMQServerPlugin`](https://github.com/apache/activemq-artemis/blob/master/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java) -interface, making sure the plugin is on the classpath, and registering it with the broker. Only the methods that you want to add behavior for need to be implemented as all of the interface methods are default methods. +Creating a plugin is very simple. It requires: -## Adding the plugin to the classpath +- Implementing the [`ActiveMQServerPlugin`](https://github.com/apache/activemq-artemis/blob/master/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java) + interface +- Making sure the plugin is [on the classpath](using-server.md#adding-runtime-dependencies) +- Registering it with the broker either via [xml](#registering-a-plugin) or [programmatically](#registering-a-plugin-programmatically). -See the documentation on [adding runtime dependencies](using-server.md) to understand how to make your plugin available to the broker. - -If you are using an embed system than you will need the jar under the regular classpath of your embedded application. +Only the methods that you want to add behavior for need to be implemented as +all of the interface methods are default methods. ## Registering a Plugin -To register a plugin with by XML you need to add the `broker-plugins` element at the `broker.xml`. It is also possible -to pass configuration to a plugin using the `property` child element(s). These properties (zero to many) -will be read and passed into the Plugin's `init(Map)` operation after the plugin -has been instantiated. +To register a plugin with by XML you need to add the `broker-plugins` element +at the `broker.xml`. It is also possible to pass configuration to a plugin +using the `property` child element(s). These properties (zero to many) will be +read and passed into the plugin's `init(Map)` operation after +the plugin has been instantiated. ```xml - - - ... - - - - - - - ... - - + + + + + + ``` ## Registering a Plugin Programmatically For registering a plugin programmatically you need to call the -registerBrokerPlugin() method and pass in a new instance of your plugin. In the example below -assuming your plugin is called `UserPlugin`, registering it looks like the following: +`registerBrokerPlugin()` method and pass in a new instance of your plugin. In +the example below assuming your plugin is called `UserPlugin`, registering it +looks like the following: ``` java - ... Configuration config = new ConfigurationImpl(); @@ -53,144 +49,80 @@ Configuration config = new ConfigurationImpl(); config.registerBrokerPlugin(new UserPlugin()); ``` -## Using the LoggingActiveMQServerPlugin - -The LoggingActiveMQServerPlugin logs specific broker events. - -You can select which events are logged by setting the following configuration properties to `true`. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyProperty Description
LOG_CONNECTION_EVENTSLog info when a Connection is created/destroy. Default `false`.
LOG_SESSION_EVENTSLog info when a Session is created/closed. Default `false`.
LOG_CONSUMER_EVENTSLogs info when a Consumer is created/closed. Default `false`.
LOG_DELIVERING_EVENTSLogs info when message is delivered to a consumer and when a message is acknowledged by a consumer. - Default `false`
LOG_SENDING_EVENTSLogs info when a message has been sent to an address and when a message has been routed within the broker. - Default `false`
LOG_INTERNAL_EVENTSLogs info when a queue created/destroyed, when a message is expired, when a bridge is deployed and when a critical - failure occurs. Default `false`
LOG_ALL_EVENTSLogs info for all the above events. Default `false`
- -By default the LoggingActiveMQServerPlugin wil not log any information. The logging is activated by setting one (or a selection) -of the above configuration properties to `true`. - -To configure the plugin, you can add the following configuration to the broker. In the example below both LOG_DELIVERING_EVENTS -and LOG_SENDING_EVENTS will be logged by the broker. +## Using the `LoggingActiveMQServerPlugin` -```xml - +The `LoggingActiveMQServerPlugin` logs specific broker events. -... - - - - - - -... +You can select which events are logged by setting the following configuration +properties to `true`. + +Property|Trigger Event|Default Value +---|---|--- +`LOG_CONNECTION_EVENTS`|Connection is created/destroy.|`false` +`LOG_SESSION_EVENTS`|Session is created/closed.|`false` +`LOG_CONSUMER_EVENTS`|Consumer is created/closed|`false` +`LOG_DELIVERING_EVENTS`|Message is delivered to a consumer and when a message is acknowledged by a consumer.|`false` +`LOG_SENDING_EVENTS`|When a message has been sent to an address and when a message has been routed within the broker.|`false` +`LOG_INTERNAL_EVENTS`|When a queue created/destroyed, when a message is expired, when a bridge is deployed and when a critical failure occurs.|`false` +`LOG_ALL_EVENTS`|Includes all the above events.|`false` + +By default the `LoggingActiveMQServerPlugin` will not log any information. The +logging is activated by setting one (or a selection) of the above configuration +properties to `true`. + +To configure the plugin, you can add the following configuration to the broker. +In the example below both `LOG_DELIVERING_EVENTS` and `LOG_SENDING_EVENTS` will +be logged by the broker. - +```xml + + + + + + ``` -Most events in the LoggingActiveMQServerPlugin follow a `beforeX` and `afterX` notification pattern e.g beforeCreateConsumer() and afterCreateConsumer(). +Most events in the `LoggingActiveMQServerPlugin` follow a `beforeX` and +`afterX` notification pattern (e.g `beforeCreateConsumer()` and +`afterCreateConsumer()`). -At Log Level `INFO`, the LoggingActiveMQServerPlugin logs an entry when an `afterX` notification occurs. By setting the Logger -"org.apache.activemq.artemis.core.server.plugin.impl" to `DEBUG` Level, log entries are generated for both `beforeX` and `afterX` notifications. -Log Level `DEBUG` will also log more information for a notification when available. +At Log Level `INFO`, the LoggingActiveMQServerPlugin logs an entry when an +`afterX` notification occurs. By setting the logger +`org.apache.activemq.artemis.core.server.plugin.impl` to `DEBUG`, log entries +are generated for both `beforeX` and `afterX` notifications. Log level `DEBUG` +will also log more information for a notification when available. ## Using the NotificationActiveMQServerPlugin -The NotificationActiveMQServerPlugin can be configured to send extra notifications for specific broker events. - -You can select which notifications are sent by setting the following configuration properties to `true`. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyProperty Description
SEND_CONNECTION_NOTIFICATIONSSends a notification when a Connection is created/destroy. Default `false`.
SEND_SESSION_NOTIFICATIONSSends a notification when a Session is created/closed. Default `false`.
SEND_ADDRESS_NOTIFICATIONSSends a notification when an Address is added/removed. Default `false`.
SEND_DELIVERED_NOTIFICATIONSSends a notification when message is delivered to a consumer. Default `false`
SEND_EXPIRED_NOTIFICATIONSSends a notification when message has been expired by the broker. Default `false`
- -By default the NotificationActiveMQServerPlugin will not send any notifications. The plugin is activated by setting one (or a selection) -of the above configuration properties to `true`. - -To configure the plugin, you can add the following configuration to the broker. In the example below both SEND_CONNECTION_NOTIFICATIONS -and SEND_SESSION_NOTIFICATIONS will be sent by the broker. +The NotificationActiveMQServerPlugin can be configured to send extra +notifications for specific broker events. -```xml - +You can select which notifications are sent by setting the following +configuration properties to `true`. -... - - - - - - -... +Property|Property Description|Default Value +---|--- +`SEND_CONNECTION_NOTIFICATIONS`|Sends a notification when a Connection is created/destroy.|`false`. +`SEND_SESSION_NOTIFICATIONS`|Sends a notification when a Session is created/closed.|`false`. +`SEND_ADDRESS_NOTIFICATIONS`|Sends a notification when an Address is added/removed.|`false`. +`SEND_DELIVERED_NOTIFICATIONS`|Sends a notification when message is delivered to a consumer.|`false` +`SEND_EXPIRED_NOTIFICATIONS`|Sends a notification when message has been expired by the broker.|`false` - +By default the NotificationActiveMQServerPlugin will not send any +notifications. The plugin is activated by setting one (or a selection) of the +above configuration properties to `true`. + +To configure the plugin, you can add the following configuration to the broker. +In the example below both `SEND_CONNECTION_NOTIFICATIONS` and +`SEND_SESSION_NOTIFICATIONS` will be sent by the broker. + +```xml + + + + + + ``` diff --git a/docs/user-manual/en/client-classpath.md b/docs/user-manual/en/client-classpath.md index ea61c3a014c..b2820fed5e9 100644 --- a/docs/user-manual/en/client-classpath.md +++ b/docs/user-manual/en/client-classpath.md @@ -4,12 +4,13 @@ Apache ActiveMQ Artemis requires just a single jar on the *client classpath*. > **Warning** > -> The client jar mentioned here can be found in the `lib/client` directory of the -> Apache ActiveMQ Artemis distribution. Be sure you only use the jar from the correct -> version of the release, you *must not* mix and match versions of jars -> from different Apache ActiveMQ Artemis versions. Mixing and matching different jar -> versions may cause subtle errors and failures to occur. +> The client jar mentioned here can be found in the `lib/client` directory of +> the Apache ActiveMQ Artemis distribution. Be sure you only use the jar from +> the correct version of the release, you *must not* mix and match versions of +> jars from different Apache ActiveMQ Artemis versions. Mixing and matching +> different jar versions may cause subtle errors and failures to occur. -Whether you are using JMS or just the Core API simply add the `artemis-jms-client-all.jar` -from the `lib/client` directory to your client classpath. This is a "shaded" jar that -contains all the Artemis code plus dependencies (e.g. JMS spec, Netty, etc.). \ No newline at end of file +Whether you are using JMS or just the Core API simply add the +`artemis-jms-client-all.jar` from the `lib/client` directory to your client +classpath. This is a "shaded" jar that contains all the Artemis code plus +dependencies (e.g. JMS spec, Netty, etc.). diff --git a/docs/user-manual/en/client-reconnection.md b/docs/user-manual/en/client-reconnection.md index dec09096c7c..0c9a99529ae 100644 --- a/docs/user-manual/en/client-reconnection.md +++ b/docs/user-manual/en/client-reconnection.md @@ -6,111 +6,102 @@ connection between the client and the server. ## 100% Transparent session re-attachment -If the failure was due to some transient failure such as a temporary -network failure, and the target server was not restarted, then the -sessions will still be existent on the server, assuming the client -hasn't been disconnected for more than connection-ttl [Detecting Dead Connections](connection-ttl.md) +If the failure was due to some transient failure such as a temporary network +failure, and the target server was not restarted, then the sessions will still +be existent on the server, assuming the client hasn't been disconnected for +more than [connection-ttl](connection-ttl.md) -In this scenario, Apache ActiveMQ Artemis will automatically re-attach the client -sessions to the server sessions when the connection reconnects. This is -done 100% transparently and the client can continue exactly as if -nothing had happened. +In this scenario, Apache ActiveMQ Artemis will automatically re-attach the +client sessions to the server sessions when the connection reconnects. This is +done 100% transparently and the client can continue exactly as if nothing had +happened. The way this works is as follows: -As Apache ActiveMQ Artemis clients send commands to their servers they store each sent -command in an in-memory buffer. In the case that connection failure -occurs and the client subsequently reattaches to the same server, as -part of the reattachment protocol the server informs the client during -reattachment with the id of the last command it successfully received -from that client. +As Apache ActiveMQ Artemis clients send commands to their servers they store +each sent command in an in-memory buffer. In the case that connection failure +occurs and the client subsequently reattaches to the same server, as part of +the reattachment protocol the server informs the client during reattachment +with the id of the last command it successfully received from that client. -If the client has sent more commands than were received before failover -it can replay any sent commands from its buffer so that the client and -server can reconcile their states.Ac +If the client has sent more commands than were received before failover it can +replay any sent commands from its buffer so that the client and server can +reconcile their states.Ac The size of this buffer is configured with the `confirmationWindowSize` parameter on the connection URL. When the server has received -`confirmationWindowSize` bytes of commands and processed them it will -send back a command confirmation to the client, and the client can then -free up space in the buffer. +`confirmationWindowSize` bytes of commands and processed them it will send back +a command confirmation to the client, and the client can then free up space in +the buffer. The window is specified in bytes. Setting this parameter to `-1` disables any buffering and prevents any -re-attachment from occurring, forcing reconnect instead. The default -value for this parameter is `-1`. (Which means by default no auto -re-attachment will occur) +re-attachment from occurring, forcing reconnect instead. The default value for +this parameter is `-1`. (Which means by default no auto re-attachment will +occur) ## Session reconnection -Alternatively, the server might have actually been restarted after -crashing or being stopped. In this case any sessions will no longer be -existent on the server and it won't be possible to 100% transparently -re-attach to them. +Alternatively, the server might have actually been restarted after crashing or +being stopped. In this case any sessions will no longer be existent on the +server and it won't be possible to 100% transparently re-attach to them. -In this case, Apache ActiveMQ Artemis will automatically reconnect the connection and -*recreate* any sessions and consumers on the server corresponding to the -sessions and consumers on the client. This process is exactly the same -as what happens during failover onto a backup server. +In this case, Apache ActiveMQ Artemis will automatically reconnect the +connection and *recreate* any sessions and consumers on the server +corresponding to the sessions and consumers on the client. This process is +exactly the same as what happens during failover onto a backup server. -Client reconnection is also used internally by components such as core -bridges to allow them to reconnect to their target servers. +Client reconnection is also used internally by components such as core bridges +to allow them to reconnect to their target servers. -Please see the section on failover [Automatic Client Failover](ha.md) to get a full understanding of how -transacted and non-transacted sessions are reconnected during -failover/reconnect and what you need to do to maintain *once and only -once*delivery guarantees. +Please see the section on failover [Automatic Client Failover](ha.md) to get a +full understanding of how transacted and non-transacted sessions are +reconnected during failover/reconnect and what you need to do to maintain *once +and only once* delivery guarantees. ## Configuring reconnection/reattachment attributes Client reconnection is configured using the following parameters: -- `retryInterval`. This optional parameter determines the period in - milliseconds between subsequent reconnection attempts, if the - connection to the target server has failed. The default value is - `2000` milliseconds. +- `retryInterval`. This optional parameter determines the period in + milliseconds between subsequent reconnection attempts, if the connection to + the target server has failed. The default value is `2000` milliseconds. -- `retryIntervalMultiplier`. This optional parameter determines - determines a multiplier to apply to the time since the last retry to - compute the time to the next retry. +- `retryIntervalMultiplier`. This optional parameter determines determines a + multiplier to apply to the time since the last retry to compute the time to + the next retry. - This allows you to implement an *exponential backoff* between retry - attempts. + This allows you to implement an *exponential backoff* between retry attempts. - Let's take an example: + Let's take an example: - If we set `retryInterval` to `1000` ms and we set - `retryIntervalMultiplier` to `2.0`, then, if the first reconnect - attempt fails, we will wait `1000` ms then `2000` ms then `4000` ms - between subsequent reconnection attempts. + If we set `retryInterval` to `1000` ms and we set `retryIntervalMultiplier` + to `2.0`, then, if the first reconnect attempt fails, we will wait `1000` ms + then `2000` ms then `4000` ms between subsequent reconnection attempts. - The default value is `1.0` meaning each reconnect attempt is spaced - at equal intervals. + The default value is `1.0` meaning each reconnect attempt is spaced at equal + intervals. -- `maxRetryInterval`. This optional parameter determines the maximum - retry interval that will be used. When setting - `retryIntervalMultiplier` it would otherwise be possible that - subsequent retries exponentially increase to ridiculously large - values. By setting this parameter you can set an upper limit on that - value. The default value is `2000` milliseconds. +- `maxRetryInterval`. This optional parameter determines the maximum retry + interval that will be used. When setting `retryIntervalMultiplier` it would + otherwise be possible that subsequent retries exponentially increase to + ridiculously large values. By setting this parameter you can set an upper limit + on that value. The default value is `2000` milliseconds. -- `reconnectAttempts`. This optional parameter determines the total - number of reconnect attempts to make before giving up and shutting - down. A value of `-1` signifies an unlimited number of attempts. The - default value is `0`. +- `reconnectAttempts`. This optional parameter determines the total number of + reconnect attempts to make before giving up and shutting down. A value of + `-1` signifies an unlimited number of attempts. The default value is `0`. All of these parameters are set on the URL used to connect to the broker. -If your client does manage to reconnect but the session is no longer -available on the server, for instance if the server has been restarted -or it has timed out, then the client won't be able to re-attach, and any -`ExceptionListener` or `FailureListener` instances registered on the -connection or session will be called. +If your client does manage to reconnect but the session is no longer available +on the server, for instance if the server has been restarted or it has timed +out, then the client won't be able to re-attach, and any `ExceptionListener` or +`FailureListener` instances registered on the connection or session will be +called. -ExceptionListeners and SessionFailureListeners -============================================== +## ExceptionListeners and SessionFailureListeners -Please note, that when a client reconnects or re-attaches, any -registered JMS `ExceptionListener` or core API `SessionFailureListener` -will be called. +Please note, that when a client reconnects or re-attaches, any registered JMS +`ExceptionListener` or core API `SessionFailureListener` will be called. diff --git a/docs/user-manual/en/clusters.md b/docs/user-manual/en/clusters.md index b3f4a770480..54dc54e19b6 100644 --- a/docs/user-manual/en/clusters.md +++ b/docs/user-manual/en/clusters.md @@ -46,13 +46,13 @@ connect to them with the minimum of configuration. Server discovery is a mechanism by which servers can propagate their connection details to: -- Messaging clients. A messaging client wants to be able to connect to - the servers of the cluster without having specific knowledge of - which servers in the cluster are up at any one time. +- Messaging clients. A messaging client wants to be able to connect to + the servers of the cluster without having specific knowledge of + which servers in the cluster are up at any one time. -- Other servers. Servers in a cluster want to be able to create - cluster connections to each other without having prior knowledge of - all the other servers in the cluster. +- Other servers. Servers in a cluster want to be able to create + cluster connections to each other without having prior knowledge of + all the other servers in the cluster. This information, let's call it the Cluster Topology, is actually sent around normal Apache ActiveMQ Artemis connections to clients and to other servers over @@ -94,12 +94,12 @@ Let's take a look at an example broadcast group from ```xml - 172.16.9.3 - 5432 - 231.7.7.7 - 9876 - 2000 - netty-connector + 172.16.9.3 + 5432 + 231.7.7.7 + 9876 + 2000 + netty-connector ``` @@ -108,64 +108,66 @@ Some of the broadcast group parameters are optional and you'll normally use the defaults, but we specify them all in the above example for clarity. Let's discuss each one in turn: -- `name` attribute. Each broadcast group in the server must have a - unique name. +- `name` attribute. Each broadcast group in the server must have a + unique name. -- `local-bind-address`. This is the local bind address that the - datagram socket is bound to. If you have multiple network interfaces - on your server, you would specify which one you wish to use for - broadcasts by setting this property. If this property is not - specified then the socket will be bound to the wildcard address, an - IP address chosen by the kernel. This is a UDP specific attribute. +- `local-bind-address`. This is the local bind address that the + datagram socket is bound to. If you have multiple network interfaces + on your server, you would specify which one you wish to use for + broadcasts by setting this property. If this property is not + specified then the socket will be bound to the wildcard address, an + IP address chosen by the kernel. This is a UDP specific attribute. -- `local-bind-port`. If you want to specify a local port to which the - datagram socket is bound you can specify it here. Normally you would - just use the default value of `-1` which signifies that an anonymous - port should be used. This parameter is always specified in - conjunction with `local-bind-address`. This is a UDP specific - attribute. +- `local-bind-port`. If you want to specify a local port to which the + datagram socket is bound you can specify it here. Normally you would + just use the default value of `-1` which signifies that an anonymous + port should be used. This parameter is always specified in + conjunction with `local-bind-address`. This is a UDP specific + attribute. -- `group-address`. This is the multicast address to which the data - will be broadcast. It is a class D IP address in the range - `224.0.0.0` to `239.255.255.255`, inclusive. The address `224.0.0.0` - is reserved and is not available for use. This parameter is - mandatory. This is a UDP specific attribute. +- `group-address`. This is the multicast address to which the data + will be broadcast. It is a class D IP address in the range + `224.0.0.0` to `239.255.255.255`, inclusive. The address `224.0.0.0` + is reserved and is not available for use. This parameter is + mandatory. This is a UDP specific attribute. -- `group-port`. This is the UDP port number used for broadcasting. - This parameter is mandatory. This is a UDP specific attribute. +- `group-port`. This is the UDP port number used for broadcasting. + This parameter is mandatory. This is a UDP specific attribute. -- `broadcast-period`. This is the period in milliseconds between - consecutive broadcasts. This parameter is optional, the default - value is `2000` milliseconds. +- `broadcast-period`. This is the period in milliseconds between + consecutive broadcasts. This parameter is optional, the default + value is `2000` milliseconds. -- `connector-ref`. This specifies the connector and optional backup - connector that will be broadcasted (see [Configuring the Transport](configuring-transports.md) for more information on - connectors). +- `connector-ref`. This specifies the connector and optional backup + connector that will be broadcasted (see [Configuring the Transport](configuring-transports.md) for more information on + connectors). Here is another example broadcast group that defines a JGroups broadcast group: - - - test-jgroups-file_ping.xml - activemq_broadcast_channel - 2000 - netty-connector - - +```xml + + + test-jgroups-file_ping.xml + activemq_broadcast_channel + 2000 + netty-connector + + +``` To be able to use JGroups to broadcast, one must specify two attributes, i.e. `jgroups-file` and `jgroups-channel`, as discussed in details as following: -- `jgroups-file` attribute. This is the name of JGroups configuration - file. It will be used to initialize JGroups channels. Make sure the - file is in the java resource path so that Apache ActiveMQ Artemis can load it. +- `jgroups-file` attribute. This is the name of JGroups configuration + file. It will be used to initialize JGroups channels. Make sure the + file is in the java resource path so that Apache ActiveMQ Artemis can load it. -- `jgroups-channel` attribute. The name that JGroups channels connect - to for broadcasting. +- `jgroups-channel` attribute. The name that JGroups channels connect + to for broadcasting. -> **Note** +> **Note:** > > The JGroups attributes (`jgroups-file` and `jgroups-channel`) and UDP > specific attributes described above are exclusive of each other. Only @@ -174,57 +176,59 @@ following: The following is an example of a JGroups file - - - - - - - - - - - - - - - - - - +```xml + + + + + + + + + + + + + + + + + + +``` As it shows, the file content defines a jgroups protocol stacks. If you want Apache ActiveMQ Artemis to use this stacks for channel creation, you have to make @@ -233,7 +237,9 @@ configuration to be the name of this jgroups configuration file. For example if the above stacks configuration is stored in a file named "jgroups-stacks.xml" then your `jgroups-file` should be like - jgroups-stacks.xml +```xml +jgroups-stacks.xml +``` #### Discovery Groups @@ -252,18 +258,18 @@ of time it will remove that server's entry from its list. Discovery groups are used in two places in Apache ActiveMQ Artemis: -- By cluster connections so they know how to obtain an initial - connection to download the topology +- By cluster connections so they know how to obtain an initial + connection to download the topology -- By messaging clients so they know how to obtain an initial - connection to download the topology +- By messaging clients so they know how to obtain an initial + connection to download the topology Although a discovery group will always accept broadcasts, its current list of available live and backup servers is only ever used when an initial connection is made, from then server discovery is done over the normal Apache ActiveMQ Artemis connections. -> **Note** +> **Note:** > > Each discovery group must be configured with broadcast endpoint (UDP > or JGroups) that matches its broadcast group counterpart. For example, @@ -277,67 +283,71 @@ configuration file `broker.xml`. All discovery groups must be defined inside a `discovery-groups` element. There can be many discovery groups defined by Apache ActiveMQ Artemis server. Let's look at an example: - - - 172.16.9.7 - 231.7.7.7 - 9876 - 10000 - - +```xml + + + 172.16.9.7 + 231.7.7.7 + 9876 + 10000 + + +``` We'll consider each parameter of the discovery group: -- `name` attribute. Each discovery group must have a unique name per - server. - -- `local-bind-address`. If you are running with multiple network - interfaces on the same machine, you may want to specify that the - discovery group listens only only a specific interface. To do this - you can specify the interface address with this parameter. This - parameter is optional. This is a UDP specific attribute. - -- `group-address`. This is the multicast IP address of the group to - listen on. It should match the `group-address` in the broadcast - group that you wish to listen from. This parameter is mandatory. - This is a UDP specific attribute. - -- `group-port`. This is the UDP port of the multicast group. It should - match the `group-port` in the broadcast group that you wish to - listen from. This parameter is mandatory. This is a UDP specific - attribute. - -- `refresh-timeout`. This is the period the discovery group waits - after receiving the last broadcast from a particular server before - removing that servers connector pair entry from its list. You would - normally set this to a value significantly higher than the - `broadcast-period` on the broadcast group otherwise servers might - intermittently disappear from the list even though they are still - broadcasting due to slight differences in timing. This parameter is - optional, the default value is `10000` milliseconds (10 seconds). +- `name` attribute. Each discovery group must have a unique name per + server. + +- `local-bind-address`. If you are running with multiple network + interfaces on the same machine, you may want to specify that the + discovery group listens only only a specific interface. To do this + you can specify the interface address with this parameter. This + parameter is optional. This is a UDP specific attribute. + +- `group-address`. This is the multicast IP address of the group to + listen on. It should match the `group-address` in the broadcast + group that you wish to listen from. This parameter is mandatory. + This is a UDP specific attribute. + +- `group-port`. This is the UDP port of the multicast group. It should + match the `group-port` in the broadcast group that you wish to + listen from. This parameter is mandatory. This is a UDP specific + attribute. + +- `refresh-timeout`. This is the period the discovery group waits + after receiving the last broadcast from a particular server before + removing that servers connector pair entry from its list. You would + normally set this to a value significantly higher than the + `broadcast-period` on the broadcast group otherwise servers might + intermittently disappear from the list even though they are still + broadcasting due to slight differences in timing. This parameter is + optional, the default value is `10000` milliseconds (10 seconds). Here is another example that defines a JGroups discovery group: - - - test-jgroups-file_ping.xml - activemq_broadcast_channel - 10000 - - +```xml + + + test-jgroups-file_ping.xml + activemq_broadcast_channel + 10000 + + +``` To receive broadcast from JGroups channels, one must specify two attributes, `jgroups-file` and `jgroups-channel`, as discussed in details as following: -- `jgroups-file` attribute. This is the name of JGroups configuration - file. It will be used to initialize JGroups channels. Make sure the - file is in the java resource path so that Apache ActiveMQ Artemis can load it. +- `jgroups-file` attribute. This is the name of JGroups configuration + file. It will be used to initialize JGroups channels. Make sure the + file is in the java resource path so that Apache ActiveMQ Artemis can load it. -- `jgroups-channel` attribute. The name that JGroups channels connect - to for receiving broadcasts. +- `jgroups-channel` attribute. The name that JGroups channels connect + to for receiving broadcasts. -> **Note** +> **Note:** > > The JGroups attributes (`jgroups-file` and `jgroups-channel`) and UDP > specific attributes described above are exclusive of each other. Only @@ -355,7 +365,9 @@ differs depending on whether you're using JMS or the core API. Use the `udp` URL scheme and a host:port combination matches the group-address and group-port from the corresponding `broadcast-group` on the server: - udp://231.7.7.7:9876 +``` +udp://231.7.7.7:9876 +``` The element `discovery-group-ref` specifies the name of a discovery group defined in `broker.xml`. @@ -402,7 +414,9 @@ A static list of possible servers can also be used by a normal client. A list of servers to be used for the initial connection attempt can be specified in the connection URI using a syntax with `()`, e.g.: - (tcp://myhost:61616,tcp://myhost2:61616)?reconnectAttempts=5 +``` +(tcp://myhost:61616,tcp://myhost2:61616)?reconnectAttempts=5 +``` The brackets are expanded so the same query can be appended after the last bracket for ease. @@ -453,219 +467,223 @@ typical cluster connection. Cluster connections are always defined in There can be zero or more cluster connections defined per Apache ActiveMQ Artemis server. - - -
- netty-connector - 1000 - 5000 - 50000 - 5000 - 500 - 1.0 - 5000 - -1 - -1 - true - ON_DEMAND - 1 - 32000 - 30000 - 1000 - 2 - -
-
+```xml + + +
+ netty-connector + 1000 + 5000 + 50000 + 5000 + 500 + 1.0 + 5000 + -1 + -1 + true + ON_DEMAND + 1 + 32000 + 30000 + 1000 + 2 + +
+
+``` In the above cluster connection all parameters have been explicitly specified. The following shows all the available configuration options -- `address` Each cluster connection only applies to addresses that - match the specified `address` field. An address is matched on the - cluster connection when it begins with the string specified in this - field. The `address` field on a cluster connection also supports comma - separated lists and an exclude syntax `!`. To prevent an address - from being matched on this cluster connection, prepend a cluster - connection address string with `!`. - - In the case shown above the cluster connection will load balance - messages sent to all addresses (since it's empty). - - The address can be any value and you can have many cluster - connections with different values of `address`, simultaneously - balancing messages for those addresses, potentially to different - clusters of servers. By having multiple cluster connections on - different addresses a single Apache ActiveMQ Artemis Server can effectively take - part in multiple clusters simultaneously. - - Be careful not to have multiple cluster connections with overlapping - values of `address`, e.g. "europe" and "europe.news" since this - could result in the same messages being distributed between more - than one cluster connection, possibly resulting in duplicate - deliveries. - - Examples: - - - 'eu' - matches all addresses starting with 'eu' - - '!eu' - matches all address except for those starting with 'eu' - - 'eu.uk,eu.de' - matches all addresses starting with either 'eu.uk' or - 'eu.de' - - 'eu,!eu.uk' - matches all addresses starting with 'eu' but not those - starting with 'eu.uk' - - Notes: - - - Address exclusion will always takes precedence over address - inclusion. - - Address matching on cluster connections does not support - wild-card matching. - -- `connector-ref`. This is the connector which will be sent to other - nodes in the cluster so they have the correct cluster topology. - - This parameter is mandatory. - -- `check-period`. The period (in milliseconds) used to check if the - cluster connection has failed to receive pings from another server. - Default is 30000. - -- `connection-ttl`. This is how long a cluster connection should stay - alive if it stops receiving messages from a specific node in the - cluster. Default is 60000. - -- `min-large-message-size`. If the message size (in bytes) is larger - than this value then it will be split into multiple segments when - sent over the network to other cluster members. Default is 102400. - -- `call-timeout`. When a packet is sent via a cluster connection and - is a blocking call, i.e. for acknowledgements, this is how long it - will wait (in milliseconds) for the reply before throwing an - exception. Default is 30000. - -- `retry-interval`. We mentioned before that, internally, cluster - connections cause bridges to be created between the nodes of the - cluster. If the cluster connection is created and the target node - has not been started, or say, is being rebooted, then the cluster - connections from other nodes will retry connecting to the target - until it comes back up, in the same way as a bridge does. - - This parameter determines the interval in milliseconds between retry - attempts. It has the same meaning as the `retry-interval` on a - bridge (as described in [Core Bridges](core-bridges.md)). - - This parameter is optional and its default value is `500` - milliseconds. - -- `retry-interval-multiplier`. This is a multiplier used to increase - the `retry-interval` after each reconnect attempt, default is 1. - -- `max-retry-interval`. The maximum delay (in milliseconds) for - retries. Default is 2000. - -- `initial-connect-attempts`. The number of times the system will try - to connect a node in the cluster initially. If the max-retry is - achieved this node will be considered permanently down and the - system will not route messages to this node. Default is -1 (infinite - retries). - -- `reconnect-attempts`. The number of times the system will try to - reconnect to a node in the cluster. If the max-retry is achieved - this node will be considered permanently down and the system will - stop routing messages to this node. Default is -1 (infinite - retries). - -- `use-duplicate-detection`. Internally cluster connections use - bridges to link the nodes, and bridges can be configured to add a - duplicate id property in each message that is forwarded. If the - target node of the bridge crashes and then recovers, messages might - be resent from the source node. By enabling duplicate detection any - duplicate messages will be filtered out and ignored on receipt at - the target node. - - This parameter has the same meaning as `use-duplicate-detection` on - a bridge. For more information on duplicate detection, please see [Duplicate Detection](duplicate-detection.md). - Default is true. - -- `message-load-balancing`. This parameter determines if/how - messages will be distributed between other nodes of the cluster. - It can be one of three values - `OFF`, `STRICT`, or `ON_DEMAND` - (default). This parameter replaces the deprecated - `forward-when-no-consumers` parameter. - - If this is set to `OFF` then messages will never be forwarded to - another node in the cluster - - If this is set to `STRICT` then each incoming message will be round - robin'd even though the same queues on the other nodes of the - cluster may have no consumers at all, or they may have consumers - that have non matching message filters (selectors). Note that - Apache ActiveMQ Artemis will *not* forward messages to other nodes - if there are no *queues* of the same name on the other nodes, even - if this parameter is set to `STRICT`. Using `STRICT` is like setting - the legacy `forward-when-no-consumers` parameter to `true`. - - If this is set to `ON_DEMAND` then Apache ActiveMQ Artemis will only - forward messages to other nodes of the cluster if the address to which - they are being forwarded has queues which have consumers, and if those - consumers have message filters (selectors) at least one of those - selectors must match the message. Using `ON_DEMAND` is like setting - the legacy `forward-when-no-consumers` parameter to `false`. - - Default is `ON_DEMAND`. - -- `max-hops`. When a cluster connection decides the set of nodes to - which it might load balance a message, those nodes do not have to be - directly connected to it via a cluster connection. Apache ActiveMQ Artemis can be - configured to also load balance messages to nodes which might be - connected to it only indirectly with other Apache ActiveMQ Artemis servers as - intermediates in a chain. - - This allows Apache ActiveMQ Artemis to be configured in more complex topologies and - still provide message load balancing. We'll discuss this more later - in this chapter. - - The default value for this parameter is `1`, which means messages - are only load balanced to other Apache ActiveMQ Artemis serves which are directly - connected to this server. This parameter is optional. - -- `confirmation-window-size`. The size (in bytes) of the window used - for sending confirmations from the server connected to. So once the - server has received `confirmation-window-size` bytes it notifies its - client, default is 1048576. A value of -1 means no window. - -- `producer-window-size`. The size for producer flow control over cluster connection. - it's by default disabled through the cluster connection bridge but you may want - to set a value if you are using really large messages in cluster. A value of -1 means no window. - -- `call-failover-timeout`. Similar to `call-timeout` but used when a - call is made during a failover attempt. Default is -1 (no timeout). - -- `notification-interval`. How often (in milliseconds) the cluster - connection should broadcast itself when attaching to the cluster. - Default is 1000. - -- `notification-attempts`. How many times the cluster connection - should broadcast itself when connecting to the cluster. Default is - 2. - -- `discovery-group-ref`. This parameter determines which discovery - group is used to obtain the list of other servers in the cluster - that this cluster connection will make connections to. +- `address` Each cluster connection only applies to addresses that + match the specified `address` field. An address is matched on the + cluster connection when it begins with the string specified in this + field. The `address` field on a cluster connection also supports comma + separated lists and an exclude syntax `!`. To prevent an address + from being matched on this cluster connection, prepend a cluster + connection address string with `!`. + + In the case shown above the cluster connection will load balance + messages sent to all addresses (since it's empty). + + The address can be any value and you can have many cluster + connections with different values of `address`, simultaneously + balancing messages for those addresses, potentially to different + clusters of servers. By having multiple cluster connections on + different addresses a single Apache ActiveMQ Artemis Server can effectively take + part in multiple clusters simultaneously. + + Be careful not to have multiple cluster connections with overlapping + values of `address`, e.g. "europe" and "europe.news" since this + could result in the same messages being distributed between more + than one cluster connection, possibly resulting in duplicate + deliveries. + + Examples: + + - 'eu' + matches all addresses starting with 'eu' + - '!eu' + matches all address except for those starting with 'eu' + - 'eu.uk,eu.de' + matches all addresses starting with either 'eu.uk' or + 'eu.de' + - 'eu,!eu.uk' + matches all addresses starting with 'eu' but not those + starting with 'eu.uk' + + **Note:**: + + - Address exclusion will always takes precedence over address + inclusion. + - Address matching on cluster connections does not support + wild-card matching. + +- `connector-ref`. This is the connector which will be sent to other + nodes in the cluster so they have the correct cluster topology. + + This parameter is mandatory. + +- `check-period`. The period (in milliseconds) used to check if the + cluster connection has failed to receive pings from another server. + Default is 30000. + +- `connection-ttl`. This is how long a cluster connection should stay + alive if it stops receiving messages from a specific node in the + cluster. Default is 60000. + +- `min-large-message-size`. If the message size (in bytes) is larger + than this value then it will be split into multiple segments when + sent over the network to other cluster members. Default is 102400. + +- `call-timeout`. When a packet is sent via a cluster connection and + is a blocking call, i.e. for acknowledgements, this is how long it + will wait (in milliseconds) for the reply before throwing an + exception. Default is 30000. + +- `retry-interval`. We mentioned before that, internally, cluster + connections cause bridges to be created between the nodes of the + cluster. If the cluster connection is created and the target node + has not been started, or say, is being rebooted, then the cluster + connections from other nodes will retry connecting to the target + until it comes back up, in the same way as a bridge does. + + This parameter determines the interval in milliseconds between retry + attempts. It has the same meaning as the `retry-interval` on a + bridge (as described in [Core Bridges](core-bridges.md)). + + This parameter is optional and its default value is `500` + milliseconds. + +- `retry-interval-multiplier`. This is a multiplier used to increase + the `retry-interval` after each reconnect attempt, default is 1. + +- `max-retry-interval`. The maximum delay (in milliseconds) for + retries. Default is 2000. + +- `initial-connect-attempts`. The number of times the system will try + to connect a node in the cluster initially. If the max-retry is + achieved this node will be considered permanently down and the + system will not route messages to this node. Default is -1 (infinite + retries). + +- `reconnect-attempts`. The number of times the system will try to + reconnect to a node in the cluster. If the max-retry is achieved + this node will be considered permanently down and the system will + stop routing messages to this node. Default is -1 (infinite + retries). + +- `use-duplicate-detection`. Internally cluster connections use + bridges to link the nodes, and bridges can be configured to add a + duplicate id property in each message that is forwarded. If the + target node of the bridge crashes and then recovers, messages might + be resent from the source node. By enabling duplicate detection any + duplicate messages will be filtered out and ignored on receipt at + the target node. + + This parameter has the same meaning as `use-duplicate-detection` on + a bridge. For more information on duplicate detection, please see [Duplicate Detection](duplicate-detection.md). + Default is true. + +- `message-load-balancing`. This parameter determines if/how + messages will be distributed between other nodes of the cluster. + It can be one of three values - `OFF`, `STRICT`, or `ON_DEMAND` + (default). This parameter replaces the deprecated + `forward-when-no-consumers` parameter. + + If this is set to `OFF` then messages will never be forwarded to + another node in the cluster + + If this is set to `STRICT` then each incoming message will be round + robin'd even though the same queues on the other nodes of the + cluster may have no consumers at all, or they may have consumers + that have non matching message filters (selectors). Note that + Apache ActiveMQ Artemis will *not* forward messages to other nodes + if there are no *queues* of the same name on the other nodes, even + if this parameter is set to `STRICT`. Using `STRICT` is like setting + the legacy `forward-when-no-consumers` parameter to `true`. + + If this is set to `ON_DEMAND` then Apache ActiveMQ Artemis will only + forward messages to other nodes of the cluster if the address to which + they are being forwarded has queues which have consumers, and if those + consumers have message filters (selectors) at least one of those + selectors must match the message. Using `ON_DEMAND` is like setting + the legacy `forward-when-no-consumers` parameter to `false`. + + Default is `ON_DEMAND`. + +- `max-hops`. When a cluster connection decides the set of nodes to + which it might load balance a message, those nodes do not have to be + directly connected to it via a cluster connection. Apache ActiveMQ Artemis can be + configured to also load balance messages to nodes which might be + connected to it only indirectly with other Apache ActiveMQ Artemis servers as + intermediates in a chain. + + This allows Apache ActiveMQ Artemis to be configured in more complex topologies and + still provide message load balancing. We'll discuss this more later + in this chapter. + + The default value for this parameter is `1`, which means messages + are only load balanced to other Apache ActiveMQ Artemis serves which are directly + connected to this server. This parameter is optional. + +- `confirmation-window-size`. The size (in bytes) of the window used + for sending confirmations from the server connected to. So once the + server has received `confirmation-window-size` bytes it notifies its + client, default is 1048576. A value of -1 means no window. + +- `producer-window-size`. The size for producer flow control over cluster connection. + it's by default disabled through the cluster connection bridge but you may want + to set a value if you are using really large messages in cluster. A value of -1 means no window. + +- `call-failover-timeout`. Similar to `call-timeout` but used when a + call is made during a failover attempt. Default is -1 (no timeout). + +- `notification-interval`. How often (in milliseconds) the cluster + connection should broadcast itself when attaching to the cluster. + Default is 1000. + +- `notification-attempts`. How many times the cluster connection + should broadcast itself when connecting to the cluster. Default is + 2. + +- `discovery-group-ref`. This parameter determines which discovery + group is used to obtain the list of other servers in the cluster + that this cluster connection will make connections to. Alternatively if you would like your cluster connections to use a static list of servers for discovery then you can do it like this. - - ... - - server0-connector - server1-connector - - +```xml + + ... + + server0-connector + server1-connector + + +``` Here we have defined 2 servers that we know for sure will that at least one will be available. There may be many more servers in the cluster but @@ -678,8 +696,10 @@ When creating connections between nodes of a cluster to form a cluster connection, Apache ActiveMQ Artemis uses a cluster user and cluster password which is defined in `broker.xml`: - ACTIVEMQ.CLUSTER.ADMIN.USER - CHANGE ME!! +```xml +ACTIVEMQ.CLUSTER.ADMIN.USER +CHANGE ME!! +``` > **Warning** > @@ -701,35 +721,35 @@ policies, and you can also implement your own and use that. The out-of-the-box policies are -- Round Robin. With this policy the first node is chosen randomly then - each subsequent node is chosen sequentially in the same order. +- Round Robin. With this policy the first node is chosen randomly then + each subsequent node is chosen sequentially in the same order. - For example nodes might be chosen in the order B, C, D, A, B, C, D, - A, B or D, A, B, C, D, A, B, C, D or C, D, A, B, C, D, A, B, C. + For example nodes might be chosen in the order B, C, D, A, B, C, D, + A, B or D, A, B, C, D, A, B, C, D or C, D, A, B, C, D, A, B, C. - Use - `org.apache.activemq.artemis.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy` - as the ``. + Use + `org.apache.activemq.artemis.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy` + as the ``. -- Random. With this policy each node is chosen randomly. +- Random. With this policy each node is chosen randomly. - Use - `org.apache.activemq.artemis.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy` - as the ``. + Use + `org.apache.activemq.artemis.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy` + as the ``. -- Random Sticky. With this policy the first node is chosen randomly - and then re-used for subsequent connections. +- Random Sticky. With this policy the first node is chosen randomly + and then re-used for subsequent connections. - Use - `org.apache.activemq.artemis.api.core.client.loadbalance.RandomStickyConnectionLoadBalancingPolicy` - as the ``. + Use + `org.apache.activemq.artemis.api.core.client.loadbalance.RandomStickyConnectionLoadBalancingPolicy` + as the ``. -- First Element. With this policy the "first" (i.e. 0th) node is - always returned. +- First Element. With this policy the "first" (i.e. 0th) node is + always returned. - Use - `org.apache.activemq.artemis.api.core.client.loadbalance.FirstElementConnectionLoadBalancingPolicy` - as the ``. + Use + `org.apache.activemq.artemis.api.core.client.loadbalance.FirstElementConnectionLoadBalancingPolicy` + as the ``. You can also implement your own policy by implementing the interface `org.apache.activemq.artemis.api.core.client.loadbalance.ConnectionLoadBalancingPolicy` @@ -742,15 +762,17 @@ default will be used which is The parameter `connectionLoadBalancingPolicyClassName` can be set on the URI to configure what load balancing policy to use: - tcp://localhost:61616?connectionLoadBalancingPolicyClassName=org.apache.activemq.artemis.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy +``` +tcp://localhost:61616?connectionLoadBalancingPolicyClassName=org.apache.activemq.artemis.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy +``` The set of servers over which the factory load balances can be determined in one of two ways: -- Specifying servers explicitly in the URL. This also requires setting - the `useTopologyForLoadBalancing` parameter to `false` on the URL. +- Specifying servers explicitly in the URL. This also requires setting + the `useTopologyForLoadBalancing` parameter to `false` on the URL. -- Using discovery. This is the default behavior. +- Using discovery. This is the default behavior. ## Specifying Members of a Cluster Explicitly @@ -760,17 +782,19 @@ typically used to form non symmetrical clusters such as chain cluster or ring clusters. This can only be done using a static list of connectors and is configured as follows: - -
jms
- netty-connector - 500 - true - STRICT - 1 - - server1-connector - -
+```xml + +
+ netty-connector + 500 + true + STRICT + 1 + + server1-connector + + +``` In this example we have set the attribute `allow-direct-connections-only` which means that the only server that @@ -808,11 +832,13 @@ information on configuring address settings, please see [Configuring Addresses a Here's an address settings snippet from `broker.xml` showing how message redistribution is enabled for a set of queues: - - - 0 - - +```xml + + + 0 + + +``` The above `address-settings` block would set a `redistribution-delay` of `0` for any queue which is bound to any address. So the above would enable diff --git a/docs/user-manual/en/config-reload.md b/docs/user-manual/en/config-reload.md index 1ba76850b3b..630a13d6f07 100644 --- a/docs/user-manual/en/config-reload.md +++ b/docs/user-manual/en/config-reload.md @@ -1,8 +1,11 @@ # Configuration Reload -The system will perform a periodic check on the configuration files, configured by `configuration-file-refresh-period`, with the default at 5000, in milliseconds. +The system will perform a periodic check on the configuration files, configured +by `configuration-file-refresh-period`, with the default at 5000, in +milliseconds. -Once the configuration file is changed (broker.xml) the following modules will be reloaded automatically: +Once the configuration file is changed (broker.xml) the following modules will +be reloaded automatically: - Address Settings - Security Settings @@ -10,7 +13,7 @@ Once the configuration file is changed (broker.xml) the following modules will b - Addresses & queues -Notice: +**Note:** Deletion of Address's and Queue's, not auto created is controlled by Address Settings @@ -23,50 +26,63 @@ Deletion of Address's and Queue's, not auto created is controlled by Address Set * FORCE - will remove the queue upon config reload, even if messages remains, losing the messages in the queue. -By default both settings are OFF as such address & queues won't be removed upon reload, given the risk of losing messages. +By default both settings are OFF as such address & queues won't be removed upon +reload, given the risk of losing messages. -When OFF You may execute explicit CLI or Management operations to remove address & queues. +When OFF You may execute explicit CLI or Management operations to remove +address & queues. ## Reloadable Parameters -The broker configuration file has 2 main parts, `` and ``. Some of the parameters in the 2 parts are monitored and, -if modified, reloaded into the broker at runtime. +The broker configuration file has 2 main parts, `` and ``. Some of +the parameters in the 2 parts are monitored and, if modified, reloaded into the +broker at runtime. -Please note that elements under `` are deprecated. Users are encouraged to use `` configuration entities. +**Note:** Elements under `` are **deprecated**. Users are encouraged to +use `` configuration entities. -> *Note:* -> Most parameters reloaded take effect immediately after reloading. However there are some -> that won’t take any effect unless you restarting the broker. +> **Note:** +> +> Most parameters reloaded take effect immediately after reloading. However +> there are some that won’t take any effect unless you restarting the broker. > Such parameters are specifically indicated in the following text. -### `` +### `` #### `` * `` element -Changes to any elements will be reloaded. Each defines security roles for a matched address. +Changes to any `` elements will be reloaded. Each +`` defines security roles for a matched address. - * The `match` attribute +* The `match` attribute - This attribute defines the address for which the security-setting is defined. It can take wildcards such as ‘#’ and ‘*’. + This attribute defines the address for which the security-setting is + defined. It can take wildcards such as ‘#’ and ‘*’. - * The `` sub-elements +* The `` sub-elements - Each `` can have a list of `` elements, each of which defines a specific permission-roles mapping. - Each permission has 2 attributes ‘type’ and ‘roles’. The ‘type’ attribute defines the type of operation allowed, the ‘roles’ - defines which roles are allowed to perform such operation. Refer to the user’s manual for a list of operations that can be defined. +Each `` can have a list of `` elements, each +of which defines a specific permission-roles mapping. Each permission has 2 +attributes ‘type’ and ‘roles’. The ‘type’ attribute defines the type of +operation allowed, the ‘roles’ defines which roles are allowed to perform such +operation. Refer to the user’s manual for a list of operations that can be +defined. -> *Note:* -> Once loaded the security-settings will take effect immediately. Any new clients will subject -> to the new security settings. Any existing clients will subject to the new settings as well, as soon as they performs -> a new security-sensitive operation. +> **Note:** +> +> Once loaded the security-settings will take effect immediately. Any new +> clients will subject to the new security settings. Any existing clients will +> subject to the new settings as well, as soon as they performs a new +> security-sensitive operation. -Below lists the effects of adding, deleting and updating of an element/attribute within the `` element, whether -an change can be done or can’t be done. +Below lists the effects of adding, deleting and updating of an +element/attribute within the `` element, whether an change +can be done or can’t be done. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | X* (at most one element is allowed) | Deleting it means delete the whole security settings from the running broker. | N/A* `` | Adding one element means adding a new set of security roles for an address in the running broker | Deleting one element means removing a set of security roles for an address in the running broker | Updating one element means updating the security roles for an address (if match attribute is not changed), or means removing the old match address settings and adding a new one (if match attribute is changed) attribute `match` | N/A* | X* | Changing this value is same as deleting the whole with the old match value and adding @@ -81,21 +97,28 @@ attribute `roles` | N/A* | X* | Changing the ‘roles’ value means updating th * `` element -Changes to elements under `` will be reloaded into runtime broker. It contains a list of `` elements. +Changes to elements under `` will be reloaded into runtime +broker. It contains a list of `` elements. * `` element - - Each address-setting element has a ‘match’ attribute that defines an address pattern for which this address-setting is defined. It also has a list of sub-elements used to define the properties of a matching address. - - > *Note:* - > Parameters reloaded in this category will take effect immediately after reloading. The effect of deletion of Address's and Queue's, - > not auto created is controlled by parameter `config-delete-addresses` and `config-delete-queues` as described in the doc. - -Below lists the effects of adding, deleting and updating of an element/attribute within the address-settings element, whether an change -can be done or can’t be done. + + Each address-setting element has a ‘match’ attribute that defines an address + pattern for which this address-setting is defined. It also has a list of + sub-elements used to define the properties of a matching address. + + > **Note:** + > + > Parameters reloaded in this category will take effect immediately + > after reloading. The effect of deletion of Address's and Queue's, not auto + > created is controlled by parameter `config-delete-addresses` and + > `config-delete-queues` as described in the doc. + +Below lists the effects of adding, deleting and updating of an +element/attribute within the address-settings element, whether an change can be +done or can’t be done. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | X(at most one element is allowed) | Deleting it means delete the whole address settings from the running broker | N/A `` | Adding one element means adding a set of address-setting for a new address in the running broker | Deleting one means removing a set of address-setting for an address in the running broker | Updating one element means updating the address setting for an address (if match attribute is not changed), or means removing the old match address settings and adding a new one (if match attribute is changed) attribute `match` | N/A | X | Changing this value is same as deleting the whole with the old match value and adding a new one with the new match value. @@ -132,20 +155,22 @@ attribute `match` | N/A | X | Changing this value is same as deleting the whole #### `` -All `` elements will be reloaded. Each `` element -has a ‘name’ and several sub-elements that defines the properties of a divert. +All `` elements will be reloaded. Each `` element has a ‘name’ +and several sub-elements that defines the properties of a divert. -> *Note:* -> Reloading `` only resulting in deploying new diverts. Existing diverts -> won’t get undeployed even if you delete a `` element. Nor an existing -> divert will be updated if its element is updated after reloading. -> To make this happen you need a restart of the broker. +> **Note:** +> +> Reloading `` only resulting in deploying new diverts. Existing diverts +> won’t get undeployed even if you delete a `` element. Nor an existing +> divert will be updated if its element is updated after reloading. To make +> this happen you need a restart of the broker. -Below lists the effects of adding, deleting and updating of an element/attribute -within the diverts element, whether an change can be done or can’t be done. +Below lists the effects of adding, deleting and updating of an +element/attribute within the diverts element, whether an change can be done or +can’t be done. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | X (no more than one can be present) | Deleting it means delete (undeploy) all diverts in running broker. | N/A `` | Adding a new divert. It will be deployed after reloading | No effect on the deployed divert.(unless restarting broker, in which case the divert will no longer be deployed) | No effect on the deployed divert (unless restarting broker, in which case the divert will be redeployed) attribute `name` | N/A | X | A new divert with the name will be deployed. (if it is not already there in broker). Otherwise no effect. @@ -159,23 +184,28 @@ attribute `name` | N/A | X | A new divert with the name will be deployed. (if it #### `` -The `` element contains a list `
` elements. Once changed, all `
` elements - in `` will be reloaded. +The `` element contains a list `
` elements. Once changed, +all `
` elements in `` will be reloaded. -> *Note:* -> Once reloaded, all new addresses (as well as the pre-configured queues) will be -> deployed to the running broker and all those that are missing from the configuration will be undeployed. +> **Note:** +> +> Once reloaded, all new addresses (as well as the pre-configured queues) will +> be deployed to the running broker and all those that are missing from the +> configuration will be undeployed. -> *Note:* -> Parameters reloaded in this category will take effect immediately after reloading. -> The effect of deletion of Address's and Queue's, not auto created is controlled by -> parameter `config-delete-addresses` and `config-delete-queues` as described in this doc. +> **Note:** +> +> Parameters reloaded in this category will take effect immediately after +> reloading. The effect of deletion of Address's and Queue's, not auto created +> is controlled by parameter `config-delete-addresses` and +> `config-delete-queues` as described in this doc. -Below lists the effects of adding, deleting and updating of an element/attribute -within the `` element, whether an change can be done or can’t be done. +Below lists the effects of adding, deleting and updating of an +element/attribute within the `` element, whether an change can be +done or can’t be done. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | X(no more than one is present) | Deleting it means delete (undeploy) all diverts in running broker. | N/A `
` | A new address will be deployed in the running broker | The corresponding address will be undeployed. | N/A attribute `name` | N/A | X | After reloading the address of the old name will be undeployed and the new will be deployed. @@ -186,21 +216,27 @@ attribute `name` | N/A | X | After reloading the address of the old name will be #### `` -The `` element contains a list `` elements. Once changed, all `` elements in `` will be reloaded. +The `` element contains a list `` elements. Once changed, all +`` elements in `` will be reloaded. -> *Note:* -> Once reloaded, all new queues will be deployed to the running broker and all +> **Note:** +> +> Once reloaded, all new queues will be deployed to the running broker and all > queues that are missing from the configuration will be undeployed. -> *Note:* -> Parameters reloaded in this category will take effect immediately after reloading. -> The effect of deletion of Address's and Queue's, not auto created is controlled by -> parameter `config-delete-addresses` and `config-delete-queues` as described in this doc. -Below lists the effects of adding, deleting and updating of an element/attribute within the `` element, -and whether an change can be done or can’t be done. +> **Note:** +> +> Parameters reloaded in this category will take effect immediately after +> reloading. The effect of deletion of Address's and Queue's, not auto created +> is controlled by parameter `config-delete-addresses` and +> `config-delete-queues` as described in this doc. + +Below lists the effects of adding, deleting and updating of an +element/attribute within the `` element, and whether an change can be +done or can’t be done. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | X(no more than one is present) | Deleting it means delete (undeploy) all queues from running broker. | N/A `` | A new queue is deployed after reloading | The queue will be undeployed after reloading. | N/A attribute `name` | N/A | X | A queue with new name will be deployed and the queue with old name will be updeployed after reloading (see Note above). @@ -214,15 +250,17 @@ attribute `durable` | N/A | No effect unless starting broker | No effect unless #### `` -Changes to any `` elements will be reloaded to the running broker. +Changes to any `` elements will be reloaded to the running broker. -> *Note:* -> Once reloaded, new queues defined in the new changes will be deployed to the running -> broker. However existing queues won’t get undeployed even if the matching element is -> deleted/missing. Also new queue elements matching existing queues won’t get re-created – they remain unchanged. +> **Note:** +> +> Once reloaded, new queues defined in the new changes will be deployed to the +> running broker. However existing queues won’t get undeployed even if the +> matching element is deleted/missing. Also new queue elements matching +> existing queues won’t get re-created – they remain unchanged. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | A new jms queue will be deployed after reloading | No effect unless starting broker | No effect unless starting broker attribute `` | N/A | X | A jms queue of the new name will be deployed after reloading `` | X(no more than one is present) | No effect unless starting broker | No effect unless starting broker @@ -230,15 +268,16 @@ attribute `` | N/A | X | A jms queue of the new name will be deployed afte #### `` -Changes to any `` elements will be reloaded to the running broker. +Changes to any `` elements will be reloaded to the running broker. -> *Note:* -> Once reloaded, new topics defined in the new changes will be deployed to -> the running broker. However existing topics won’t get undeployed even if the +> **Note:** +> +> Once reloaded, new topics defined in the new changes will be deployed to the +> running broker. However existing topics won’t get undeployed even if the > matching element is deleted/missing. Also any `` elements matching > existing topics won’t get re-deployed – they remain unchanged. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | A new jms topic will be deployed after reloading | No effect unless starting broker | No effect unless starting broker attribute `name` | N/A | X | A jms topic of the new name will be deployed after reloading diff --git a/docs/user-manual/en/configuration-index.md b/docs/user-manual/en/configuration-index.md index 4b45de9d43e..0064a776387 100644 --- a/docs/user-manual/en/configuration-index.md +++ b/docs/user-manual/en/configuration-index.md @@ -1,51 +1,29 @@ -Configuration Reference -======================= +# Configuration Reference This section is a quick index for looking up configuration. Click on the element name to go to the specific chapter. -Server Configuration -==================== +## Broker Configuration -broker.xml --------------------------- +### broker.xml -This is the main core server configuration file which contains the 'core' -element. -The 'core' element contains the main server configuration. +This is the main core server configuration file which contains the `core` +element. The `core` element contains the main server configuration. -# System properties +#### Modularising broker.xml -It is possible to use System properties to replace some of the configuration properties. If you define a System property starting with "brokerconfig." that will be passed along to Bean Utils and the configuration would be replaced. - -To define global-max-size=1000000 using a system property you would have to define this property, for example through java arguments: - -``` -java -Dbrokerconfig.globalMaxSize=1000000 -``` - -You can also change the prefix through the broker.xml by setting: - -``` -yourprefix -``` - -This is to help you customize artemis on embedded systems. - -# Modularising config into separate files. - -XML XInclude support is provided in the configuration as such if you wish to break your configuration out into separate files you can. +XML XInclude support is provided in `broker.xml` so that you can break your configuration out into separate files. To do this ensure the following is defined at the root configuration element. ``` - xmlns:xi="http://www.w3.org/2001/XInclude" +xmlns:xi="http://www.w3.org/2001/XInclude" ``` You can now define include tag's where you want to bring in xml configuration from another file: ``` - + ``` You should ensure xml elements in separated files should be namespaced correctly for example if address-settings element was separated, it should have the element namespace defined: @@ -55,241 +33,338 @@ You should ensure xml elements in separated files should be namespaced correctly ``` An example can of this feature can be seen in the test suites: -``` - ./artemis-server/src/test/resources/ConfigurationTest-xinclude-config.xml ``` -N.B. if you use xmllint to validate xml's against schema you should enable xinclude flag when running. +./artemis-server/src/test/resources/ConfigurationTest-xinclude-config.xml +``` +**Note:** if you use `xmllint` to validate the XML against the schema you should enable xinclude flag when running. ``` - --xinclude +--xinclude ``` For further information on XInclude see: -[https://www.w3.org/TR/xinclude/](https://www.w3.org/TR/xinclude/) +[https://www.w3.org/TR/xinclude/](https://www.w3.org/TR/xinclude/) + +### System properties + +It is possible to use System properties to replace some of the configuration properties. If you define a System property starting with "brokerconfig." that will be passed along to Bean Utils and the configuration would be replaced. + +To define global-max-size=1000000 using a system property you would have to define this property, for example through java arguments: + +``` +java -Dbrokerconfig.globalMaxSize=1000000 +``` + +You can also change the prefix through the `broker.xml` by setting: + +``` +yourprefix +``` + +This is to help you customize artemis on embedded systems. -# The core configuration +## The core configuration This describes the root of the XML configuration. You will see here also multiple sub-types listed. For example on the main config you will have bridges and at the [list of bridge](#bridge-type) type we will describe the properties for that configuration. +Name | Description | Default +---|---|--- +[acceptors](configuring-transports.md#acceptors) | a list of remoting acceptors | n/a +[acceptors.acceptor](configuring-transports.md#acceptors) | Each acceptor is composed for just an URL | n/a +[addresses](address-model.md#basic-address-configuration) | [a list of addresses](#address-type) | n/a +[address-settings](address-model.md#configuring-addresses-and-queues-via-address-settings) | [a list of address-setting](#address-setting-type) | n/a +[allow-failback](ha.md#failing-back-to-live-server)| Should stop backup on live restart. | `true` +[amqp-use-core-subscription-naming](amqp.md) | If true uses CORE queue naming convention for AMQP. | `false` +[async-connection-execution-enabled](connection-ttl.md) | If False delivery would be always asynchronous. | `true` +[bindings-directory](persistence.md) | The folder in use for the bindings folder | `data/bindings` +[bridges](core-bridges.md) | [a list of core bridges](#bridge-type) | n/a +[ha-policy](ha.md) | the HA policy of this server | none +[broadcast-groups](clusters.md#broadcast-groups) | [a list of broadcast-group](#broadcast-group-type) | n/a +[broker-plugins](broker-plugins.md) | [a list of broker-plugins](#broker-plugin-type) | n/a +[configuration-file-refresh-period](config-reload.md) | The frequency in milliseconds the configuration file is checked for changes | 5000 +[check-for-live-server](ha.md#data-replication)| Used for a live server to verify if there are other nodes with the same ID on the topology | n/a +[cluster-connections](clusters.md#configuring-cluster-connections) | [a list of cluster-connection](#cluster-connection-type) | n/a +[cluster-password](clusters.md) |Cluster password. It applies to all cluster configurations. | n/a +[cluster-user](clusters.md) |Cluster username. It applies to all cluster configurations. | n/a +[connection-ttl-override](connection-ttl.md) |if set, this will override how long (in ms) to keep a connection alive without receiving a ping. -1 disables this setting. | -1 +[connection-ttl-check-interval](connection-ttl.md) |how often (in ms) to check connections for ttl violation. | 2000 +[connectors.connector](configuring-transports.md) | The URL for the connector. This is a list | n/a +[create-bindings-dir](persistence.md) | true means that the server will create the bindings directory on start up. | `true` +[create-journal-dir](persistence.md)| true means that the journal directory will be created. | `true` +[discovery-groups](clusters.md#discovery-groups)| [a list of discovery-group](#discovery-group-type) | n/a +[disk-scan-period](paging.md#max-disk-usage) | The interval where the disk is scanned for percentual usage. | 5000 +[diverts](diverts.md) | [a list of diverts to use](#divert-type) | n/a +[global-max-size](paging.md#global-max-size) | The amount in bytes before all addresses are considered full. | Half of the JVM's `-Xmx` +[graceful-shutdown-enabled](graceful-shutdown.md)| true means that graceful shutdown is enabled. | `false` +[graceful-shutdown-timeout](graceful-shutdown.md)| Timeout on waiting for clients to disconnect before server shutdown. | -1 +[grouping-handler](message-grouping.md) | [a message grouping handler](#grouping-handler-type) | n/a +[id-cache-size](duplicate-detection.md#configuring-the-duplicate-id-cache) | The duplicate detection circular cache size. | 20000 +[jmx-domain](management.md#configuring-jmx) | the JMX domain used to registered MBeans in the MBeanServer. | `org.apache.activemq` +[jmx-use-broker-name](management.md#configuring-jmx) | whether or not to use the broker name in the JMX properties. | `true` +[jmx-management-enabled](management.md#configuring-jmx) | true means that the management API is available via JMX. | `true` +[journal-buffer-size](persistence.md#configuring-the-message-journal) | The size of the internal buffer on the journal in KB. | 490KB +[journal-buffer-timeout](persistence.md#configuring-the-message-journal) | The Flush timeout for the journal buffer | 500000 for ASYNCIO; 3333333 for NIO +[journal-compact-min-files](persistence.md#configuring-the-message-journal) | The minimal number of data files before we can start compacting. Setting this to 0 means compacting is disabled. | 10 +[journal-compact-percentage](persistence.md#configuring-the-message-journal) | The percentage of live data on which we consider compacting the journal. | 30 +[journal-directory](persistence.md#configuring-the-message-journal) | the directory to store the journal files in. | `data/journal` +[journal-file-size](persistence.md#configuring-the-message-journal) | the size (in bytes) of each journal file. | 10MB +[journal-lock-acquisition-timeout](persistence.md#configuring-the-message-journal) | how long (in ms) to wait to acquire a file lock on the journal. | -1 +[journal-max-io](persistence.md#configuring-the-message-journal) | the maximum number of write requests that can be in the ASYNCIO queue at any one time. | 4096 for ASYNCIO; 1 for NIO; ignored for MAPPED +[journal-file-open-timeout](persistence.md#configuring-the-message-journal) | the length of time in seconds to wait when opening a new journal file before timing out and failing. | 5 +[journal-min-files](persistence.md#configuring-the-message-journal) | how many journal files to pre-create. | 2 +[journal-pool-files](persistence.md#configuring-the-message-journal) | The upper threshold of the journal file pool, -1 means no Limit. The system will create as many files as needed however when reclaiming files it will shrink back to the `journal-pool-files` | -1 +[journal-sync-non-transactional](persistence.md#configuring-the-message-journal) | if true wait for non transaction data to be synced to the journal before returning response to client. | `true` +[journal-sync-transactional](persistence.md#configuring-the-message-journal)| if true wait for transaction data to be synchronized to the journal before returning response to client. | `true` +[journal-type](persistence.md#configuring-the-message-journal) | the type of journal to use. | `ASYNCIO` +[journal-datasync](persistence.md#configuring-the-message-journal) | It will use msync/fsync on journal operations. | `true` +[large-messages-directory](large-messages.md) | the directory to store large messages. | `data/largemessages` +log-delegate-factory-class-name | **deprecated** the name of the factory class to use for log delegation. | n/a +[management-address](management.md#configuring-management)| the name of the management address to send management messages to. | `activemq.management` +[management-notification-address](management.md#configuring-the-management-notification-address) | the name of the address that consumers bind to receive management notifications. | `activemq.notifications` +[mask-password](masking-passwords.md) | This option controls whether passwords in server configuration need be masked. If set to "true" the passwords are masked. | `false` +[max-saved-replicated-journals-size](ha.md#data-replication) | This specifies how many times a replicated backup server can restart after moving its files on start. Once there are this number of backup journal files the server will stop permanently after if fails back. -1 Means no Limit; 0 don't keep a copy at all. | 2 +[max-disk-usage](paging.md#max-disk-usage) | The max percentage of data we should use from disks. The System will block while the disk is full. Disable by setting -1. | 100 +[memory-measure-interval](perf-tuning.md) | frequency to sample JVM memory in ms (or -1 to disable memory sampling). | -1 +[memory-warning-threshold](perf-tuning.md)| Percentage of available memory which will trigger a warning log. | 25 +[message-counter-enabled](management.md#message-counters) | true means that message counters are enabled. | `false` +[message-counter-max-day-history](management.md#message-counters)| how many days to keep message counter history. | 10 +[message-counter-sample-period](management.md#message-counters) | the sample period (in ms) to use for message counters. | 10000 +[message-expiry-scan-period](message-expiry.md#configuring-the-expiry-reaper-thread) | how often (in ms) to scan for expired messages. | 30000 +[message-expiry-thread-priority](message-expiry.md#configuring-the-expiry-reaper-thread)| the priority of the thread expiring messages. | 3 +name | node name; used in topology notifications if set. | n/a +[password-codec](masking-passwords.md) | the name of the class (and optional configuration properties) used to decode masked passwords. Only valid when `mask-password` is `true`. | n/a +[page-max-concurrent-io](paging.md) | The max number of concurrent reads allowed on paging. | 5 +[paging-directory](paging.md#configuration)| the directory to store paged messages in. | `data/paging` +[persist-delivery-count-before-delivery](undelivered-messages.md#delivery-count-persistence) | True means that the delivery count is persisted before delivery. False means that this only happens after a message has been cancelled. | `false` +[persistence-enabled](persistence.md#zero-persistence)| true means that the server will use the file based journal for persistence. | `true` +[persist-id-cache](duplicate-detection.md#configuring-the-duplicate-id-cache) | true means that ID's are persisted to the journal. | `true` +queues | **deprecated** [use addresses](#address-type) | n/a +[remoting-incoming-interceptors](intercepting-operations.md)| a list of <class-name/> elements with the names of classes to use for intercepting incoming remoting packets | n/a +[remoting-outgoing-interceptors](intercepting-operations.md)| a list of <class-name/> elements with the names of classes to use for intercepting outgoing remoting packets | n/a +[resolveProtocols]() | Use [ServiceLoader](https://docs.oracle.com/javase/tutorial/ext/basics/spi.html) to load protocol modules. | `true` +[resource-limit-settings](resource-limits.md) | [a list of resource-limits](#resource-limit-type) | n/a +[scheduled-thread-pool-max-size](thread-pooling.md#server-scheduled-thread-pool)| Maximum number of threads to use for the scheduled thread pool. | 5 +[security-enabled](security.md) | true means that security is enabled. | `true` +[security-invalidation-interval](security.md) | how long (in ms) to wait before invalidating the security cache. | 10000 +system-property-prefix | Prefix for replacing configuration settings using Bean Utils. | n/a +internal-naming-prefix | the prefix used when naming the internal queues and addresses required for implementing certain behaviours. | `$.activemq.internal` +[populate-validated-user](security.md#tracking-the-validated-user)| whether or not to add the name of the validated user to the messages that user sends. | `false` +[security-settings](security.md#role-based-security-for-addresses) | [a list of security-setting](#security-setting-type). | n/a +[thread-pool-max-size](thread-pooling.md#thread-management) | Maximum number of threads to use for the thread pool. -1 means 'no limits'. | 30 +[transaction-timeout](transaction-config.md) | how long (in ms) before a transaction can be removed from the resource manager after create time. | 300000 +[transaction-timeout-scan-period](transaction-config.md) | how often (in ms) to scan for timeout transactions. | 1000 +[wild-card-routing-enabled](wildcard-routing.md) | true means that the server supports wild card routing. | `true` +[network-check-NIC](network-isolation.md) | the NIC (Network Interface Controller) to be used on InetAddress.isReachable. | n/a +[network-check-URL-list](network-isolation.md) | the list of http URIs to be used to validate the network. | n/a +[network-check-list](network-isolation.md) | the list of pings to be used on ping or InetAddress.isReachable. | n/a +[network-check-period](network-isolation.md) | a frequency in milliseconds to how often we should check if the network is still up. | 10000 +[network-check-timeout](network-isolation.md) | a timeout used in milliseconds to be used on the ping. | 1000 +[network-check-ping-command](network-isolation.md) | the command used to oping IPV4 addresses. | n/a +[network-check-ping6-command](network-isolation.md) | the command used to oping IPV6 addresses. | n/a +[critical-analyzer](critical-analysis.md) | enable or disable the critical analysis. | `true` +[critical-analyzer-timeout](critical-analysis.md) | timeout used to do the critical analysis. | 120000 ms +[critical-analyzer-check-period](critical-analysis.md) | time used to check the response times. | 0.5 \* `critical-analyzer-timeout` +[critical-analyzer-policy](critical-analysis.md) | should the server log, be halted or shutdown upon failures. | `LOG` +resolve-protocols | if true then the broker will make use of any protocol managers that are in available on the classpath, otherwise only the core protocol will be available, unless in embedded mode where users can inject their own protocol managers. | `true` +[resource-limit-settings](resource-limits.md) | [a list of resource-limit](#resource-limit-type). | n/a +server-dump-interval | interval to log server specific information (e.g. memory usage etc). | -1 +store | the store type used by the server. | n/a +[wildcard-addresses](wildcard-syntax.md) | parameters to configure wildcard address matching format. | n/a + +## address-setting type + +Name | Description | Default +---|---|--- +[match](address-model.md) | The filter to apply to the setting | n/a +[dead-letter-address](undelivered-messages.md) | Dead letter address | n/a +[expiry-address](message-expiry.md) | Expired messages address | n/a +[expiry-delay](address-model.md) | Expiration time override; -1 don't override | -1 +[redelivery-delay](undelivered-messages.md) | Time to wait before redelivering a message | 0 +[redelivery-delay-multiplier](address-model.md) | Multiplier to apply to the `redelivery-delay` | 1.0 +[max-redelivery-delay](address-model.md) | Max value for the `redelivery-delay` | 10 \* `redelivery-delay` +[max-delivery-attempts](undelivered-messages.md)| Number of retries before dead letter address| 10 +[max-size-bytes](paging.md)| Max size a queue can be before invoking `address-full-policy` | -1 +[max-size-bytes-reject-threshold]() | Used with `BLOCK`, the max size an address can reach before messages are rejected; works in combination with `max-size-bytes` **for AMQP clients only**. | -1 +[page-size-bytes](paging.md) | Size of each file on page | 10485760 +[page-max-cache-size](paging.md) | Maximum number of files cached from paging | 5 +[address-full-policy](address-model.md)| What to do when a queue reaches `max-size-bytes` | `PAGE` +[message-counter-history-day-limit](address-model.md) | Days to keep message counter data | 0 +[last-value-queue](last-value-queues.md) | **deprecated** Queue is a last value queue; see `default-last-value-queue` instead | `false` +[default-last-value-queue](last-value-queues.md)| `last-value` value if none is set on the queue | `false` +[default-exclusive-queue](exclusive-queues.md) | `exclusive` value if none is set on the queue | `false` +[redistribution-delay](clusters.md) | Timeout before redistributing values after no consumers | -1 +[send-to-dla-on-no-route](address-model.md) | Forward messages to DLA when no queues subscribing | `false` +[slow-consumer-threshold](slow-consumers.md) | Min rate of msgs/sec consumed before a consumer is considered "slow" | -1 +[slow-consumer-policy](slow-consumers.md) | What to do when "slow" consumer is detected | `NOTIFY` +[slow-consumer-check-period](slow-consumers.md) | How often to check for "slow" consumers | 5 +[auto-create-jms-queues](address-model.md#configuring-addresses-and-queues-via-address-settings)| **deprecated** Create JMS queues automatically; see `auto-create-queues` & `auto-create-addresses` | `true` +[auto-delete-jms-queues](address-model.md#configuring-addresses-and-queues-via-address-settings)| **deprecated** Delete JMS queues automatically; see `auto-create-queues` & `auto-create-addresses` | `true` +[auto-create-jms-topics](address-model.md#configuring-addresses-and-queues-via-address-settings)| **deprecated** Create JMS topics automatically; see `auto-create-queues` & `auto-create-addresses` | `true` +[auto-delete-jms-topics](address-model.md#configuring-addresses-and-queues-via-address-settings)| **deprecated** Delete JMS topics automatically; see `auto-create-queues` & `auto-create-addresses` | `true` +[auto-create-queues](address-model.md#configuring-addresses-and-queues-via-address-settings) | Create queues automatically | `true` +[auto-delete-queues](address-model.md#configuring-addresses-and-queues-via-address-settings) | Delete queues automatically | `true` +[config-delete-queues](config-reload.md)| How to deal with queues deleted from XML at runtime| `OFF` +[auto-create-addresses](address-model.md#configuring-addresses-and-queues-via-address-settings) | Create addresses automatically | `true` +[auto-delete-addresses](address-model.md#configuring-addresses-and-queues-via-address-settings) | Delete addresses automatically | `true` +[config-delete-addresses](config-reload.md) | How to deal with addresses deleted from XML at runtime | `OFF` +[management-browse-page-size]() | Number of messages a management resource can browse| 200 +[default-purge-on-no-consumers](address-model.md#non-durable-subscription-queue) | `purge-on-no-consumers` value if none is set on the queue | `false` +[default-max-consumers](address-model.md#shared-durable-subscription-queue-using-max-consumers) | `max-consumers` value if none is set on the queue | -1 +[default-queue-routing-type](address-model.md#routing-type) | Routing type for auto-created queues if the type can't be otherwise determined | `MULTICAST` +[default-address-routing-type](address-model.md#routing-type) | Routing type for auto-created addresses if the type can't be otherwise determined | `MULTICAST` + + +## bridge type + +Name | Description | Default +---|---|--- +[name ](core-bridges.md)| unique name | n/a +[queue-name](core-bridges.md) | name of queue that this bridge consumes from | n/a +[forwarding-address](core-bridges.md) | address to forward to. If omitted original address is used | n/a +[ha](core-bridges.md)| whether this bridge supports fail-over | `false` +[filter](core-bridges.md) | optional core filter expression | n/a +[transformer-class-name](core-bridges.md) | optional name of transformer class | n/a +[min-large-message-size](core-bridges.md) | Limit before message is considered large. | 100KB +[check-period](connection-ttl.md)| How often to check for [TTL](https://en.wikipedia.org/wiki/Time_to_live) violation. -1 means disabled. | 30000 +[connection-ttl](connection-ttl.md) | [TTL](https://en.wikipedia.org/wiki/Time_to_live "Time to Live") for the Bridge. This should be greater than the ping period. | 60000 +[retry-interval](core-bridges.md)| period (in ms) between successive retries. | 2000 +[retry-interval-multiplier](core-bridges.md) | multiplier to apply to successive retry intervals. | 1 +[max-retry-interval](core-bridges.md) | Limit to the retry-interval growth. | 2000 +[reconnect-attempts](core-bridges.md) | maximum number of retry attempts.| -1 (no limit) +[use-duplicate-detection](core-bridges.md)| forward duplicate detection headers? | `true` +[confirmation-window-size](core-bridges.md) | number of bytes before confirmations are sent. | 1MB +[producer-window-size](core-bridges.md)| Producer flow control size on the bridge. | -1 (disabled) +[user](core-bridges.md) | Username for the bridge, the default is the cluster username. | n/a +[password](core-bridges.md)| Password for the bridge, default is the cluster password. | n/a +[reconnect-attempts-same-node](core-bridges.md) | Number of retries before trying another node. | 10 + +## broadcast-group type + +Name | Type +---|--- +[name ](clusters.md) | unique name +[local-bind-address](clusters.md) | Local bind address that the datagram socket is bound to. +[local-bind-port](clusters.md) | Local port to which the datagram socket is bound to. +[group-address](clusters.md)| Multicast address to which the data will be broadcast. +[group-port](clusters.md)| UDP port number used for broadcasting. +[broadcast-period](clusters.md)| Period in milliseconds between consecutive broadcasts. Default=2000. +[jgroups-file](clusters.md) | Name of JGroups configuration file. +[jgroups-channel](clusters.md) | Name of JGroups Channel. +[connector-ref](clusters.md)| The `connector` to broadcast. + + +## cluster-connection type + +Name | Description | Default +---|---|--- +[name](clusters.md) | unique name | n/a +[address](clusters.md) | name of the address this cluster connection applies to | n/a +[connector-ref](clusters.md) | Name of the connector reference to use. | n/a +[check-period](connection-ttl.md) | The period (in milliseconds) used to check if the cluster connection has failed to receive pings from another server | 30000 +[connection-ttl](connection-ttl.md)| Timeout for TTL. | 60000 +[min-large-message-size](large-messages.md) | Messages larger than this are considered large-messages. | 100KB +[call-timeout](clusters.md) | Time(ms) before giving up on blocked calls. | 30000 +[retry-interval](clusters.md)| period (in ms) between successive retries. | 500 +[retry-interval-multiplier](clusters.md) | multiplier to apply to the retry-interval. | 1 +[max-retry-interval](clusters.md) | Maximum value for retry-interval. | 2000 +[reconnect-attempts](clusters.md) | How many attempts should be made to reconnect after failure. | -1 +[use-duplicate-detection](clusters.md)| should duplicate detection headers be inserted in forwarded messages? | `true` +[message-load-balancing](clusters.md) | how should messages be load balanced? | `OFF` +[max-hops](clusters.md)| maximum number of hops cluster topology is propagated. | 1 +[confirmation-window-size](client-reconnection.md#client-reconnection-and-session-reattachment)| The size (in bytes) of the window used for confirming data from the server connected to. | 1048576 +[producer-window-size](clusters.md)| Flow Control for the Cluster connection bridge. | -1 (disabled) +[call-failover-timeout](clusters.md#configuring-cluster-connections)| How long to wait for a reply if in the middle of a fail-over. -1 means wait forever. | -1 +[notification-interval](clusters.md) | how often the cluster connection will notify the cluster of its existence right after joining the cluster. | 1000 +[notification-attempts](clusters.md) | how many times this cluster connection will notify the cluster of its existence right after joining the cluster | 2 + + +## discovery-group type + Name | Description -:--- | :--- -[acceptors](configuring-transports.md "Understanding Acceptors") | a list of remoting acceptors -[acceptors.acceptor](configuring-transports.md "Understanding Acceptors") | Each acceptor is composed for just an URL -[address-settings](address-model.md "Configuring Addresses and Queues Via Address Settings") | [a list of address-setting](#address-setting-type) -[allow-failback](ha.md "Failing Back to live Server") | Should stop backup on live restart. default true -[amqp-use-core-subscription-naming](using-AMQP.md "Message Conversions") | If true uses CORE queue naming convention for AMQP. default false -[async-connection-execution-enabled](connection-ttl.md "Configuring Asynchronous Connection Execution") | If False delivery would be always asynchronous. default true -[bindings-directory](persistence.md "Configuring the bindings journal") | The folder in use for the bindings folder -[bridges](core-bridges.md "Core Bridges") | [a list of bridge](#bridge-type) -[broadcast-groups](clusters.md "Clusters") | [a list of broadcast-group](#broadcast-group-type) -[configuration-file-refresh-period](config-reload.md) | The frequency in milliseconds the configuration file is checked for changes (default 5000) -[check-for-live-server](ha.md) | Used for a live server to verify if there are other nodes with the same ID on the topology -[cluster-connections](clusters.md "Clusters") | [a list of cluster-connection](#cluster-connection-type) -[cluster-password](clusters.md "Clusters") | Cluster password. It applies to all cluster configurations. -[cluster-user](clusters.md "Clusters") | Cluster username. It applies to all cluster configurations. -[connection-ttl-override](connection-ttl.md) | if set, this will override how long (in ms) to keep a connection alive without receiving a ping. -1 disables this setting. Default -1 -[connection-ttl-check-period](connection-ttl.md) | how often (in ms) to check connections for ttl violation. Default 2000 -[connectors.connector](configuring-transports.md "Understanding Connectors") | The URL for the connector. This is a list -[create-bindings-dir](persistence.md "Configuring the bindings journal") | true means that the server will create the bindings directory on start up. Default=true -[create-journal-dir](persistence.md) | true means that the journal directory will be created. Default=true -[discovery-groups](clusters.md "Clusters") | [a list of discovery-group](#discovery-group-type) -[disk-scan-period](paging.md#max-disk-usage) | The interval where the disk is scanned for percentual usage. Default=5000 ms. -[diverts](diverts.md "Diverting and Splitting Message Flows") | [a list of diverts to use](#divert-type) -[global-max-size](paging.md#global-max-size) | The amount in bytes before all addresses are considered full. Default is half of the memory used by the JVM (-Xmx argument). -[graceful-shutdown-enabled](graceful-shutdown.md "Graceful Server Shutdown") | true means that graceful shutdown is enabled. Default=false -[graceful-shutdown-timeout](graceful-shutdown.md "Graceful Server Shutdown") | Timeout on waiting for clients to disconnect before server shutdown. Default=-1 -[grouping-handler](message-grouping.md "Message Grouping") | Message Group configuration -[id-cache-size](duplicate-detection.md "Configuring the Duplicate ID Cache") | The duplicate detection circular cache size. Default=20000 -[jmx-domain](management.md "Configuring JMX") | the JMX domain used to registered MBeans in the MBeanServer. Default=org.apache.activemq -[jmx-management-enabled](management.md "Configuring JMX") | true means that the management API is available via JMX. Default=true -[journal-buffer-size](persistence.md) | The size of the internal buffer on the journal in KB. Default=490 KiB -[journal-buffer-timeout](persistence.md) | The Flush timeout for the journal buffer -[journal-compact-min-files](persistence.md) | The minimal number of data files before we can start compacting. Setting this to 0 means compacting is disabled. Default=10 -[journal-compact-percentage](persistence.md) | The percentage of live data on which we consider compacting the journal. Default=30 -[journal-directory](persistence.md) | the directory to store the journal files in. Default=data/journal -[journal-file-size](persistence.md) | the size (in bytes) of each journal file. Default=10485760 (10 MB) -[journal-max-io](persistence.md#configuring-the-message-journal) | the maximum number of write requests that can be in the AIO queue at any one time. Default is 4096 for AIO and 1 for NIO, ignored for MAPPED. -[journal-min-files](persistence.md#configuring-the-message-journal) | how many journal files to pre-create. Default=2 -[journal-pool-files](persistence.md#configuring-the-message-journal) | The upper threshold of the journal file pool,-1 (default) means no Limit. The system will create as many files as needed however when reclaiming files it will shrink back to the `journal-pool-files` -[journal-sync-non-transactional](persistence.md) | if true wait for non transaction data to be synced to the journal before returning response to client. Default=true -[journal-sync-transactional](persistence.md) | if true wait for transaction data to be synchronized to the journal before returning response to client. Default=true -[journal-type](persistence.md) | the type of journal to use. Default=ASYNCIO -[journal-datasync](persistence.md) | It will use msync/fsync on journal operations. Default=true. -[large-messages-directory](large-messages.md "Configuring the server") | the directory to store large messages. Default=data/largemessages -[management-address](management.md "Configuring Core Management") | the name of the management address to send management messages to. Default=activemq.management -[management-notification-address](management.md "Configuring The Core Management Notification Address") | the name of the address that consumers bind to receive management notifications. Default=activemq.notifications -[mask-password](masking-passwords.md "Masking Passwords") | This option controls whether passwords in server configuration need be masked. If set to "true" the passwords are masked. Default=false -[max-saved-replicated-journals-size](ha.md#data-replication) | This specifies how many times a replicated backup server can restart after moving its files on start. Once there are this number of backup journal files the server will stop permanently after if fails back. -1 Means no Limit, 0 don't keep a copy at all, Default=2 -[max-disk-usage](paging.md#max-disk-usage) | The max percentage of data we should use from disks. The System will block while the disk is full. Disable by setting -1. Default=100 -[memory-measure-interval](perf-tuning.md) | frequency to sample JVM memory in ms (or -1 to disable memory sampling). Default=-1 -[memory-warning-threshold](perf-tuning.md) | Percentage of available memory which will trigger a warning log. Default=25 -[message-counter-enabled](management.md "Configuring Message Counters") | true means that message counters are enabled. Default=false -[message-counter-max-day-history](management.md "Configuring Message Counters") | how many days to keep message counter history. Default=10 (days) -[message-counter-sample-period](management.md "Configuring Message Counters") | the sample period (in ms) to use for message counters. Default=10000 -[message-expiry-scan-period](message-expiry.md "Configuring The Expiry Reaper Thread") | how often (in ms) to scan for expired messages. Default=30000 -[message-expiry-thread-priority](message-expiry.md "Configuring The Expiry Reaper Thread") | the priority of the thread expiring messages. Default=3 -[password-codec](masking-passwords.md "Masking Passwords") | the name of the class (and optional configuration properties) used to decode masked passwords. Only valid when `mask-password` is `true`. Default=empty -[page-max-concurrent-io](paging.md "Paging Mode") | The max number of concurrent reads allowed on paging. Default=5 -[paging-directory](paging.md "Configuration") | the directory to store paged messages in. Default=data/paging -[persist-delivery-count-before-delivery](undelivered-messages.md "Delivery Count Persistence") | True means that the delivery count is persisted before delivery. False means that this only happens after a message has been cancelled. Default=false -[persistence-enabled](persistence.md "Configuring ActiveMQ Artemis for Zero Persistence") | true means that the server will use the file based journal for persistence. Default=true -[persist-id-cache](duplicate-detection.md "Configuring the Duplicate ID Cache") | true means that ID's are persisted to the journal. Default=true -[queues](address-model.md "Predefined Queues") | [a list of queue to be created](#queue-type) -[remoting-incoming-interceptors](intercepting-operations.md "Intercepting Operations") | A list of interceptor -[resolveProtocols]() | Use [ServiceLoader](https://docs.oracle.com/javase/tutorial/ext/basics/spi.html) to load protocol modules. Default=true -[scheduled-thread-pool-max-size](thread-pooling.md#server-scheduled-thread-pool "Server Scheduled Thread Pool")| Maximum number of threads to use for the scheduled thread pool. Default=5 -[security-enabled](security.md "Security") | true means that security is enabled. Default=true -[security-invalidation-interval](security.md "Security") | how long (in ms) to wait before invalidating the security cache. Default=10000 -system-property-prefix | Prefix for replacing configuration settings using Bean Utils. -[populate-validated-user](security.md "Security") | whether or not to add the name of the validated user to the messages that user sends. Default=false -[security-settings](security.md "Role based security for addresses") | [a list of security-setting](#security-setting-type) -[thread-pool-max-size](thread-pooling.md "Server Scheduled Thread Pool") | Maximum number of threads to use for the thread pool. -1 means 'no limits'.. Default=30 -[transaction-timeout](transaction-config.md "Resource Manager Configuration") | how long (in ms) before a transaction can be removed from the resource manager after create time. Default=300000 -[transaction-timeout-scan-period](transaction-config.md "Resource Manager Configuration") | how often (in ms) to scan for timeout transactions. Default=1000 -[wild-card-routing-enabled](wildcard-routing.md "Routing Messages With Wild Cards") | true means that the server supports wild card routing. Default=true -[network-check-NIC](network-isolation.md) | The NIC (Network Interface Controller) to be used on InetAddress.isReachable -[network-check-URL](network-isolation.md) | The list of http URIs to be used to validate the network -[network-check-list](network-isolation.md) | The list of pings to be used on ping or InetAddress.isReachable -[network-check-ping-command](network-isolation.md) | The command used to oping IPV4 addresses -[network-check-ping6-command](network-isolation.md) | The command used to oping IPV6 addresses -[critical-analyzer](critical-analysis.md) | Enable or disable the critical analysis (default true) -[critical-analyzer-timeout](critical-analysis.md) | Timeout used to do the critical analysis (default 120000 milliseconds) -[critical-analyzer-check-period](critical-analysis.md) | Time used to check the response times (default half of critical-analyzer-timeout) -[critical-analyzer-policy](critical-analysis.md) | Should the server log, be halted or shutdown upon failures (default `LOG`) - - -#address-setting type +---|--- +[name](clusters.md)| unique name +[group-address](clusters.md)| Multicast IP address of the group to listen on +[group-port](clusters.md)| UDP port number of the multi cast group +[jgroups-file](clusters.md) | Name of a JGroups configuration file. If specified, the server uses JGroups for discovery. +[jgroups-channel](clusters.md) | Name of a JGroups Channel. If specified, the server uses the named channel for discovery. +[refresh-timeout]()| Period the discovery group waits after receiving the last broadcast from a particular server before removing that servers connector pair entry from its list. Default=10000 +[local-bind-address](clusters.md) | local bind address that the datagram socket is bound to +[local-bind-port](clusters.md) | local port to which the datagram socket is bound to. Default=-1 +initial-wait-timeout | time to wait for an initial broadcast to give us at least one node in the cluster. Default=10000 + +## divert type Name | Description -:--- | :--- -[match ](address-model.md "Configuring Queues Via Address Settings") | The filter to apply to the setting -[dead-letter-address](undelivered-messages.md "Configuring Dead Letter Addresses") | dead letter address -[expiry-address](message-expiry.md "Configuring Expiry Addresses") | expired messages address -[expiry-delay](address-model.md "Configuring Queues Via Address Settings") | expiration time override, -1 don't override with default=-1 -[redelivery-delay](undelivered-messages.md "Configuring Delayed Redelivery") | time to redeliver a message (in ms) with default=0 -[redelivery-delay-multiplier](address-model.md "Configuring Queues Via Address Settings") | multiplier to apply to the "redelivery-delay" -[max-redelivery-delay](address-model.md "Configuring Queues Via Address Settings") | Max value for the redelivery-delay -[max-delivery-attempts](undelivered-messages.md "Configuring Dead Letter Addresses") | Number of retries before dead letter address, default=10 -[max-size-bytes](paging.md "Paging") | Limit before paging. -1 = infinite -[page-size-bytes](paging.md "Paging") | Size of each file on page, default=10485760 -[page-max-cache-size](paging.md "Paging") | Maximum number of files cached from paging default=5 -[address-full-policy](address-model.md "Configuring Queues Via Address Settings") | Model to chose after queue full -[message-counter-history-day-limit](address-model.md "Configuring Queues Via Address Settings") | Days to keep in history -[last-value-queue](last-value-queues.md "Last-Value Queues") | Queue is a last value queue, default=false -[redistribution-delay](clusters.md "Clusters") | Timeout before redistributing values after no consumers. default=-1 -[send-to-dla-on-no-route](address-model.md "Configuring Queues Via Address Settings") | Forward messages to DLA when no queues subscribing. default=false - - -#bridge type +---|--- +[name](diverts.md) | unique name +[transformer-class-name](diverts.md) | an optional class name of a transformer +[exclusive](diverts.md) | whether this is an exclusive divert. Default=false +[routing-name](diverts.md) | the routing name for the divert +[address](diverts.md) | the address this divert will divert from +[forwarding-address](diverts.md) | the forwarding address for the divert +[filter](diverts.md) | optional core filter expression + + +## address type Name | Description -:--- | :--- -[name ](core-bridges.md "Core Bridges") | unique name -[queue-name](core-bridges.md "Core Bridges") | name of queue that this bridge consumes from -[forwarding-address](core-bridges.md "Core Bridges") | address to forward to. If omitted original address is used -[ha](core-bridges.md "Core Bridges") | whether this bridge supports fail-over -[filter](core-bridges.md "Core Bridges") | optional core filter expression -[transformer-class-name](core-bridges.md "Core Bridges") | optional name of transformer class -[min-large-message-size](core-bridges.md "Core Bridges") | Limit before message is considered large. default 100KB -[check-period](connection-ttl.md "Detecting Dead Connections") | [TTL](https://en.wikipedia.org/wiki/Time_to_live "Time to Live") check period for the bridge. -1 means disabled. default 30000 (ms) -[connection-ttl](connection-ttl.md "Detecting Dead Connections") | [TTL](https://en.wikipedia.org/wiki/Time_to_live "Time to Live") for the Bridge. This should be greater than the ping period. default 60000 (ms) -[retry-interval](core-bridges.md "Core Bridges") | period (in ms) between successive retries. default 2000 -[retry-interval-multiplier](core-bridges.md "Core Bridges") | multiplier to apply to successive retry intervals. default 1 -[max-retry-interval](core-bridges.md "Core Bridges") | Limit to the retry-interval growth. default 2000 -[reconnect-attempts](core-bridges.md "Core Bridges") | maximum number of retry attempts, -1 means 'no limits'. default -1 -[use-duplicate-detection](core-bridges.md "Core Bridges") | forward duplicate detection headers?. default true -[confirmation-window-size](core-bridges.md "Core Bridges") | number of bytes before confirmations are sent. default 1MB -[producer-window-size](core-bridges.md "Core Bridges") | Producer flow control size on the bridge. Default -1 (disabled) -[user](core-bridges.md "Core Bridges") | Username for the bridge, the default is the cluster username -[password](core-bridges.md "Core Bridges") | Password for the bridge, default is the cluster password -[reconnect-attempts-same-node](core-bridges.md "Core Bridges") | Number of retries before trying another node. default 10 - -# broadcast-group type +---|--- +name | unique name | n/a +[anycast](address-model.md)| list of anycast [queues](#queue-type) +[multicast](address-model.md) | list of multicast [queues](#queue-type) -Name | Type -:--- | :--- -[name ](clusters.md "Clusters") | unique name -[local-bind-address](clusters.md "Clusters") | local bind address that the datagram socket is bound to -[local-bind-port](clusters.md "Clusters") | local port to which the datagram socket is bound to -[group-address](clusters.md "Clusters") | multicast address to which the data will be broadcast -[group-port](clusters.md "Clusters") | UDP port number used for broadcasting -[broadcast-period](clusters.md "Clusters") | period in milliseconds between consecutive broadcasts. default 2000 -[jgroups-file](clusters.md) | Name of JGroups configuration file -[jgroups-channel](clusters.md) | Name of JGroups Channel -[connector-ref](clusters.md "Clusters") | +## queue type -#cluster-connection type +Name | Description | Default +---|---|--- +name | unique name | n/a +filter | optional core filter expression | n/a +durable | whether the queue is durable (persistent). | `true` +user | the name of the user to associate with the creation of the queue | n/a +[max-consumers](address-model.md#shared-durable-subscription-queue-using-max-consumers) | the max number of consumers allowed on this queue | -1 (no max) +[purge-on-no-consumers](address-model.md#non-durable-subscription-queue) | whether or not to delete all messages and prevent routing when no consumers are connected | `false` +[exclusive](exclusive-queues.md) | only deliver messages to one of the connected consumers | `false` +[last-value](last-value-queues.md) | use last-value semantics | `false` -Name | Description -:--- | :--- -[name](clusters.md "Clusters") | unique name -[address](clusters.md "Clusters") | name of the address this cluster connection applies to -[connector-ref](clusters.md "Clusters") | Name of the connector reference to use. -[check-period](connection-ttl.md "Detecting Dead Connections") | The period (in milliseconds) used to check if the cluster connection has failed to receive pings from another server with default = 30000 -[connection-ttl](connection-ttl.md "Detecting Dead Connections") | Timeout for TTL. Default 60000 -[min-large-message-size](large-messages.md "Large Messages") | Messages larger than this are considered large-messages, default=100KB -[call-timeout](clusters.md "Clusters") | Time(ms) before giving up on blocked calls. Default=30000 -[retry-interval](clusters.md "Clusters") | period (in ms) between successive retries. Default=500 -[retry-interval-multiplier](clusters.md "Clusters") | multiplier to apply to the retry-interval. Default=1 -[max-retry-interval](clusters.md "Clusters") | Maximum value for retry-interval. Default=2000 -[reconnect-attempts](clusters.md "Clusters") | How many attempts should be made to reconnect after failure. Default=-1 -[use-duplicate-detection](clusters.md "Clusters") | should duplicate detection headers be inserted in forwarded messages?. Default=true -[message-load-balancing](clusters.md "Clusters") | how should messages be load balanced? Default=OFF -[max-hops](clusters.md "Clusters") | maximum number of hops cluster topology is propagated. Default=1 -[confirmation-window-size](client-reconnection.md "Client Reconnection and Session Reattachment")| The size (in bytes) of the window used for confirming data from the server connected to. Default 1048576 -[producer-window-size](clusters.md "Clusters") | Flow Control for the Cluster connection bridge. Default -1 (disabled) -[call-failover-timeout](clusters.md "Configuring Cluster Connections") | How long to wait for a reply if in the middle of a fail-over. -1 means wait forever. Default -1 -[notification-interval](clusters.md "Clusters") | how often the cluster connection will notify the cluster of its existence right after joining the cluster. Default 1000 -[notification-attempts](clusters.md "Clusters") | how many times this cluster connection will notify the cluster of its existence right after joining the cluster Default 2 - - -#discovery-group type -Name | Description -:--- | :--- -[name](clusters.md "Clusters") | unique name -[group-address](clusters.md "Clusters") | Multicast IP address of the group to listen on -[group-port](clusters.md "Clusters") | UDP port number of the multi cast group -[jgroups-file](clusters.md) | Name of a JGroups configuration file. If specified, the server uses JGroups for discovery. -[jgroups-channel](clusters.md) | Name of a JGroups Channel. If specified, the server uses the named channel for discovery. -[refresh-timeout]() | Period the discovery group waits after receiving the last broadcast from a particular server before removing that servers connector pair entry from its list. Default=10000 -[local-bind-address](clusters.md "Clusters") | local bind address that the datagram socket is bound to -[local-bind-port](clusters.md "Clusters") | local port to which the datagram socket is bound to. Default=-1 -[initial-wait-timeout]() | time to wait for an initial broadcast to give us at least one node in the cluster. Default=10000 - -#divert type +## security-setting type Name | Description -:--- | :--- -[name](diverts.md "Diverting and Splitting Message Flows") | unique name -[transformer-class-name](diverts.md "Diverting and Splitting Message Flows") | an optional class name of a transformer -[exclusive](diverts.md "Diverting and Splitting Message Flows") | whether this is an exclusive divert. Default=false -[routing-name](diverts.md "Diverting and Splitting Message Flows") | the routing name for the divert -[address](diverts.md "Diverting and Splitting Message Flows") | the address this divert will divert from -[forwarding-address](diverts.md "Diverting and Splitting Message Flows") | the forwarding address for the divert -[filter](diverts.md "Diverting and Splitting Message Flows")| optional core filter expression +---|--- +[match](security.md)| [address expression](wildcard-syntax.md) +[permission](security.md) | +[permission.type](security.md) | the type of permission +[permission.roles](security.md) | a comma-separated list of roles to apply the permission to -#queue type +## broker-plugin type Name | Description -:--- | :--- -[name ](address-model.md "Predefined Queues") | unique name -[address](address-model.md "Predefined Queues") | address for the queue -[filter](address-model.md "Predefined Queues") | optional core filter expression -[durable](address-model.md "Predefined Queues") | whether the queue is durable (persistent). Default=true +---|--- +[property](broker-plugins.md#registering-a-plugin)| properties to configure a plugin +[class-name](broker-plugins.md#registering-a-plugin) | the name of the broker plugin class to instantiate -#security-setting type +## resource-limit type -Name | Description -:--- | :--- -[match ](security.md "Role based security for addresses") | [address expression](wildcard-syntax.md) -[permission](security.md "Role based security for addresses") | -[permission.type ](security.md "Role based security for addresses") | the type of permission -[permission.roles ](security.md "Role based security for addresses") | a comma-separated list of roles to apply the permission to +Name | Description | Default +---|---|--- +[match](resource-limits.md#configuring-limits-via-resource-limit-settings)| the name of the user to whom the limits should be applied | n/a +[max-connections](resource-limits.md#configuring-limits-via-resource-limit-settings)| how many connections are allowed by the matched user | -1 (no max) +[max-queues](resource-limits.md#configuring-limits-via-resource-limit-settings)| how many queues can be created by the matched user | -1 (no max) + + +## grouping-handler type +Name | Description | Default +---|---|--- +[name](message-grouping.md#clustered-grouping) | A unique name | n/a +[type](message-grouping.md#clustered-grouping) | `LOCAL` or `REMOTE` | n/a +[address](message-grouping.md#clustered-grouping) | A reference to a `cluster-connection` `address` | n/a +[timeout](message-grouping.md#clustered-grouping) | How long to wait for a decision | 5000 +[group-timeout](message-grouping.md#clustered-grouping) | How long a group binding will be used. | -1 (disabled) +[reaper-period](message-grouping.md#clustered-grouping) | How often the reaper will be run to check for timed out group bindings. Only valid for `LOCAL` handlers. | 30000 \ No newline at end of file diff --git a/docs/user-manual/en/configuring-transports.md b/docs/user-manual/en/configuring-transports.md index 47dfc7b6322..ee3a0471c6a 100644 --- a/docs/user-manual/en/configuring-transports.md +++ b/docs/user-manual/en/configuring-transports.md @@ -1,43 +1,39 @@ # Configuring the Transport -In this chapter we'll describe the concepts required for understanding -Apache ActiveMQ Artemis transports and where and how they're configured. +In this chapter we'll describe the concepts required for understanding Apache +ActiveMQ Artemis transports and where and how they're configured. ## Acceptors One of the most important concepts in Apache ActiveMQ Artemis transports is the -*acceptor*. Let's dive straight in and take a look at an acceptor -defined in xml in the configuration file `broker.xml`. +*acceptor*. Let's dive straight in and take a look at an acceptor defined in +xml in the configuration file `broker.xml`. ```xml - - tcp://localhost:61617 - +tcp://localhost:61617 ``` -Acceptors are always defined inside an `acceptors` element. There can be -one or more acceptors defined in the `acceptors` element. There's no -upper limit to the number of acceptors per server. +Acceptors are always defined inside an `acceptors` element. There can be one or +more acceptors defined in the `acceptors` element. There's no upper limit to +the number of acceptors per server. -Each acceptor defines a way in which connections can be made to the -Apache ActiveMQ Artemis server. +Each acceptor defines a way in which connections can be made to the Apache +ActiveMQ Artemis server. In the above example we're defining an acceptor that uses -[Netty](https://netty.io/) to listen for connections at port -`61617`. - -The `acceptor` element contains a `URL` that defines the kind of Acceptor -to create along with its configuration. The `schema` part of the `URL` -defines the Acceptor type which can either be `tcp` or `vm` which is -`Netty` or an In VM Acceptor respectively. For `Netty` the host and the -port of the `URL` define what host and port the `acceptor` will bind to. For -In VM the `Authority` part of the `URL` defines a unique server id. - -The `acceptor` can also be configured with a set of key=value pairs -used to configure the specific transport, the set of -valid key=value pairs depends on the specific transport be used and are -passed straight through to the underlying transport. These are set on the -`URL` as part of the query, like so: +[Netty](https://netty.io/) to listen for connections at port `61617`. + +The `acceptor` element contains a `URL` that defines the kind of Acceptor to +create along with its configuration. The `schema` part of the `URL` defines the +Acceptor type which can either be `tcp` or `vm` which is `Netty` or an In VM +Acceptor respectively. For `Netty` the host and the port of the `URL` define +what host and port the `acceptor` will bind to. For In VM the `Authority` part +of the `URL` defines a unique server id. + +The `acceptor` can also be configured with a set of key=value pairs used to +configure the specific transport, the set of valid key=value pairs depends on +the specific transport be used and are passed straight through to the +underlying transport. These are set on the `URL` as part of the query, like so: ```xml tcp://localhost:61617?sslEnabled=true&keyStorePath=/path @@ -45,43 +41,41 @@ passed straight through to the underlying transport. These are set on the ## Connectors -Whereas acceptors are used on the server to define how we accept -connections, connectors are used to define how to connect to a server. +Whereas acceptors are used on the server to define how we accept connections, +connectors are used to define how to connect to a server. Let's look at a connector defined in our `broker.xml` file: ```xml - - tcp://localhost:61617 - +tcp://localhost:61617 ``` -Connectors can be defined inside a `connectors` element. There can be -one or more connectors defined in the `connectors` element. There's no -upper limit to the number of connectors per server. +Connectors can be defined inside a `connectors` element. There can be one or +more connectors defined in the `connectors` element. There's no upper limit to +the number of connectors per server. A `connector` is used when the server acts as a client itself, e.g.: -- When one server is bridged to another -- When a server takes part in a cluster +- When one server is bridged to another +- When a server takes part in a cluster In these cases the server needs to know how to connect to other servers. That's defined by `connectors`. ## Configuring the Transport Directly from the Client -How do we configure a core `ClientSessionFactory` with the information -that it needs to connect with a server? +How do we configure a core `ClientSessionFactory` with the information that it +needs to connect with a server? Connectors are also used indirectly when configuring a core -`ClientSessionFactory` to directly talk to a server. Although in this -case there's no need to define such a connector in the server side -configuration, instead we just specify the appropriate URI. +`ClientSessionFactory` to directly talk to a server. Although in this case +there's no need to define such a connector in the server side configuration, +instead we just specify the appropriate URI. -Here's an example of creating a `ClientSessionFactory` which will -connect directly to the acceptor we defined earlier in this chapter, it -uses the standard Netty TCP transport and will try and connect on port -61617 to localhost (default): +Here's an example of creating a `ClientSessionFactory` which will connect +directly to the acceptor we defined earlier in this chapter, it uses the +standard Netty TCP transport and will try and connect on port 61617 to +localhost (default): ```java ServerLocator locator = ActiveMQClient.createServerLocator("tcp://localhost:61617"); @@ -91,8 +85,8 @@ ClientSessionFactory sessionFactory = locator.createClientSessionFactory(); ClientSession session = sessionFactory.createSession(...); ``` -Similarly, if you're using JMS, you can configure the JMS connection -factory directly on the client side: +Similarly, if you're using JMS, you can configure the JMS connection factory +directly on the client side: ```java ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61617"); @@ -103,8 +97,7 @@ Connection jmsConnection = connectionFactory.createConnection(); ## Configuring the Netty transport Out of the box, Apache ActiveMQ Artemis currently uses -[Netty](https://netty.io/), a high performance low level -network library. +[Netty](https://netty.io/), a high performance low level network library. Our Netty transport can be configured in several different ways; to use straightforward TCP sockets, SSL, or to tunnel over HTTP or HTTPS.. @@ -113,14 +106,14 @@ We believe this caters for the vast majority of transport requirements. ### Single Port Support -Apache ActiveMQ Artemis supports using a single port for all -protocols, Apache ActiveMQ Artemis will automatically detect which protocol is being -used CORE, AMQP, STOMP or OPENWIRE and use the appropriate Apache ActiveMQ Artemis -handler. It will also detect whether protocols such as HTTP or Web -Sockets are being used and also use the appropriate decoders +Apache ActiveMQ Artemis supports using a single port for all protocols, Apache +ActiveMQ Artemis will automatically detect which protocol is being used CORE, +AMQP, STOMP or OPENWIRE and use the appropriate Apache ActiveMQ Artemis +handler. It will also detect whether protocols such as HTTP or Web Sockets are +being used and also use the appropriate decoders -It is possible to limit which protocols are supported by using the -`protocols` parameter on the Acceptor like so: +It is possible to limit which protocols are supported by using the `protocols` +parameter on the Acceptor like so: ```xml tcp://localhost:61617?protocols=CORE,AMQP @@ -129,348 +122,344 @@ It is possible to limit which protocols are supported by using the ### Configuring Netty TCP Netty TCP is a simple unencrypted TCP sockets based transport. If you're -running connections across an untrusted network please bear in -mind this transport is unencrypted. You may want to look at the SSL or -HTTPS configurations. +running connections across an untrusted network please bear in mind this +transport is unencrypted. You may want to look at the SSL or HTTPS +configurations. -With the Netty TCP transport all connections are initiated from the -client side (i.e. the server does not initiate any connections to the -client). This works well with firewall policies that typically only allow -connections to be initiated in one direction. +With the Netty TCP transport all connections are initiated from the client side +(i.e. the server does not initiate any connections to the client). This works +well with firewall policies that typically only allow connections to be +initiated in one direction. All the valid keys for the `tcp` URL scheme used for Netty are defined in the -class `org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants`. -Most parameters can be used either with acceptors or connectors, some only -work with acceptors. The following parameters can be used to configure -Netty for simple TCP: +class +`org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants`. +Most parameters can be used either with acceptors or connectors, some only work +with acceptors. The following parameters can be used to configure Netty for +simple TCP: -> **Note** +> **Note:** > -> The `host` and `port` parameters are only used in the core API, in -> XML configuration these are set in the URI host and port. - -- `host`. This specifies the host name or IP address to connect to - (when configuring a connector) or to listen on (when configuring an - acceptor). The default value for this property is `localhost`. When - configuring acceptors, multiple hosts or IP addresses can be - specified by separating them with commas. It is also possible to - specify `0.0.0.0` to accept connection from all the host's network - interfaces. It's not valid to specify multiple addresses when - specifying the host for a connector; a connector makes a connection - to one specific address. - - > **Note** - > - > Don't forget to specify a host name or IP address! If you want - > your server able to accept connections from other nodes you must - > specify a hostname or IP address at which the acceptor will bind - > and listen for incoming connections. The default is localhost - > which of course is not accessible from remote nodes! - -- `port`. This specified the port to connect to (when configuring a - connector) or to listen on (when configuring an acceptor). The - default value for this property is `61616`. - -- `tcpNoDelay`. If this is `true` then [Nagle's - algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm) will be - disabled. This is a [Java (client) socket - option](https://docs.oracle.com/javase/8/docs/technotes/guides/net/socketOpt.html). - The default value for this property is `true`. - -- `tcpSendBufferSize`. This parameter determines the size of the - TCP send buffer in bytes. The default value for this property is - `32768` bytes (32KiB). - - TCP buffer sizes should be tuned according to the bandwidth and - latency of your network. Here's a good link that explains the theory - behind [this](http://www-didc.lbl.gov/TCP-tuning/). - - In summary TCP send/receive buffer sizes should be calculated as: - - buffer_size = bandwidth * RTT. - - Where bandwidth is in *bytes per second* and network round trip time - (RTT) is in seconds. RTT can be easily measured using the `ping` - utility. - - For fast networks you may want to increase the buffer sizes from the - defaults. - -- `tcpReceiveBufferSize`. This parameter determines the size of the - TCP receive buffer in bytes. The default value for this property is - `32768` bytes (32KiB). +> The `host` and `port` parameters are only used in the core API, in XML +> configuration these are set in the URI host and port. + +- `host`. This specifies the host name or IP address to connect to (when + configuring a connector) or to listen on (when configuring an acceptor). The + default value for this property is `localhost`. When configuring acceptors, + multiple hosts or IP addresses can be specified by separating them with commas. + It is also possible to specify `0.0.0.0` to accept connection from all the + host's network interfaces. It's not valid to specify multiple addresses when + specifying the host for a connector; a connector makes a connection to one + specific address. + + > **Note:** + > + > Don't forget to specify a host name or IP address! If you want your server + > able to accept connections from other nodes you must specify a hostname or + > IP address at which the acceptor will bind and listen for incoming + > connections. The default is localhost which of course is not accessible + > from remote nodes! + +- `port`. This specified the port to connect to (when configuring a connector) + or to listen on (when configuring an acceptor). The default value for this + property is `61616`. + +- `tcpNoDelay`. If this is `true` then [Nagle's + algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm) will be + disabled. This is a [Java (client) socket + option](https://docs.oracle.com/javase/8/docs/technotes/guides/net/socketOpt.html). + The default value for this property is `true`. + +- `tcpSendBufferSize`. This parameter determines the size of the TCP send + buffer in bytes. The default value for this property is `32768` bytes + (32KiB). + + TCP buffer sizes should be tuned according to the bandwidth and latency of + your network. Here's a good link that explains the theory behind + [this](http://www-didc.lbl.gov/TCP-tuning/). + + In summary TCP send/receive buffer sizes should be calculated as: + + buffer_size = bandwidth * RTT. + + Where bandwidth is in *bytes per second* and network round trip time (RTT) is + in seconds. RTT can be easily measured using the `ping` utility. + + For fast networks you may want to increase the buffer sizes from the + defaults. + +- `tcpReceiveBufferSize`. This parameter determines the size of the TCP receive + buffer in bytes. The default value for this property is `32768` bytes + (32KiB). -- `writeBufferLowWaterMark`. This parameter determines the low water mark of - the Netty write buffer. Once the number of bytes queued in the write buffer exceeded - the high water mark and then dropped down below this value, Netty's channel - will start to be writable again. The default value for this property is - `32768` bytes (32KiB). +- `writeBufferLowWaterMark`. This parameter determines the low water mark of + the Netty write buffer. Once the number of bytes queued in the write buffer + exceeded the high water mark and then dropped down below this value, Netty's + channel will start to be writable again. The default value for this property is + `32768` bytes (32KiB). -- `writeBufferHighWaterMark`. This parameter determines the high water mark of - the Netty write buffer. If the number of bytes queued in the write buffer exceeds - this value, Netty's channel will start to be not writable. The default value for - this property is `131072` bytes (128KiB). - -- `batchDelay`. Before writing packets to the transport, Apache ActiveMQ Artemis can - be configured to batch up writes for a maximum of `batchDelay` - milliseconds. This can increase overall throughput for very small - messages. It does so at the expense of an increase in average - latency for message transfer. The default value for this property is - `0` ms. - -- `directDeliver`. When a message arrives on the server and is - delivered to waiting consumers, by default, the delivery is done on - the same thread as that on which the message arrived. This gives - good latency in environments with relatively small messages and a - small number of consumers, but at the cost of overall throughput and - scalability - especially on multi-core machines. If you want the - lowest latency and a possible reduction in throughput then you can - use the default value for `directDeliver` (i.e. `true`). If you are - willing to take some small extra hit on latency but want the highest - throughput set `directDeliver` to `false`. - -- `nioRemotingThreads` This is deprecated. It is replaced by `remotingThreads`, - if you are using this please update your configuration - -- `remotingThreads`. Apache ActiveMQ Artemis will, - by default, use a number of threads equal to three times the number - of cores (or hyper-threads) as reported by - `Runtime.getRuntime().availableProcessors()` for processing incoming - packets. If you want to override this value, you can set the number - of threads by specifying this parameter. The default value for this - parameter is `-1` which means use the value from - `Runtime.getRuntime().availableProcessors()` \* 3. - -- `localAddress`. When configured a Netty Connector it is possible to - specify which local address the client will use when connecting to - the remote address. This is typically used in the Application Server - or when running Embedded to control which address is used for - outbound connections. If the local-address is not set then the - connector will use any local address available - -- `localPort`. When configured a Netty Connector it is possible to - specify which local port the client will use when connecting to the - remote address. This is typically used in the Application Server or - when running Embedded to control which port is used for outbound - connections. If the local-port default is used, which is 0, then the - connector will let the system pick up an ephemeral port. valid ports - are 0 to 65535 - -- `connectionsAllowed`. This is only valid for acceptors. It limits the - number of connections which the acceptor will allow. When this limit - is reached a DEBUG level message is issued to the log, and the connection - is refused. The type of client in use will determine what happens when - the connection is refused. In the case of a `core` client, it will - result in a `org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException`. - -- `handshake-timeout`. Prevents an unauthorised client opening a large - number of connections and just keeping them open. As connections each - require a file handle this consumes resources that are then unavailable - to other clients. Once the connection is authenticated, the usual rules - can be enforced regarding resource consumption. Default value is set to - 10 seconds. Each integer is valid value. When set value to zero or - negative integer this feature is turned off. Changing value needs - to restart server to take effect. +- `writeBufferHighWaterMark`. This parameter determines the high water mark of + the Netty write buffer. If the number of bytes queued in the write buffer + exceeds this value, Netty's channel will start to be not writable. The default + value for this property is `131072` bytes (128KiB). + +- `batchDelay`. Before writing packets to the transport, Apache ActiveMQ + Artemis can be configured to batch up writes for a maximum of `batchDelay` + milliseconds. This can increase overall throughput for very small messages. It + does so at the expense of an increase in average latency for message transfer. + The default value for this property is `0` ms. + +- `directDeliver`. When a message arrives on the server and is delivered to + waiting consumers, by default, the delivery is done on the same thread as + that on which the message arrived. This gives good latency in environments with + relatively small messages and a small number of consumers, but at the cost of + overall throughput and scalability - especially on multi-core machines. If you + want the lowest latency and a possible reduction in throughput then you can use + the default value for `directDeliver` (i.e. `true`). If you are willing to take + some small extra hit on latency but want the highest throughput set + `directDeliver` to `false`. + +- `nioRemotingThreads` This is deprecated. It is replaced by `remotingThreads`, + if you are using this please update your configuration + +- `remotingThreads`. Apache ActiveMQ Artemis will, by default, use a number of + threads equal to three times the number of cores (or hyper-threads) as + reported by `Runtime.getRuntime().availableProcessors()` for processing + incoming packets. If you want to override this value, you can set the number of + threads by specifying this parameter. The default value for this parameter is + `-1` which means use the value from + `Runtime.getRuntime().availableProcessors()` \* 3. + +- `localAddress`. When configured a Netty Connector it is possible to specify + which local address the client will use when connecting to the remote + address. This is typically used in the Application Server or when running + Embedded to control which address is used for outbound connections. If the + local-address is not set then the connector will use any local address + available + +- `localPort`. When configured a Netty Connector it is possible to specify + which local port the client will use when connecting to the remote address. + This is typically used in the Application Server or when running Embedded to + control which port is used for outbound connections. If the local-port default + is used, which is 0, then the connector will let the system pick up an + ephemeral port. valid ports are 0 to 65535 + +- `connectionsAllowed`. This is only valid for acceptors. It limits the number + of connections which the acceptor will allow. When this limit is reached a + DEBUG level message is issued to the log, and the connection is refused. The + type of client in use will determine what happens when the connection is + refused. In the case of a `core` client, it will result in a + `org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException`. + +- `handshake-timeout`. Prevents an unauthorised client opening a large number + of connections and just keeping them open. As connections each require a file + handle this consumes resources that are then unavailable to other clients. Once + the connection is authenticated, the usual rules can be enforced regarding + resource consumption. Default value is set to 10 seconds. Each integer is valid + value. When set value to zero or negative integer this feature is turned off. + Changing value needs to restart server to take effect. ### Configuring Netty Native Transport -Netty Native Transport support exists for selected OS platforms. -This allows Apache ActiveMQ Artemis to use native sockets/io instead of Java NIO. +Netty Native Transport support exists for selected OS platforms. This allows +Apache ActiveMQ Artemis to use native sockets/io instead of Java NIO. -These Native transports add features specific to a particular platform, -generate less garbage, and generally improve performance when compared to Java NIO based transport. +These Native transports add features specific to a particular platform, +generate less garbage, and generally improve performance when compared to Java +NIO based transport. Both Clients and Server can benefit from this. Current Supported Platforms. -- Linux running 64bit JVM -- MacOS running 64bit JVM +- Linux running 64bit JVM +- MacOS running 64bit JVM -Apache ActiveMQ Artemis will by default enable the corresponding native transport if a supported platform is detected. +Apache ActiveMQ Artemis will by default enable the corresponding native +transport if a supported platform is detected. -If running on an unsupported platform or any issues loading native libs, Apache ActiveMQ Artemis will fallback onto Java NIO. +If running on an unsupported platform or any issues loading native libs, Apache +ActiveMQ Artemis will fallback onto Java NIO. #### Linux Native Transport -On supported Linux platforms Epoll is used, @see https://en.wikipedia.org/wiki/Epoll. +On supported Linux platforms Epoll is used, @see +https://en.wikipedia.org/wiki/Epoll. The following properties are specific to this native transport: -- `useEpoll` enables the use of epoll if a supported linux platform is running a 64bit JVM is detected. - Setting this to `false` will force the use of Java NIO instead of epoll. Default is `true` +- `useEpoll` enables the use of epoll if a supported linux platform is running + a 64bit JVM is detected. Setting this to `false` will force the use of Java + NIO instead of epoll. Default is `true` #### MacOS Native Transport -On supported MacOS platforms KQueue is used, @see https://en.wikipedia.org/wiki/Kqueue. +On supported MacOS platforms KQueue is used, @see +https://en.wikipedia.org/wiki/Kqueue. The following properties are specific to this native transport: -- `useKQueue` enables the use of kqueue if a supported MacOS platform running a 64bit JVM is detected. - Setting this to `false` will force the use of Java NIO instead of kqueue. Default is `true` +- `useKQueue` enables the use of kqueue if a supported MacOS platform running a + 64bit JVM is detected. Setting this to `false` will force the use of Java + NIO instead of kqueue. Default is `true` - ### Configuring Netty SSL -Netty SSL is similar to the Netty TCP transport but it provides -additional security by encrypting TCP connections using the Secure -Sockets Layer SSL +Netty SSL is similar to the Netty TCP transport but it provides additional +security by encrypting TCP connections using the Secure Sockets Layer SSL Please see the examples for a full working example of using Netty SSL. -Netty SSL uses all the same properties as Netty TCP but adds the -following additional properties: - -- `sslEnabled` - - Must be `true` to enable SSL. Default is `false`. - -- `keyStorePath` - - When used on an `acceptor` this is the path to the SSL key store on - the server which holds the server's certificates (whether - self-signed or signed by an authority). - - When used on a `connector` this is the path to the client-side SSL - key store which holds the client certificates. This is only relevant - for a `connector` if you are using 2-way SSL (i.e. mutual - authentication). Although this value is configured on the server, it - is downloaded and used by the client. If the client needs to use a - different path from that set on the server then it can override the - server-side setting by either using the customary - "javax.net.ssl.keyStore" system property or the ActiveMQ-specific - "org.apache.activemq.ssl.keyStore" system property. The - ActiveMQ-specific system property is useful if another component on - client is already making use of the standard, Java system property. - -- `keyStorePassword` - - When used on an `acceptor` this is the password for the server-side - keystore. - - When used on a `connector` this is the password for the client-side - keystore. This is only relevant for a `connector` if you are using - 2-way SSL (i.e. mutual authentication). Although this value can be - configured on the server, it is downloaded and used by the client. - If the client needs to use a different password from that set on the - server then it can override the server-side setting by either using - the customary "javax.net.ssl.keyStorePassword" system property or - the ActiveMQ-specific "org.apache.activemq.ssl.keyStorePassword" - system property. The ActiveMQ-specific system property is useful if - another component on client is already making use of the standard, - Java system property. - -- `trustStorePath` - - When used on an `acceptor` this is the path to the server-side SSL - key store that holds the keys of all the clients that the server - trusts. This is only relevant for an `acceptor` if you are using - 2-way SSL (i.e. mutual authentication). - - When used on a `connector` this is the path to the client-side SSL - key store which holds the public keys of all the servers that the - client trusts. Although this value can be configured on the server, - it is downloaded and used by the client. If the client needs to use - a different path from that set on the server then it can override - the server-side setting by either using the customary - "javax.net.ssl.trustStore" system property or the ActiveMQ-specific - "org.apache.activemq.ssl.trustStore" system property. The - ActiveMQ-specific system property is useful if another component on - client is already making use of the standard, Java system property. - -- `trustStorePassword` - - When used on an `acceptor` this is the password for the server-side - trust store. This is only relevant for an `acceptor` if you are - using 2-way SSL (i.e. mutual authentication). - - When used on a `connector` this is the password for the client-side - truststore. Although this value can be configured on the server, it - is downloaded and used by the client. If the client needs to use a - different password from that set on the server then it can override - the server-side setting by either using the customary - "javax.net.ssl.trustStorePassword" system property or the - ActiveMQ-specific "org.apache.activemq.ssl.trustStorePassword" - system property. The ActiveMQ-specific system property is useful if - another component on client is already making use of the standard, - Java system property. - -- `enabledCipherSuites` - - Whether used on an `acceptor` or `connector` this is a comma - separated list of cipher suites used for SSL communication. The - default value is `null` which means the JVM's default will be used. - -- `enabledProtocols` - - Whether used on an `acceptor` or `connector` this is a comma - separated list of protocols used for SSL communication. The default - value is `null` which means the JVM's default will be used. - -- `needClientAuth` - - This property is only for an `acceptor`. It tells a client - connecting to this acceptor that 2-way SSL is required. Valid values - are `true` or `false`. Default is `false`. +Netty SSL uses all the same properties as Netty TCP but adds the following +additional properties: + +- `sslEnabled` + + Must be `true` to enable SSL. Default is `false`. + +- `keyStorePath` + + When used on an `acceptor` this is the path to the SSL key store on the + server which holds the server's certificates (whether self-signed or signed by + an authority). + + When used on a `connector` this is the path to the client-side SSL key store + which holds the client certificates. This is only relevant for a `connector` if + you are using 2-way SSL (i.e. mutual authentication). Although this value is + configured on the server, it is downloaded and used by the client. If the + client needs to use a different path from that set on the server then it can + override the server-side setting by either using the customary + "javax.net.ssl.keyStore" system property or the ActiveMQ-specific + "org.apache.activemq.ssl.keyStore" system property. The ActiveMQ-specific + system property is useful if another component on client is already making use + of the standard, Java system property. + +- `keyStorePassword` + + When used on an `acceptor` this is the password for the server-side keystore. + + When used on a `connector` this is the password for the client-side keystore. + This is only relevant for a `connector` if you are using 2-way SSL (i.e. mutual + authentication). Although this value can be configured on the server, it is + downloaded and used by the client. If the client needs to use a different + password from that set on the server then it can override the server-side + setting by either using the customary "javax.net.ssl.keyStorePassword" system + property or the ActiveMQ-specific "org.apache.activemq.ssl.keyStorePassword" + system property. The ActiveMQ-specific system property is useful if another + component on client is already making use of the standard, Java system + property. + +- `trustStorePath` + + When used on an `acceptor` this is the path to the server-side SSL key store + that holds the keys of all the clients that the server trusts. This is only + relevant for an `acceptor` if you are using 2-way SSL (i.e. mutual + authentication). + + When used on a `connector` this is the path to the client-side SSL key store + which holds the public keys of all the servers that the client trusts. Although + this value can be configured on the server, it is downloaded and used by the + client. If the client needs to use a different path from that set on the server + then it can override the server-side setting by either using the customary + "javax.net.ssl.trustStore" system property or the ActiveMQ-specific + "org.apache.activemq.ssl.trustStore" system property. The ActiveMQ-specific + system property is useful if another component on client is already making use + of the standard, Java system property. + +- `trustStorePassword` + + When used on an `acceptor` this is the password for the server-side trust + store. This is only relevant for an `acceptor` if you are using 2-way SSL (i.e. + mutual authentication). + + When used on a `connector` this is the password for the client-side + truststore. Although this value can be configured on the server, it is + downloaded and used by the client. If the client needs to use a different + password from that set on the server then it can override the server-side + setting by either using the customary "javax.net.ssl.trustStorePassword" system + property or the ActiveMQ-specific "org.apache.activemq.ssl.trustStorePassword" + system property. The ActiveMQ-specific system property is useful if another + component on client is already making use of the standard, Java system + property. + +- `enabledCipherSuites` + + Whether used on an `acceptor` or `connector` this is a comma separated list + of cipher suites used for SSL communication. The default value is `null` which + means the JVM's default will be used. + +- `enabledProtocols` + + Whether used on an `acceptor` or `connector` this is a comma separated list + of protocols used for SSL communication. The default value is `null` which + means the JVM's default will be used. + +- `needClientAuth` + + This property is only for an `acceptor`. It tells a client connecting to this + acceptor that 2-way SSL is required. Valid values are `true` or `false`. + Default is `false`. - Note that this property takes precedence over `wantClientAuth` and if - its value is set to true then `wantClientAuth` will be ignored. + **Note:** This property takes precedence over `wantClientAuth` and if its + value is set to true then `wantClientAuth` will be ignored. -- `wantClientAuth` +- `wantClientAuth` - This property is only for an `acceptor`. It tells a client - connecting to this acceptor that 2-way SSL is requested but not required. - Valid values are `true` or `false`. Default is `false`. + This property is only for an `acceptor`. It tells a client connecting to this + acceptor that 2-way SSL is requested but not required. Valid values are `true` + or `false`. Default is `false`. - Note that if the property `needClientAuth` is set to true then that - property will take precedence and this property will be ignored. + **Note:** If the property `needClientAuth` is set to `true` then that + property will take precedence and this property will be ignored. -- `verifyHost` +- `verifyHost` - When used on an `acceptor` the `CN` of the connecting client's SSL certificate - will be compared to its hostname to verify they match. This is useful - only for 2-way SSL. + When used on an `acceptor` the `CN` of the connecting client's SSL + certificate will be compared to its hostname to verify they match. This is + useful only for 2-way SSL. - When used on a `connector` the `CN` of the server's SSL certificate will be - compared to its hostname to verify they match. This is useful for both 1-way - and 2-way SSL. + When used on a `connector` the `CN` of the server's SSL certificate will be + compared to its hostname to verify they match. This is useful for both 1-way + and 2-way SSL. - Valid values are `true` or `false`. Default is `false`. + Valid values are `true` or `false`. Default is `false`. -- `trustAll` +- `trustAll` - When used on a `connector` the client will trust the provided server certificate - implicitly, regardless of any configured trust store. **Warning:** This setting is - primarily for testing purposes only and should not be used in production. + When used on a `connector` the client will trust the provided server + certificate implicitly, regardless of any configured trust store. **Warning:** + This setting is primarily for testing purposes only and should not be used in + production. - Valid values are `true` or `false`. Default is `false`. + Valid values are `true` or `false`. Default is `false`. -- `forceSSLParameters` +- `forceSSLParameters` - When used on a `connector` any SSL settings that are set as parameters on the connector will - be used instead of JVM system properties including both javax.net.ssl and ActiveMQ system properties - to configure the SSL context for this connector. + When used on a `connector` any SSL settings that are set as parameters on the + connector will be used instead of JVM system properties including both + javax.net.ssl and ActiveMQ system properties to configure the SSL context for + this connector. - Valid values are `true` or `false`. Default is `false`. + Valid values are `true` or `false`. Default is `false`. -- `useDefaultSslContext` +- `useDefaultSslContext` - Only valid on a `connector`. Allows the `connector` to use the "default" SSL - context (via `SSLContext.getDefault()`) which can be set programmatically by - the client (via `SSLContext.setDefault(SSLContext)`). If set to `true` all - other SSL related parameters except for `sslEnabled` are ignored. + Only valid on a `connector`. Allows the `connector` to use the "default" SSL + context (via `SSLContext.getDefault()`) which can be set programmatically by + the client (via `SSLContext.setDefault(SSLContext)`). If set to `true` all + other SSL related parameters except for `sslEnabled` are ignored. - Valid values are `true` or `false`. Default is `false`. + Valid values are `true` or `false`. Default is `false`. -- `sslProvider` - - Used to change the SSL Provider between `JDK` and `OPENSSL`. The default is `JDK`. - If used with `OPENSSL` you can add `netty-tcnative` to your classpath to use the native - installed openssl. This can be useful if you want to use special ciphersuite - elliptic curve combinations - which are support through openssl but not through the JDK provider. See https://en.wikipedia.org/wiki/Comparison_of_TLS_implementations - for more information's. - - +- `sslProvider` + Used to change the SSL Provider between `JDK` and `OPENSSL`. The default is + `JDK`. If used with `OPENSSL` you can add `netty-tcnative` to your classpath + to use the native installed openssl. This can be useful if you want to use + special ciphersuite - elliptic curve combinations which are support through + openssl but not through the JDK provider. See + https://en.wikipedia.org/wiki/Comparison_of_TLS_implementations for more + information's. + ### Configuring Netty HTTP Netty HTTP tunnels packets over the HTTP protocol. It can be useful in @@ -481,22 +470,22 @@ Please see the examples for a full working example of using Netty HTTP. Netty HTTP uses the same properties as Netty TCP but adds the following additional properties: -- `httpEnabled`. This is now no longer needed. With single port support - Apache ActiveMQ Artemis will now automatically detect if http is being - used and configure itself. +- `httpEnabled`. This is now no longer needed. With single port support Apache + ActiveMQ Artemis will now automatically detect if http is being used and + configure itself. -- `httpClientIdleTime`. How long a client can be idle before - sending an empty http request to keep the connection alive +- `httpClientIdleTime`. How long a client can be idle before sending an empty + http request to keep the connection alive -- `httpClientIdleScanPeriod`. How often, in milliseconds, to scan - for idle clients +- `httpClientIdleScanPeriod`. How often, in milliseconds, to scan for idle + clients -- `httpResponseTime`. How long the server can wait before sending an - empty http response to keep the connection alive +- `httpResponseTime`. How long the server can wait before sending an empty http + response to keep the connection alive -- `httpServerScanPeriod`. How often, in milliseconds, to scan for - clients needing responses +- `httpServerScanPeriod`. How often, in milliseconds, to scan for clients + needing responses -- `httpRequiresSessionId`. If `true` the client will wait after the - first call to receive a session id. Used the http connector is - connecting to servlet acceptor (not recommended) +- `httpRequiresSessionId`. If `true` the client will wait after the first call + to receive a session id. Used the http connector is connecting to servlet + acceptor (not recommended) diff --git a/docs/user-manual/en/connection-ttl.md b/docs/user-manual/en/connection-ttl.md index a1876bd0961..3677d6fdca0 100644 --- a/docs/user-manual/en/connection-ttl.md +++ b/docs/user-manual/en/connection-ttl.md @@ -18,8 +18,7 @@ ServerLocator locator = null; ClientSessionFactory sf = null; ClientSession session = null; -try -{ +try { locator = ActiveMQClient.createServerLocatorWithoutHA(..); sf = locator.createClientSessionFactory();; @@ -27,21 +26,16 @@ try session = sf.createSession(...); ... do some stuff with the session... -} -finally -{ - if (session != null) - { +} finally { + if (session != null) { session.close(); } - if (sf != null) - { + if (sf != null) { sf.close(); } - if(locator != null) - { + if(locator != null) { locator.close(); } } @@ -52,18 +46,14 @@ And here's an example of a well behaved JMS client application: ```java Connection jmsConnection = null; -try -{ +try { ConnectionFactory jmsConnectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616"); jmsConnection = jmsConnectionFactory.createConnection(); ... do some stuff with the connection... -} -finally -{ - if (connection != null) - { +} finally { + if (connection != null) { connection.close(); } } @@ -73,17 +63,13 @@ finally Or with using auto-closeable feature from Java, which can save a few lines of code: ```java - - try ( ActiveMQConnectionFactory jmsConnectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616"); - Connection jmsConnection = jmsConnectionFactory.createConnection()) -{ + Connection jmsConnection = jmsConnectionFactory.createConnection()) { ... do some stuff with the connection... } ``` - Unfortunately users don't always write well behaved applications, and sometimes clients just crash so they don't have a chance to clean up their resources! @@ -186,17 +172,17 @@ from a thread pool so that the remoting thread is not tied up for too long. Please note that processing operations asynchronously on another thread adds a little more latency. These packets are: -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.RollbackMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.RollbackMessage` -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionCloseMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionCloseMessage` -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionCommitMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionCommitMessage` -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXACommitMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXACommitMessage` -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAPrepareMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAPrepareMessage` -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXARollbackMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXARollbackMessage` To disable asynchronous connection execution, set the parameter `async-connection-execution-enabled` in `broker.xml` to diff --git a/docs/user-manual/en/core-bridges.md b/docs/user-manual/en/core-bridges.md index 668a3b5a4a9..ede12abefae 100644 --- a/docs/user-manual/en/core-bridges.md +++ b/docs/user-manual/en/core-bridges.md @@ -1,45 +1,46 @@ # Core Bridges The function of a bridge is to consume messages from a source queue, and -forward them to a target address, typically on a different Apache ActiveMQ Artemis -server. +forward them to a target address, typically on a different Apache ActiveMQ +Artemis server. -The source and target servers do not have to be in the same cluster -which makes bridging suitable for reliably sending messages from one -cluster to another, for instance across a WAN, or internet and where the -connection may be unreliable. +The source and target servers do not have to be in the same cluster which makes +bridging suitable for reliably sending messages from one cluster to another, +for instance across a WAN, or internet and where the connection may be +unreliable. The bridge has built in resilience to failure so if the target server connection is lost, e.g. due to network failure, the bridge will retry -connecting to the target until it comes back online. When it comes back -online it will resume operation as normal. +connecting to the target until it comes back online. When it comes back online +it will resume operation as normal. -In summary, bridges are a way to reliably connect two separate Apache ActiveMQ Artemis -servers together. With a core bridge both source and target servers must -be Apache ActiveMQ Artemis servers. +In summary, bridges are a way to reliably connect two separate Apache ActiveMQ +Artemis servers together. With a core bridge both source and target servers +must be Apache ActiveMQ Artemis servers. -Bridges can be configured to provide *once and only once* delivery -guarantees even in the event of the failure of the source or the target -server. They do this by using duplicate detection (described in [Duplicate Detection](duplicate-detection.md)). +Bridges can be configured to provide *once and only once* delivery guarantees +even in the event of the failure of the source or the target server. They do +this by using duplicate detection (described in [Duplicate +Detection](duplicate-detection.md)). -> **Note** +> **Note:** > -> Although they have similar function, don't confuse core bridges with -> JMS bridges! +> Although they have similar function, don't confuse core bridges with JMS +> bridges! > -> Core bridges are for linking an Apache ActiveMQ Artemis node with another Apache ActiveMQ Artemis -> node and do not use the JMS API. A JMS Bridge is used for linking any -> two JMS 1.1 compliant JMS providers. So, a JMS Bridge could be used -> for bridging to or from different JMS compliant messaging system. It's -> always preferable to use a core bridge if you can. Core bridges use -> duplicate detection to provide *once and only once* guarantees. To -> provide the same guarantee using a JMS bridge you would have to use XA -> which has a higher overhead and is more complex to configure. +> Core bridges are for linking an Apache ActiveMQ Artemis node with another +> Apache ActiveMQ Artemis node and do not use the JMS API. A JMS Bridge is used +> for linking any two JMS 1.1 compliant JMS providers. So, a JMS Bridge could +> be used for bridging to or from different JMS compliant messaging system. +> It's always preferable to use a core bridge if you can. Core bridges use +> duplicate detection to provide *once and only once* guarantees. To provide +> the same guarantee using a JMS bridge you would have to use XA which has a +> higher overhead and is more complex to configure. ## Configuring Bridges -Bridges are configured in `broker.xml`. Let's kick off -with an example (this is actually from the bridge example): +Bridges are configured in `broker.xml`. Let's kick off with an example (this is +actually from the bridge example): ```xml @@ -68,154 +69,144 @@ with an example (this is actually from the bridge example): ``` -In the above example we have shown all the parameters its possible to -configure for a bridge. In practice you might use many of the defaults -so it won't be necessary to specify them all explicitly. +In the above example we have shown all the parameters its possible to configure +for a bridge. In practice you might use many of the defaults so it won't be +necessary to specify them all explicitly. Let's take a look at all the parameters in turn: -- `name` attribute. All bridges must have a unique name in the server. +- `name` attribute. All bridges must have a unique name in the server. -- `queue-name`. This is the unique name of the local queue that the - bridge consumes from, it's a mandatory parameter. +- `queue-name`. This is the unique name of the local queue that the bridge + consumes from, it's a mandatory parameter. - The queue must already exist by the time the bridge is instantiated - at start-up. - -- `forwarding-address`. This is the address on the target server that - the message will be forwarded to. If a forwarding address is not - specified, then the original address of the message will be - retained. - -- `filter-string`. An optional filter string can be supplied. If - specified then only messages which match the filter expression - specified in the filter string will be forwarded. The filter string - follows the ActiveMQ Artemis filter expression syntax described in [Filter Expressions](filter-expressions.md). - -- `transformer-class-name`. An optional transformer-class-name can be - specified. This is the name of a user-defined class which implements - the `org.apache.activemq.artemis.core.server.transformer.Transformer` interface. - - If this is specified then the transformer's `transform()` method - will be invoked with the message before it is forwarded. This gives - you the opportunity to transform the message's header or body before - forwarding it. - -- `ha`. This optional parameter determines whether or not this bridge - should support high availability. True means it will connect to any - available server in a cluster and support failover. The default - value is `false`. - -- `retry-interval`. This optional parameter determines the period in - milliseconds between subsequent reconnection attempts, if the - connection to the target server has failed. The default value is - `2000`milliseconds. - -- `retry-interval-multiplier`. This optional parameter determines - determines a multiplier to apply to the time since the last retry to - compute the time to the next retry. - - This allows you to implement an *exponential backoff* between retry - attempts. - - Let's take an example: - - If we set `retry-interval`to `1000` ms and we set - `retry-interval-multiplier` to `2.0`, then, if the first reconnect - attempt fails, we will wait `1000` ms then `2000` ms then `4000` ms - between subsequent reconnection attempts. - - The default value is `1.0` meaning each reconnect attempt is spaced - at equal intervals. - -- `initial-connect-attempts`. This optional parameter determines the - total number of initial connect attempts the bridge will make before - giving up and shutting down. A value of `-1` signifies an unlimited - number of attempts. The default value is `-1`. - -- `reconnect-attempts`. This optional parameter determines the total - number of reconnect attempts the bridge will make before giving up - and shutting down. A value of `-1` signifies an unlimited number of - attempts. The default value is `-1`. - -- `failover-on-server-shutdown`. This optional parameter determines - whether the bridge will attempt to failover onto a backup server (if - specified) when the target server is cleanly shutdown rather than - crashed. - - The bridge connector can specify both a live and a backup server, if - it specifies a backup server and this parameter is set to `true` - then if the target server is *cleanly* shutdown the bridge - connection will attempt to failover onto its backup. If the bridge - connector has no backup server configured then this parameter has no - effect. - - Sometimes you want a bridge configured with a live and a backup - target server, but you don't want to failover to the backup if the - live server is simply taken down temporarily for maintenance, this - is when this parameter comes in handy. - - The default value for this parameter is `false`. - -- `use-duplicate-detection`. This optional parameter determines - whether the bridge will automatically insert a duplicate id property - into each message that it forwards. - - Doing so, allows the target server to perform duplicate detection on - messages it receives from the source server. If the connection fails - or server crashes, then, when the bridge resumes it will resend - unacknowledged messages. This might result in duplicate messages - being sent to the target server. By enabling duplicate detection - allows these duplicates to be screened out and ignored. - - This allows the bridge to provide a *once and only once* delivery - guarantee without using heavyweight methods such as XA (see [Duplicate Detection](duplicate-detection.md) for - more information). - - The default value for this parameter is `true`. - -- `confirmation-window-size`. This optional parameter determines the - `confirmation-window-size` to use for the connection used to forward - messages to the target node. This attribute is described in section - [Reconnection and Session Reattachment](client-reconnection.md) - - > **Warning** - > - > When using the bridge to forward messages to an address which uses - > the `BLOCK` `address-full-policy` from a queue which has a - > `max-size-bytes` set it's important that - > `confirmation-window-size` is less than or equal to - > `max-size-bytes` to prevent the flow of messages from ceasing. - -- `producer-window-size`. This optional parameter determines the - producer flow control through the bridge. You usually leave this off - unless you are dealing with huge large messages. + The queue must already exist by the time the bridge is instantiated at + start-up. + +- `forwarding-address`. This is the address on the target server that the + message will be forwarded to. If a forwarding address is not specified, then + the original address of the message will be retained. + +- `filter-string`. An optional filter string can be supplied. If specified then + only messages which match the filter expression specified in the filter + string will be forwarded. The filter string follows the ActiveMQ Artemis filter + expression syntax described in [Filter Expressions](filter-expressions.md). + +- `transformer-class-name`. An optional transformer-class-name can be + specified. This is the name of a user-defined class which implements the + `org.apache.activemq.artemis.core.server.transformer.Transformer` interface. + + If this is specified then the transformer's `transform()` method will be + invoked with the message before it is forwarded. This gives you the opportunity + to transform the message's header or body before forwarding it. + +- `ha`. This optional parameter determines whether or not this bridge should + support high availability. True means it will connect to any available server + in a cluster and support failover. The default value is `false`. + +- `retry-interval`. This optional parameter determines the period in + milliseconds between subsequent reconnection attempts, if the connection to + the target server has failed. The default value is `2000`milliseconds. + +- `retry-interval-multiplier`. This optional parameter determines determines a + multiplier to apply to the time since the last retry to compute the time to + the next retry. + + This allows you to implement an *exponential backoff* between retry + attempts. + + Let's take an example: + + If we set `retry-interval`to `1000` ms and we set `retry-interval-multiplier` + to `2.0`, then, if the first reconnect attempt fails, we will wait `1000` ms + then `2000` ms then `4000` ms between subsequent reconnection attempts. + + The default value is `1.0` meaning each reconnect attempt is spaced at equal + intervals. + +- `initial-connect-attempts`. This optional parameter determines the total + number of initial connect attempts the bridge will make before giving up and + shutting down. A value of `-1` signifies an unlimited number of attempts. The + default value is `-1`. + +- `reconnect-attempts`. This optional parameter determines the total number of + reconnect attempts the bridge will make before giving up and shutting down. A + value of `-1` signifies an unlimited number of attempts. The default value is + `-1`. + +- `failover-on-server-shutdown`. This optional parameter determines whether the + bridge will attempt to failover onto a backup server (if specified) when the + target server is cleanly shutdown rather than crashed. + + The bridge connector can specify both a live and a backup server, if it + specifies a backup server and this parameter is set to `true` then if the + target server is *cleanly* shutdown the bridge connection will attempt to + failover onto its backup. If the bridge connector has no backup server + configured then this parameter has no effect. + + Sometimes you want a bridge configured with a live and a backup target + server, but you don't want to failover to the backup if the live server is + simply taken down temporarily for maintenance, this is when this parameter + comes in handy. + + The default value for this parameter is `false`. + +- `use-duplicate-detection`. This optional parameter determines whether the + bridge will automatically insert a duplicate id property into each message + that it forwards. + + Doing so, allows the target server to perform duplicate detection on messages + it receives from the source server. If the connection fails or server crashes, + then, when the bridge resumes it will resend unacknowledged messages. This + might result in duplicate messages being sent to the target server. By enabling + duplicate detection allows these duplicates to be screened out and ignored. + + This allows the bridge to provide a *once and only once* delivery guarantee + without using heavyweight methods such as XA (see [Duplicate + Detection](duplicate-detection.md) for more information). + + The default value for this parameter is `true`. + +- `confirmation-window-size`. This optional parameter determines the + `confirmation-window-size` to use for the connection used to forward messages + to the target node. This attribute is described in section [Reconnection and + Session Reattachment](client-reconnection.md) + + > **Warning** + > + > When using the bridge to forward messages to an address which uses the + > `BLOCK` `address-full-policy` from a queue which has a `max-size-bytes` set + > it's important that `confirmation-window-size` is less than or equal to + > `max-size-bytes` to prevent the flow of messages from ceasing. + +- `producer-window-size`. This optional parameter determines the producer flow + control through the bridge. You usually leave this off unless you are dealing + with huge large messages. - Default=-1 (disabled) - -- `user`. This optional parameter determines the user name to use when - creating the bridge connection to the remote server. If it is not - specified the default cluster user specified by `cluster-user` in - `broker.xml` will be used. - -- `password`. This optional parameter determines the password to use - when creating the bridge connection to the remote server. If it is - not specified the default cluster password specified by - `cluster-password` in `broker.xml` will be used. - -- `static-connectors` or `discovery-group-ref`. Pick either of these - options to connect the bridge to the target server. - - The `static-connectors` is a list of `connector-ref` elements - pointing to `connector` elements defined elsewhere. A *connector* - encapsulates knowledge of what transport to use (TCP, SSL, HTTP etc) - as well as the server connection parameters (host, port etc). For - more information about what connectors are and how to configure - them, please see [Configuring the Transport](configuring-transports.md). - - The `discovery-group-ref` element has one attribute - - `discovery-group-name`. This attribute points to a `discovery-group` - defined elsewhere. For more information about what discovery-groups - are and how to configure them, please see [Discovery Groups](clusters.md). + Default=-1 (disabled) + +- `user`. This optional parameter determines the user name to use when creating + the bridge connection to the remote server. If it is not specified the + default cluster user specified by `cluster-user` in `broker.xml` will be used. + +- `password`. This optional parameter determines the password to use when + creating the bridge connection to the remote server. If it is not specified + the default cluster password specified by `cluster-password` in `broker.xml` + will be used. + +- `static-connectors` or `discovery-group-ref`. Pick either of these options to + connect the bridge to the target server. + + The `static-connectors` is a list of `connector-ref` elements pointing to + `connector` elements defined elsewhere. A *connector* encapsulates knowledge of + what transport to use (TCP, SSL, HTTP etc) as well as the server connection + parameters (host, port etc). For more information about what connectors are and + how to configure them, please see [Configuring the + Transport](configuring-transports.md). + + The `discovery-group-ref` element has one attribute - `discovery-group-name`. + This attribute points to a `discovery-group` defined elsewhere. For more + information about what discovery-groups are and how to configure them, please + see [Discovery Groups](clusters.md). diff --git a/docs/user-manual/en/core.md b/docs/user-manual/en/core.md new file mode 100644 index 00000000000..cd8d131a549 --- /dev/null +++ b/docs/user-manual/en/core.md @@ -0,0 +1,228 @@ +# Using Core + +Apache ActiveMQ Artemis core is a messaging system with its own API. We call +this the *core API*. + +If you don't want to use the JMS API or any of the other supported protocols +you can use the core API directly. The core API provides all the functionality +of JMS but without much of the complexity. It also provides features that are +not available using JMS. + +## Core Messaging Concepts + +Some of the core messaging concepts are similar to JMS concepts, but core +messaging concepts are also different in some ways as well. In general the core +API is simpler than the JMS API, since we remove distinctions between queues, +topics and subscriptions. We'll discuss each of the major core messaging +concepts in turn, but to see the API in detail please consult the Javadoc. + +Also refer to the [addressing model](address-model.md) chapter for a high-level +overview of these concepts as well as configuration details. + +### Message + +- A message is the unit of data which is sent between clients and servers. + +- A message has a body which is a buffer containing convenient methods for + reading and writing data into it. + +- A message has a set of properties which are key-value pairs. Each property + key is a string and property values can be of type integer, long, short, + byte, byte[], String, double, float or boolean. + +- A message has an *address* it is being sent to. When the message arrives on + the server it is routed to any queues that are bound to the address. The + routing semantics (i.e. anycast or multicast) are determined by the "routing + type" of the address and queue. If the queues are bound with any filter, the + message will only be routed to that queue if the filter matches. An address may + have many queues bound to it or even none. There may also be entities other + than queues (e.g. *diverts*) bound to addresses. + +- Messages can be either durable or non durable. Durable messages in a durable + queue will survive a server crash or restart. Non durable messages will never + survive a server crash or restart. + +- Messages can be specified with a priority value between 0 and 9. 0 represents + the lowest priority and 9 represents the highest. The broker will attempt to + deliver higher priority messages before lower priority ones. + +- Messages can be specified with an optional expiry time. The broker will not + deliver messages after its expiry time has been exceeded. + +- Messages also have an optional timestamp which represents the time the + message was sent. + +- Apache ActiveMQ Artemis also supports the sending/consuming of very large + messages much larger than can fit in available RAM at any one time. + +### Address + +A server maintains a mapping between an address and a set of queues. Zero or +more queues can be bound to a single address. Each queue can be bound with an +optional message filter. When a message is routed, it is routed to the set of +queues bound to the message's address. If any of the queues are bound with a +filter expression, then the message will only be routed to the subset of bound +queues which match that filter expression. + +Other entities, such as *diverts* can also be bound to an address and messages +will also be routed there. + +> **Note:** +> +> Although core supports publish-subscribe semantics there is no such thing as +> a "topic" per se. "Topic" is mainly a JMS term. In core we just deal with +> *addresses*, *queues*, and *routing types*. +> +> For example, a JMS topic would be implemented by a single address to which +> many queues are bound using multicast routing. Each queue represents a +> "subscription" in normal "topic" terms. A JMS queue would be implemented as a +> single address to which one queue is bound using anycast routing - that queue +> represents the JMS queue. + +### Queue + +Queues can be durable, meaning the messages they contain survive a server crash +or restart, as long as the messages in them are durable. Non durable queues do +not survive a server restart or crash even if the messages they contain are +durable. + +Queues can also be temporary, meaning they are automatically deleted when the +client connection is closed, if they are not explicitly deleted before that. + +Queues can be bound with an optional filter expression. If a filter expression +is supplied then the server will only route messages that match that filter +expression to any queues bound to the address. + +Many queues can be bound to a single address. A particular queue is only bound +to a maximum of one address. + +### Routing Type + +The routing type determines the semantics used when routing messages to the +queues bound to the address where the message was sent. Two types are +supported: + +- `ANYCAST` + + The message is routed to only **one** of the queues bound to the address. If + multiple queues are bound to the address then messages are routed to them in a + round-robin fashion. + +- `MULTICAST` + + The message is route to **all** of the queues bound to the address. + +## Core API + +### ServerLocator + +Clients use `ServerLocator` instances to create `ClientSessionFactory` +instances. `ServerLocator` instances are used to locate servers and create +connections to them. + +In JMS terms think of a `ServerLocator` in the same way you would a JMS +Connection Factory. + +`ServerLocator` instances are created using the `ActiveMQClient` factory class. + +### ClientSessionFactory + +Clients use `ClientSessionFactory` instances to create `ClientSession` +instances. `ClientSessionFactory` instances are basically the connection to a +server + +In JMS terms think of them as JMS Connections. + +`ClientSessionFactory` instances are created using the `ServerLocator` class. + +### ClientSession + +A client uses a `ClientSession`for consuming and producing messages and for +grouping them in transactions. `ClientSession` instances can support both +transactional and non transactional semantics and also provide an `XAResource` +interface so messaging operations can be performed as part of a +[JTA](http://www.oracle.com/technetwork/java/javaee/tech/jta-138684.html) +transaction. + +`ClientSession` instances group `ClientConsumer` instances and `ClientProducer` +instances. + +`ClientSession` instances can be registered with an optional +`SendAcknowledgementHandler`. This allows your client code to be notified +asynchronously when sent messages have successfully reached the server. This +unique Apache ActiveMQ Artemis feature, allows you to have full guarantees that +sent messages have reached the server without having to block on each message +sent until a response is received. Blocking on each messages sent is costly +since it requires a network round trip for each message sent. By not blocking +and receiving send acknowledgements asynchronously you can create true end to +end asynchronous systems which is not possible using the standard JMS API. For +more information on this advanced feature please see the section [Guarantees of +sends and commits](send-guarantees.md). + +### ClientConsumer + +Clients use `ClientConsumer` instances to consume messages from a queue. Core +messaging supports both synchronous and asynchronous message consumption +semantics. `ClientConsumer` instances can be configured with an optional filter +expression and will only consume messages which match that expression. + +### ClientProducer + +Clients create `ClientProducer` instances on `ClientSession` instances so they +can send messages. `ClientProducer` instances can specify an address to which +all sent messages are routed, or they can have no specified address, and the +address is specified at send time for the message. + +> **Warning** +> +> Please note that `ClientSession`, `ClientProducer` and `ClientConsumer` +> instances are *designed to be re-used*. +> +> It's an anti-pattern to create new `ClientSession`, `ClientProducer` and +> `ClientConsumer` instances for each message you produce or consume. If you do +> this, your application will perform very poorly. This is discussed further +> in the section on performance tuning [Performance Tuning](perf-tuning.md). + +## A simple example of using Core + +Here's a very simple program using the core messaging API to send and receive a +message. Logically it's comprised of two sections: firstly setting up the +producer to write a message to an *addresss*, and secondly, creating a *queue* +for the consumer using anycast routing, creating the consumer, and *starting* +it. + +```java +ServerLocator locator = ActiveMQClient.createServerLocator("vm://0"); + +// In this simple example, we just use one session for both producing and receiving + +ClientSessionFactory factory = locator.createClientSessionFactory(); +ClientSession session = factory.createSession(); + +// A producer is associated with an address ... + +ClientProducer producer = session.createProducer("example"); +ClientMessage message = session.createMessage(true); +message.getBodyBuffer().writeString("Hello"); + +// We need a queue attached to the address ... + +session.createQueue("example", RoutingType.ANYCAST, "example", true); + +// And a consumer attached to the queue ... + +ClientConsumer consumer = session.createConsumer("example"); + +// Once we have a queue, we can send the message ... + +producer.send(message); + +// We need to start the session before we can -receive- messages ... + +session.start(); +ClientMessage msgReceived = consumer.receive(); + +System.out.println("message = " + msgReceived.getBodyBuffer().readString()); + +session.close(); +``` diff --git a/docs/user-manual/en/critical-analysis.md b/docs/user-manual/en/critical-analysis.md index 18c17cdf413..40da82db8bd 100644 --- a/docs/user-manual/en/critical-analysis.md +++ b/docs/user-manual/en/critical-analysis.md @@ -22,7 +22,7 @@ You can use these following configuration options on broker.xml to configure how Name | Description -:--- | :--- +--- | --- critical-analyzer | Enable or disable the critical analysis (default true) critical-analyzer-timeout | Timeout used to do the critical analysis (default 120000 milliseconds) critical-analyzer-check-period | Time used to check the response times (default half of critical-analyzer-timeout) @@ -42,7 +42,7 @@ If you have critical-analyzer-policy=HALT [Artemis Critical Analyzer] 18:10:00,831 ERROR [org.apache.activemq.artemis.core.server] AMQ224079: The process for the virtual machine will be killed, as component org.apache.activemq.artemis.tests.integration.critical.CriticalSimpleTest$2@5af97850 is not responsive ``` -While if you have critical-analyzer-policy=SHUTDOWN +While if you have critical-analyzer-policy=`SHUTDOWN` ``` [Artemis Critical Analyzer] 18:07:53,475 ERROR [org.apache.activemq.artemis.core.server] AMQ224080: The server process will now be stopped, as component org.apache.activemq.artemis.tests.integration.critical.CriticalSimpleTest$2@5af97850 is not responsive @@ -85,7 +85,7 @@ AMQ119003: End Thread dump - The Server will be halted if configured to `HALT` -- The system will be stopped if `SHUTDOWN` is used: -* Notice that if the system is not behaving well, there is no guarantees the stop will work. +- The system will be stopped if `SHUTDOWN` is used. **Notice**: If the system + is not behaving well, there is no guarantees the stop will work. diff --git a/docs/user-manual/en/data-tools.md b/docs/user-manual/en/data-tools.md new file mode 100644 index 00000000000..fb7443fd9f9 --- /dev/null +++ b/docs/user-manual/en/data-tools.md @@ -0,0 +1,348 @@ +# Data Tools + +You can use the Artemis CLI to execute data maintenance tools: + +This is a list of sub-commands available + +Name | Description +---|--- +exp | Export the message data using a special and independent XML format +imp | Imports the journal to a running broker using the output from expt +data | Prints a report about journal records and summary of existent records, as well a report on paging +encode | shows an internal format of the journal encoded to String +decode | imports the internal journal format from encode + +You can use the help at the tool for more information on how to execute each of the tools. For example: + +``` +$ ./artemis help data print +NAME + artemis data print - Print data records information (WARNING: don't use + while a production server is running) + +SYNOPSIS + artemis data print [--bindings ] [--broker ] + [--f] [--jdbc] [--jdbc-bindings-table-name ] + [--jdbc-connection-url ] + [--jdbc-driver-class-name ] + [--jdbc-large-message-table-name ] + [--jdbc-message-table-name ] + [--jdbc-page-store-table-name ] [--journal ] + [--large-messages ] [--output ] + [--paging ] [--safe] [--verbose] [--] [] + +OPTIONS + --bindings + The folder used for bindings (default from broker.xml) + + --broker + This would override the broker configuration from the bootstrap + + --f + This will allow certain tools like print-data to be performed + ignoring any running servers. WARNING: Changing data concurrently + with a running broker may damage your data. Be careful with this + option. + + --jdbc + It will activate jdbc + + --jdbc-bindings-table-name + Name of the jdbc bindigns table + + --jdbc-connection-url + The connection used for the database + + --jdbc-driver-class-name + JDBC driver classname + + --jdbc-large-message-table-name + Name of the large messages table + + --jdbc-message-table-name + Name of the jdbc messages table + + --jdbc-page-store-table-name + Name of the page sotre messages table + + --journal + The folder used for messages journal (default from broker.xml) + + --large-messages + The folder used for large-messages (default from broker.xml) + + --output + Output name for the file + + --paging + The folder used for paging (default from broker.xml) + + --safe + It will print your data structure without showing your data + + --verbose + Adds more information on the execution + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Broker Configuration URI, default + 'xml:${ARTEMIS_INSTANCE}/etc/bootstrap.xml' +``` + + +For a full list of data tools commands available use: + +``` +$ ./artemis help data +NAME + artemis data - data tools group (print|imp|exp|encode|decode|compact) + (example ./artemis data print) + +SYNOPSIS + artemis data + artemis data compact [--verbose] [--paging ] + [--journal ] [--large-messages ] + [--broker ] [--bindings ] + artemis data decode [--verbose] [--suffix ] [--paging ] + [--prefix ] [--file-size ] --input + [--journal ] [--directory ] + [--large-messages ] [--broker ] + [--bindings ] + artemis data encode [--verbose] [--directory ] + [--suffix ] [--paging ] [--prefix ] + [--file-size ] [--journal ] + [--large-messages ] [--broker ] + [--bindings ] + artemis data exp [--jdbc-bindings-table-name ] + [--jdbc-message-table-name ] [--paging ] + [--jdbc-connection-url ] + [--jdbc-large-message-table-name ] [--f] + [--large-messages ] [--broker ] + [--jdbc-page-store-table-name ] + [--jdbc-driver-class-name ] [--jdbc] [--verbose] + [--journal ] [--output ] [--bindings ] + artemis data imp [--user ] [--legacy-prefixes] [--verbose] + [--host ] [--port ] [--transaction] --input + [--password ] [--sort] + artemis data print [--jdbc-bindings-table-name ] + [--jdbc-message-table-name ] [--paging ] + [--jdbc-connection-url ] + [--jdbc-large-message-table-name ] [--f] + [--large-messages ] [--broker ] + [--jdbc-page-store-table-name ] + [--jdbc-driver-class-name ] [--safe] [--jdbc] [--verbose] + [--journal ] [--output ] [--bindings ] + +COMMANDS + With no arguments, Display help information + + print + Print data records information (WARNING: don't use while a + production server is running) + + With --jdbc-bindings-table-name option, Name of the jdbc bindigns + table + + With --jdbc-message-table-name option, Name of the jdbc messages + table + + With --paging option, The folder used for paging (default from + broker.xml) + + With --jdbc-connection-url option, The connection used for the + database + + With --jdbc-large-message-table-name option, Name of the large + messages table + + With --f option, This will allow certain tools like print-data to be + performed ignoring any running servers. WARNING: Changing data + concurrently with a running broker may damage your data. Be careful + with this option. + + With --large-messages option, The folder used for large-messages + (default from broker.xml) + + With --broker option, This would override the broker configuration + from the bootstrap + + With --jdbc-page-store-table-name option, Name of the page sotre + messages table + + With --jdbc-driver-class-name option, JDBC driver classname + + With --safe option, It will print your data structure without + showing your data + + With --jdbc option, It will activate jdbc + + With --verbose option, Adds more information on the execution + + With --journal option, The folder used for messages journal (default + from broker.xml) + + With --output option, Output name for the file + + With --bindings option, The folder used for bindings (default from + broker.xml) + + exp + Export all message-data using an XML that could be interpreted by + any system. + + With --jdbc-bindings-table-name option, Name of the jdbc bindigns + table + + With --jdbc-message-table-name option, Name of the jdbc messages + table + + With --paging option, The folder used for paging (default from + broker.xml) + + With --jdbc-connection-url option, The connection used for the + database + + With --jdbc-large-message-table-name option, Name of the large + messages table + + With --f option, This will allow certain tools like print-data to be + performed ignoring any running servers. WARNING: Changing data + concurrently with a running broker may damage your data. Be careful + with this option. + + With --large-messages option, The folder used for large-messages + (default from broker.xml) + + With --broker option, This would override the broker configuration + from the bootstrap + + With --jdbc-page-store-table-name option, Name of the page sotre + messages table + + With --jdbc-driver-class-name option, JDBC driver classname + + With --jdbc option, It will activate jdbc + + With --verbose option, Adds more information on the execution + + With --journal option, The folder used for messages journal (default + from broker.xml) + + With --output option, Output name for the file + + With --bindings option, The folder used for bindings (default from + broker.xml) + + imp + Import all message-data using an XML that could be interpreted by + any system. + + With --user option, User name used to import the data. (default + null) + + With --legacy-prefixes option, Do not remove prefixes from legacy + imports + + With --verbose option, Adds more information on the execution + + With --host option, The host used to import the data (default + localhost) + + With --port option, The port used to import the data (default 61616) + + With --transaction option, If this is set to true you will need a + whole transaction to commit at the end. (default false) + + With --input option, The input file name (default=exp.dmp) + + With --password option, User name used to import the data. (default + null) + + With --sort option, Sort the messages from the input (used for older + versions that won't sort messages) + + decode + Decode a journal's internal format into a new journal set of files + + With --verbose option, Adds more information on the execution + + With --suffix option, The journal suffix (default amq) + + With --paging option, The folder used for paging (default from + broker.xml) + + With --prefix option, The journal prefix (default activemq-data) + + With --file-size option, The journal size (default 10485760) + + With --input option, The input file name (default=exp.dmp) + + With --journal option, The folder used for messages journal (default + from broker.xml) + + With --directory option, The journal folder (default journal folder + from broker.xml) + + With --large-messages option, The folder used for large-messages + (default from broker.xml) + + With --broker option, This would override the broker configuration + from the bootstrap + + With --bindings option, The folder used for bindings (default from + broker.xml) + + encode + Encode a set of journal files into an internal encoded data format + + With --verbose option, Adds more information on the execution + + With --directory option, The journal folder (default the journal + folder from broker.xml) + + With --suffix option, The journal suffix (default amq) + + With --paging option, The folder used for paging (default from + broker.xml) + + With --prefix option, The journal prefix (default activemq-data) + + With --file-size option, The journal size (default 10485760) + + With --journal option, The folder used for messages journal (default + from broker.xml) + + With --large-messages option, The folder used for large-messages + (default from broker.xml) + + With --broker option, This would override the broker configuration + from the bootstrap + + With --bindings option, The folder used for bindings (default from + broker.xml) + + compact + Compacts the journal of a non running server + + With --verbose option, Adds more information on the execution + + With --paging option, The folder used for paging (default from + broker.xml) + + With --journal option, The folder used for messages journal (default + from broker.xml) + + With --large-messages option, The folder used for large-messages + (default from broker.xml) + + With --broker option, This would override the broker configuration + from the bootstrap + + With --bindings option, The folder used for bindings (default from + broker.xml) + +``` \ No newline at end of file diff --git a/docs/user-manual/en/diverts.md b/docs/user-manual/en/diverts.md index 7408f4b73a1..5681e96f5cd 100644 --- a/docs/user-manual/en/diverts.md +++ b/docs/user-manual/en/diverts.md @@ -117,9 +117,9 @@ non-exclusive divert, again from the divert example: ```xml -
orders
- spyTopic - false +
orders
+ spyTopic + false
``` diff --git a/docs/user-manual/en/duplicate-detection.md b/docs/user-manual/en/duplicate-detection.md index c96c70224de..b3e95359779 100644 --- a/docs/user-manual/en/duplicate-detection.md +++ b/docs/user-manual/en/duplicate-detection.md @@ -48,7 +48,7 @@ already received a message with that value of the header. If it has received a message with the same value before then it will ignore the message. -> **Note** +> **Note:** > > Using duplicate detection to move messages between nodes can give you > the same *once and only once* delivery guarantees as if you were using @@ -118,7 +118,7 @@ configured by the parameter `persist-id-cache`, also in be persisted to permanent storage as they are received. The default value for this parameter is `true`. -> **Note** +> **Note:** > > When choosing a size of the duplicate id cache be sure to set it to a > larger enough size so if you resend messages all the previously sent diff --git a/docs/user-manual/en/embedding-activemq.md b/docs/user-manual/en/embedding-activemq.md index d309a58a4fd..3dcfb31f85c 100644 --- a/docs/user-manual/en/embedding-activemq.md +++ b/docs/user-manual/en/embedding-activemq.md @@ -1,29 +1,30 @@ # Embedding Apache ActiveMQ Artemis -Apache ActiveMQ Artemis is designed as set of simple Plain Old Java Objects (POJOs). -This means Apache ActiveMQ Artemis can be instantiated and run in any dependency -injection framework such as Spring or Google Guice. It also means that if you have an application that could use -messaging functionality internally, then it can *directly instantiate* -Apache ActiveMQ Artemis clients and servers in its own application code to perform that -functionality. We call this *embedding* Apache ActiveMQ Artemis. - -Examples of applications that might want to do this include any -application that needs very high performance, transactional, persistent -messaging but doesn't want the hassle of writing it all from scratch. - -Embedding Apache ActiveMQ Artemis can be done in very few easy steps. Instantiate the -configuration object, instantiate the server, start it, and you have a -Apache ActiveMQ Artemis running in your virtual machine. It's as simple and easy as -that. +Apache ActiveMQ Artemis is designed as set of simple Plain Old Java Objects +(POJOs). This means Apache ActiveMQ Artemis can be instantiated and run in any +dependency injection framework such as Spring or Google Guice. It also means +that if you have an application that could use messaging functionality +internally, then it can *directly instantiate* Apache ActiveMQ Artemis clients +and servers in its own application code to perform that functionality. We call +this *embedding* Apache ActiveMQ Artemis. + +Examples of applications that might want to do this include any application +that needs very high performance, transactional, persistent messaging but +doesn't want the hassle of writing it all from scratch. + +Embedding Apache ActiveMQ Artemis can be done in very few easy steps. +Instantiate the configuration object, instantiate the server, start it, and you +have a Apache ActiveMQ Artemis running in your virtual machine. It's as simple +and easy as that. ## Simple Config File Embedding -The simplest way to embed Apache ActiveMQ Artemis is to use the embedded wrapper -classes and configure Apache ActiveMQ Artemis through its configuration files. There -are two different helper classes for this depending on whether your -using the Apache ActiveMQ Artemis Core API or JMS. +The simplest way to embed Apache ActiveMQ Artemis is to use the embedded +wrapper classes and configure Apache ActiveMQ Artemis through its configuration +files. There are two different helper classes for this depending on whether +your using the Apache ActiveMQ Artemis Core API or JMS. -## Embeddeing Apache ActiveMQ Artemis Server +## Embedding an Apache ActiveMQ Artemis Broker For instantiating a core Apache ActiveMQ Artemis Server, the steps are pretty simple. The example requires that you have defined a configuration file @@ -38,9 +39,9 @@ EmbeddedActiveMQ embedded = new EmbeddedActiveMQ(); embedded.start(); -ClientSessionFactory nettyFactory = ActiveMQClient.createClientSessionFactory( - new TransportConfiguration( - InVMConnectorFactory.class.getName())); +ServerLocator serverLocator = ActiveMQClient.createServerLocator("vm://0"); + +ClientSessionFactory factory = serverLocator.createSessionFactory(); ClientSession session = factory.createSession(); @@ -65,23 +66,22 @@ System.out.println("message = " + msgReceived.getBody().readString()); session.close(); ``` -The `EmbeddedActiveMQ` class has a few additional setter methods that -allow you to specify a different config file name as well as other -properties. See the javadocs for this class for more details. +The `EmbeddedActiveMQ` class has a few additional setter methods that allow you +to specify a different config file name as well as other properties. See the +javadocs for this class for more details. ## POJO instantiation - Embedding Programmatically -You can follow this step-by-step guide to programmatically embed the -core, non-JMS Apache ActiveMQ Artemis Server instance: +You can follow this step-by-step guide to programmatically embed the core, +non-JMS Apache ActiveMQ Artemis Server instance: -Create the configuration object - this contains configuration -information for an Apache ActiveMQ Artemis instance. The setter methods of this class -allow you to programmatically set configuration options as describe in -the [Server Configuration](configuration-index.md) section. +Create the configuration object - this contains configuration information for +an Apache ActiveMQ Artemis instance. The setter methods of this class allow you +to programmatically set configuration options as describe in the [Server +Configuration](configuration-index.md) section. -The acceptors are configured through `ConfigurationImpl`. Just add the -`NettyAcceptorFactory` on the transports the same way you would through -the main configuration file. +The acceptors are configured through `Configuration`. Just add the acceptor URL +the same way you would through the main configuration file. ```java import org.apache.activemq.artemis.core.config.Configuration; @@ -90,12 +90,9 @@ import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; ... Configuration config = new ConfigurationImpl(); -HashSet transports = new HashSet(); - -transports.add(new TransportConfiguration(NettyAcceptorFactory.class.getName())); -transports.add(new TransportConfiguration(InVMAcceptorFactory.class.getName())); -config.setAcceptorConfigurations(transports); +config.addAcceptorConfiguration("in-vm", "vm://0"); +config.addAcceptorConfiguration("tcp", "tcp://127.0.0.1:61616"); ``` You need to instantiate an instance of @@ -123,8 +120,9 @@ server.start(); ## Dependency Frameworks -You may also choose to use a dependency injection framework such as -The Spring Framework. See [Spring Integration](spring-integration.md) for more details on +You may also choose to use a dependency injection framework such as The Spring +Framework. See [Spring Integration](spring-integration.md) for more details on Spring and Apache ActiveMQ Artemis. -Apache ActiveMQ Artemis standalone uses [Airline](https://github.com/airlift/airline) to bootstrap. +Apache ActiveMQ Artemis standalone uses +[Airline](https://github.com/airlift/airline) to bootstrap. diff --git a/docs/user-manual/en/examples.md b/docs/user-manual/en/examples.md index 1f76dd7c1ac..70d5ee0d8d8 100644 --- a/docs/user-manual/en/examples.md +++ b/docs/user-manual/en/examples.md @@ -1,10 +1,10 @@ -Examples -======== +# Examples -The Apache ActiveMQ Artemis distribution comes with over 90 run out-of-the-box examples demonstrating many of the features. +The Apache ActiveMQ Artemis distribution comes with over 90 run out-of-the-box +examples demonstrating many of the features. -The examples are available in both the binary and source distribution under the `examples` directory. Examples are split - by the following source tree: +The examples are available in both the binary and source distribution under the +`examples` directory. Examples are split by the following source tree: - features - Examples containing broker specific features. - clustered - examples showing load balancing and distribution capabilities. @@ -18,59 +18,70 @@ The examples are available in both the binary and source distribution under the - openwire - stomp -Running the Examples -===================== +## Running the Examples -To run any example, simply `cd` into the appropriate example directory and type `mvn verify` or `mvn install` (For -details please read the readme.html in each example directory). +To run any example, simply `cd` into the appropriate example directory and type +`mvn verify` or `mvn install` (For details please read the readme.html in each +example directory). -You can use the profile `-Pexamples` to run multiple examples under any example tree. +You can use the profile `-Pexamples` to run multiple examples under any example +tree. -For each server, you will have a created server under `./target/server0` (some examples use more than one server). +For each server, you will have a created server under `./target/server0` (some +examples use more than one server). -You have the option to prevent the example from starting the server (e.g. if you want to start the server manually) by -simply specifying the `-PnoServer` profile, e.g.: +You have the option to prevent the example from starting the server (e.g. if +you want to start the server manually) by simply specifying the `-PnoServer` +profile, e.g.: ```sh # running an example without running the server mvn verify -PnoServer ``` -Also under `./target` there will be a script repeating the commands to create each server. Here is the `create-server0.sh` -generated by the `Queue` example. This is useful to see exactly what command(s) are required to configure the server(s). +Also under `./target` there will be a script repeating the commands to create +each server. Here is the `create-server0.sh` generated by the `Queue` example. +This is useful to see exactly what command(s) are required to configure the +server(s). ```sh # These are the commands used to create server0 -/myInstallDirectory/apache-artemis-1.1.0/bin/artemis create --allow-anonymous --silent --force --no-web --user guest --password guest --role guest --port-offset 0 --data ./data --allow-anonymous --no-autotune --verbose /myInstallDirectory/apache-artemis-1.1.0/examples/features/standard/queue/target/server0 +/myInstallDirectory/apache-artemis/bin/artemis create --allow-anonymous --silent --force --no-web --user guest --password guest --role guest --port-offset 0 --data ./data --allow-anonymous --no-autotune --verbose /myInstallDirectory/apache-artemis-1.1.0/examples/features/standard/queue/target/server0 ``` -Several examples use UDP clustering which may not work in your environment by default. On linux the command would be: +Several examples use UDP clustering which may not work in your environment by +default. On linux the command would be: ```sh route add -net 224.0.0.0 netmask 240.0.0.0 dev lo ``` -This command should be run as root. This will redirect any traffic directed to `224.0.0.0` to the loopback interface. -On Mac OS X, the command is slightly different: +This command should be run as root. This will redirect any traffic directed to +`224.0.0.0` to the loopback interface. On Mac OS X, the command is slightly +different: -``` sh +```sh sudo route add 224.0.0.0 127.0.0.1 -netmask 240.0.0.0 ``` -All the examples use the [Maven plugin](maven-plugin.md), which can be useful for running your test servers as well. +All the examples use the [Maven plugin](maven-plugin.md), which can be useful +for running your test servers as well. -This is the common output when running an example. On this case taken from the `Queue` example: +This is the common output when running an example. On this case taken from the +`Queue` example: ```sh [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ -[INFO] Building ActiveMQ Artemis JMS Queue Example 1.1.0 +[INFO] Building ActiveMQ Artemis JMS Queue Example 2.5.0 [INFO] ------------------------------------------------------------------------ [INFO] +[INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-maven) @ queue --- +[INFO] [INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-java) @ queue --- [INFO] -[INFO] --- maven-remote-resources-plugin:1.5:process (default) @ queue --- +[INFO] --- maven-remote-resources-plugin:1.5:process (process-resource-bundles) @ queue --- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ queue --- [INFO] Using 'UTF-8' encoding to copy filtered resources. @@ -78,17 +89,16 @@ This is the common output when running an example. On this case taken from the ` [INFO] Copying 3 resources [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ queue --- -[INFO] Changes detected - recompiling the module! -[INFO] Compiling 1 source file to /work/apache-artemis-1.1.0/examples/features/standard/queue/target/classes +[INFO] Nothing to compile - all classes are up to date [INFO] -[INFO] --- maven-checkstyle-plugin:2.16:check (default) @ queue --- +[INFO] --- maven-checkstyle-plugin:2.17:check (default) @ queue --- [INFO] -[INFO] --- apache-rat-plugin:0.11:check (default) @ queue --- +[INFO] --- apache-rat-plugin:0.12:check (default) @ queue --- [INFO] RAT will not execute since it is configured to be skipped via system property 'rat.skip'. [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ queue --- [INFO] Using 'UTF-8' encoding to copy filtered resources. -[INFO] skip non existing resourceDirectory /work/apache-artemis-1.1.0/examples/features/standard/queue/src/test/resources +[INFO] skip non existing resourceDirectory /home/user/activemq-artemis/examples/features/standard/queue/src/test/resources [INFO] Copying 3 resources [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ queue --- @@ -97,815 +107,739 @@ This is the common output when running an example. On this case taken from the ` [INFO] --- maven-surefire-plugin:2.18.1:test (default-test) @ queue --- [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ queue --- -[INFO] Building jar: /work/apache-artemis-1.1.0/examples/features/standard/queue/target/queue-1.1.0.jar +[INFO] Building jar: /home/user/activemq-artemis/examples/features/standard/queue/target/queue-2.5.0.jar [INFO] [INFO] --- maven-site-plugin:3.3:attach-descriptor (attach-descriptor) @ queue --- [INFO] [INFO] >>> maven-source-plugin:2.2.1:jar (attach-sources) > generate-sources @ queue >>> [INFO] +[INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-maven) @ queue --- +[INFO] [INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-java) @ queue --- [INFO] [INFO] <<< maven-source-plugin:2.2.1:jar (attach-sources) < generate-sources @ queue <<< [INFO] +[INFO] [INFO] --- maven-source-plugin:2.2.1:jar (attach-sources) @ queue --- -[INFO] Building jar: /work/apache-artemis-1.1.0/examples/features/standard/queue/target/queue-1.1.0-sources.jar +[INFO] Building jar: /home/user/activemq-artemis/examples/features/standard/queue/target/queue-2.5.0-sources.jar [INFO] [INFO] >>> maven-source-plugin:2.2.1:jar (default) > generate-sources @ queue >>> [INFO] +[INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-maven) @ queue --- +[INFO] [INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-java) @ queue --- [INFO] [INFO] <<< maven-source-plugin:2.2.1:jar (default) < generate-sources @ queue <<< [INFO] +[INFO] [INFO] --- maven-source-plugin:2.2.1:jar (default) @ queue --- [INFO] -[INFO] --- artemis-maven-plugin:1.1.0:create (create) @ queue --- +[INFO] --- dependency-check-maven:1.4.3:check (default) @ queue --- +[INFO] Skipping dependency-check +[INFO] +[INFO] --- artemis-maven-plugin:2.5.0:create (create) @ queue --- [INFO] Local id: local - url: file:///Users/apacheuser/.m2/repository/ + url: file:///home/user/.m2/repository/ layout: default snapshots: [enabled => true, update => always] releases: [enabled => true, update => always] [INFO] Entries.size 2 -[INFO] ... key=project = MavenProject: org.apache.activemq.examples.broker:queue:1.1.0 @ /work/apache-artemis-1.1.0/examples/features/standard/queue/pom.xml -[INFO] ... key=pluginDescriptor = Component Descriptor: role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisCLIPlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:1.1.0:cli' -role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisCreatePlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:1.1.0:create' -role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisClientPlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:1.1.0:runClient' +[INFO] ... key=project = MavenProject: org.apache.activemq.examples.broker:queue:2.5.0 @ /home/user/activemq-artemis/examples/features/standard/queue/pom.xml +[INFO] ... key=pluginDescriptor = Component Descriptor: role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisCLIPlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:2.5.0:cli' +role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisCreatePlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:2.5.0:create' +role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisDependencyScanPlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:2.5.0:dependency-scan' +role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisClientPlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:2.5.0:runClient' --- -Executing org.apache.activemq.artemis.cli.commands.Create create --allow-anonymous --silent --force --no-web --user guest --password guest --role guest --port-offset 0 --data ./data --allow-anonymous --no-autotune --verbose /work/apache-artemis-1.1.0/examples/features/standard/queue/target/server0 -Home::/work/apache-artemis-1.1.0/examples/features/standard/queue/../../../.., Instance::. -Creating ActiveMQ Artemis instance at: /work/apache-artemis-1.1.0/examples/features/standard/queue/target/server0 +Executing org.apache.activemq.artemis.cli.commands.Create create --allow-anonymous --silent --force --user guest --password guest --role guest --port-offset 0 --data ./data --allow-anonymous --no-web --no-autotune --verbose --aio /home/user/activemq-artemis/examples/features/standard/queue/target/server0 +Home::/home/user/activemq-artemis/examples/features/standard/queue/../../../../artemis-distribution/target/apache-artemis-2.5.0-bin/apache-artemis-2.5.0, Instance::null +Creating ActiveMQ Artemis instance at: /home/user/activemq-artemis/examples/features/standard/queue/target/server0 You can now start the broker by executing: - "/work/apache-artemis-1.1.0/examples/features/standard/queue/target/server0/bin/artemis" run + "/home/user/activemq-artemis/examples/features/standard/queue/target/server0/bin/artemis" run Or you can run the broker in the background using: - "/work/apache-artemis-1.1.0/examples/features/standard/queue/target/server0/bin/artemis-service" start + "/home/user/activemq-artemis/examples/features/standard/queue/target/server0/bin/artemis-service" start [INFO] ################################################################################################### [INFO] create-server0.sh created with commands to reproduce server0 -[INFO] under /work/apache-artemis-1.1.0/examples/features/standard/queue/target +[INFO] under /home/user/activemq-artemis/examples/features/standard/queue/target [INFO] ################################################################################################### [INFO] -[INFO] --- artemis-maven-plugin:1.1.0:cli (start) @ queue --- -[INFO] awaiting server to start +[INFO] --- artemis-maven-plugin:2.5.0:cli (start) @ queue --- [INFO] awaiting server to start server-out: _ _ _ server-out: / \ ____| |_ ___ __ __(_) _____ server-out: / _ \| _ \ __|/ _ \ \/ | |/ __/ server-out: / ___ \ | \/ |_/ __/ |\/| | |\___ \ server-out: /_/ \_\| \__\____|_| |_|_|/___ / -server-out: Apache ActiveMQ Artemis 1.1.0 +server-out: Apache ActiveMQ Artemis 2.5.0 server-out: server-out: -server-out:17:30:25,091 INFO [org.apache.activemq.artemis.integration.bootstrap] AMQ101000: Starting ActiveMQ Artemis Server -server-out:17:30:25,120 INFO [org.apache.activemq.artemis.core.server] AMQ221000: live Message Broker is starting with configuration Broker Configuration (clustered=false,journalDirectory=./data/journal,bindingsDirectory=./data/bindings,largeMessagesDirectory=./data/large-messages,pagingDirectory=./data/paging) -server-out:17:30:25,152 INFO [org.apache.activemq.artemis.core.server] AMQ221013: Using NIO Journal -server-out:17:30:25,195 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-server]. Adding protocol support for: CORE -server-out:17:30:25,199 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-amqp-protocol]. Adding protocol support for: AMQP -server-out:17:30:25,209 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-hornetq-protocol]. Adding protocol support for: HORNETQ -server-out:17:30:25,211 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-mqtt-protocol]. Adding protocol support for: MQTT -server-out:17:30:25,214 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-openwire-protocol]. Adding protocol support for: OPENWIRE -server-out:17:30:25,335 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-stomp-protocol]. Adding protocol support for: STOMP +server-out:2018-03-13 09:06:37,980 WARN [org.apache.activemq.artemis.core.server] AMQ222018: AIO was not located on this platform, it will fall back to using pure Java NIO. If your platform is Linux, install LibAIO to enable the AIO journal +server-out:2018-03-13 09:06:38,052 INFO [org.apache.activemq.artemis.integration.bootstrap] AMQ101000: Starting ActiveMQ Artemis Server +[INFO] awaiting server to start +server-out:2018-03-13 09:06:38,123 INFO [org.apache.activemq.artemis.core.server] AMQ221000: live Message Broker is starting with configuration Broker Configuration (clustered=false,journalDirectory=./data/journal,bindingsDirectory=./data/bindings,largeMessagesDirectory=./data/large-messages,pagingDirectory=./data/paging) +server-out:2018-03-13 09:06:38,146 INFO [org.apache.activemq.artemis.core.server] AMQ221013: Using NIO Journal +server-out:2018-03-13 09:06:38,178 INFO [org.apache.activemq.artemis.core.server] AMQ221057: Global Max Size is being adjusted to 1/2 of the JVM max size (-Xmx). being defined as 1,073,741,824 +server-out:2018-03-13 09:06:38,197 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-server]. Adding protocol support for: CORE +server-out:2018-03-13 09:06:38,198 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-amqp-protocol]. Adding protocol support for: AMQP +server-out:2018-03-13 09:06:38,198 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-hornetq-protocol]. Adding protocol support for: HORNETQ +server-out:2018-03-13 09:06:38,198 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-mqtt-protocol]. Adding protocol support for: MQTT +server-out:2018-03-13 09:06:38,199 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-openwire-protocol]. Adding protocol support for: OPENWIRE +server-out:2018-03-13 09:06:38,199 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-stomp-protocol]. Adding protocol support for: STOMP +server-out:2018-03-13 09:06:38,261 INFO [org.apache.activemq.artemis.core.server] AMQ221034: Waiting indefinitely to obtain live lock +server-out:2018-03-13 09:06:38,262 INFO [org.apache.activemq.artemis.core.server] AMQ221035: Live Server Obtained live lock +server-out:2018-03-13 09:06:38,386 INFO [org.apache.activemq.artemis.core.server] AMQ221003: Deploying queue DLQ on address DLQ +server-out:2018-03-13 09:06:38,445 INFO [org.apache.activemq.artemis.core.server] AMQ221003: Deploying queue ExpiryQueue on address ExpiryQueue [INFO] awaiting server to start -server-out:17:30:25,781 INFO [org.apache.activemq.artemis.core.server] AMQ221003: trying to deploy queue jms.queue.DLQ -server-out:17:30:25,835 INFO [org.apache.activemq.artemis.core.server] AMQ221003: trying to deploy queue jms.queue.ExpiryQueue -server-out:17:30:25,933 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started Acceptor at 0.0.0.0:61616 for protocols [CORE,MQTT,AMQP,HORNETQ,STOMP,OPENWIRE] -server-out:17:30:25,936 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started Acceptor at 0.0.0.0:5445 for protocols [HORNETQ,STOMP] -server-out:17:30:25,939 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started Acceptor at 0.0.0.0:5672 for protocols [AMQP] -server-out:17:30:25,944 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started Acceptor at 0.0.0.0:1883 for protocols [MQTT] -server-out:17:30:25,948 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started Acceptor at 0.0.0.0:61613 for protocols [STOMP] -server-out:17:30:25,949 INFO [org.apache.activemq.artemis.core.server] AMQ221007: Server is now live -server-out:17:30:25,949 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 1.1.0 [nodeID=a855176b-50f0-11e5-937e-2fe9bb000966] +server-out:2018-03-13 09:06:38,739 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started EPOLL Acceptor at 0.0.0.0:61616 for protocols [CORE,MQTT,AMQP,STOMP,HORNETQ,OPENWIRE] +server-out:2018-03-13 09:06:38,741 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started EPOLL Acceptor at 0.0.0.0:5445 for protocols [HORNETQ,STOMP] +server-out:2018-03-13 09:06:38,742 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started EPOLL Acceptor at 0.0.0.0:5672 for protocols [AMQP] +server-out:2018-03-13 09:06:38,744 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started EPOLL Acceptor at 0.0.0.0:1883 for protocols [MQTT] +server-out:2018-03-13 09:06:38,746 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started EPOLL Acceptor at 0.0.0.0:61613 for protocols [STOMP] +server-out:2018-03-13 09:06:38,752 INFO [org.apache.activemq.artemis.core.server] AMQ221007: Server is now live +server-out:2018-03-13 09:06:38,752 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 2.5.0 [0.0.0.0, nodeID=bf1853a1-26c7-11e8-9378-d96702a756ed] [INFO] Server started [INFO] -[INFO] --- artemis-maven-plugin:1.1.0:runClient (runClient) @ queue --- +[INFO] --- artemis-maven-plugin:2.5.0:runClient (runClient) @ queue --- Sent message: This is a text message Received message: This is a text message [INFO] -[INFO] --- artemis-maven-plugin:1.1.0:cli (stop) @ queue --- -server-out:17:30:27,476 INFO [org.apache.activemq.artemis.core.server] AMQ221002: Apache ActiveMQ Artemis Message Broker version 1.0.1-SNA +[INFO] --- artemis-maven-plugin:2.5.0:cli (stop) @ queue --- +server-out:2018-03-13 09:06:40,888 INFO [org.apache.activemq.artemis.core.server] AMQ221002: Apache ActiveMQ Artemis Message Broker version 2.5.0 [bf1853a1-26c7-11e8-9378-d96702a756ed] stopped, uptime 2.786 seconds +server-out:Server stopped! [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ -[INFO] Total time: 7.840 s -[INFO] Finished at: 2015-09-01T17:30:27-04:00 -[INFO] Final Memory: 42M/508M +[INFO] Total time: 6.641 s +[INFO] Finished at: 2018-03-13T09:06:40-05:00 +[INFO] Final Memory: 43M/600M [INFO] ------------------------------------------------------------------------ ``` -List -==== - -This includes a preview list of a few examples that we distribute with Artemis. Please refer to the distribution for a more accurate list. +This includes a preview list of a few examples that we distribute with Artemis. +Please refer to the distribution for a more accurate list. -Applet ------- +## Applet This example shows you how to send and receive JMS messages from an Applet. -Application-Layer Failover --------------------------- +## Application-Layer Failover -Apache ActiveMQ Artemis also supports Application-Layer failover, useful in the case -that replication is not enabled on the server side. +Apache ActiveMQ Artemis also supports Application-Layer failover, useful in the +case that replication is not enabled on the server side. -With Application-Layer failover, it's up to the application to register -a JMS `ExceptionListener` with Apache ActiveMQ Artemis which will be called by Apache ActiveMQ Artemis -in the event that connection failure is detected. +With Application-Layer failover, it's up to the application to register a JMS +`ExceptionListener` with Apache ActiveMQ Artemis which will be called by Apache +ActiveMQ Artemis in the event that connection failure is detected. -The code in the `ExceptionListener` then recreates the JMS connection, -session, etc on another node and the application can continue. +The code in the `ExceptionListener` then recreates the JMS connection, session, +etc on another node and the application can continue. -Application-layer failover is an alternative approach to High -Availability (HA). Application-layer failover differs from automatic -failover in that some client side coding is required in order to -implement this. Also, with Application-layer failover, since the old -session object dies and a new one is created, any uncommitted work in -the old session will be lost, and any unacknowledged messages might be -redelivered. +Application-layer failover is an alternative approach to High Availability +(HA). Application-layer failover differs from automatic failover in that some +client side coding is required in order to implement this. Also, with +Application-layer failover, since the old session object dies and a new one is +created, any uncommitted work in the old session will be lost, and any +unacknowledged messages might be redelivered. -Core Bridge Example -------------------- +## Core Bridge Example -The `bridge` example demonstrates a core bridge deployed on one server, -which consumes messages from a local queue and forwards them to an -address on a second server. +The `bridge` example demonstrates a core bridge deployed on one server, which +consumes messages from a local queue and forwards them to an address on a +second server. -Core bridges are used to create message flows between any two Apache ActiveMQ Artemis -servers which are remotely separated. Core bridges are resilient and -will cope with temporary connection failure allowing them to be an ideal -choice for forwarding over unreliable connections, e.g. a WAN. +Core bridges are used to create message flows between any two Apache ActiveMQ +Artemis servers which are remotely separated. Core bridges are resilient and +will cope with temporary connection failure allowing them to be an ideal choice +for forwarding over unreliable connections, e.g. a WAN. -Browser -------- +## Browser -The `browser` example shows you how to use a JMS `QueueBrowser` with -Apache ActiveMQ Artemis. +The `browser` example shows you how to use a JMS `QueueBrowser` with Apache +ActiveMQ Artemis. -Queues are a standard part of JMS, please consult the JMS 2.0 -specification for full details. +Queues are a standard part of JMS, please consult the JMS 2.0 specification for +full details. -A `QueueBrowser` is used to look at messages on the queue without -removing them. It can scan the entire content of a queue or only -messages matching a message selector. +A `QueueBrowser` is used to look at messages on the queue without removing +them. It can scan the entire content of a queue or only messages matching a +message selector. -Client Kickoff --------------- +## Client Kickoff -The `client-kickoff` example shows how to terminate client connections -given an IP address using the JMX management API. +The `client-kickoff` example shows how to terminate client connections given an +IP address using the JMX management API. -Client side failover listener ------------------------------ +## Client side failover listener -The `client-side-failoverlistener` example shows how to register a -listener to monitor failover events +The `client-side-failoverlistener` example shows how to register a listener to +monitor failover events -Client-Side Load-Balancing --------------------------- +## Client-Side Load-Balancing -The `client-side-load-balancing` example demonstrates how sessions -created from a single JMS `Connection` can be created to different nodes -of the cluster. In other words it demonstrates how Apache ActiveMQ Artemis does -client-side load-balancing of sessions across the cluster. +The `client-side-load-balancing` example demonstrates how sessions created from +a single JMS `Connection` can be created to different nodes of the cluster. In +other words it demonstrates how Apache ActiveMQ Artemis does client-side +load-balancing of sessions across the cluster. -Clustered Durable Subscription ------------------------------- +## Clustered Durable Subscription This example demonstrates a clustered JMS durable subscription -Clustered Grouping ------------------- +## Clustered Grouping -This is similar to the message grouping example except that it -demonstrates it working over a cluster. Messages sent to different nodes -with the same group id will be sent to the same node and the same -consumer. +This is similar to the message grouping example except that it demonstrates it +working over a cluster. Messages sent to different nodes with the same group id +will be sent to the same node and the same consumer. -Clustered Queue ---------------- +## Clustered Queue -The `clustered-queue` example demonstrates a queue deployed on two -different nodes. The two nodes are configured to form a cluster. We then -create a consumer for the queue on each node, and we create a producer -on only one of the nodes. We then send some messages via the producer, -and we verify that both consumers receive the sent messages in a -round-robin fashion. +The `clustered-queue` example demonstrates a queue deployed on two different +nodes. The two nodes are configured to form a cluster. We then create a +consumer for the queue on each node, and we create a producer on only one of +the nodes. We then send some messages via the producer, and we verify that both +consumers receive the sent messages in a round-robin fashion. -Clustering with JGroups ------------------------ +## Clustering with JGroups -The `clustered-jgroups` example demonstrates how to form a two node -cluster using JGroups as its underlying topology discovery technique, -rather than the default UDP broadcasting. We then create a consumer for -the queue on each node, and we create a producer on only one of the -nodes. We then send some messages via the producer, and we verify that -both consumers receive the sent messages in a round-robin fashion. +The `clustered-jgroups` example demonstrates how to form a two node cluster +using JGroups as its underlying topology discovery technique, rather than the +default UDP broadcasting. We then create a consumer for the queue on each node, +and we create a producer on only one of the nodes. We then send some messages +via the producer, and we verify that both consumers receive the sent messages +in a round-robin fashion. -Clustered Standalone --------------------- +## Clustered Standalone -The `clustered-standalone` example demonstrates how to configure and -starts 3 cluster nodes on the same machine to form a cluster. A -subscriber for a JMS topic is created on each node, and we create a -producer on only one of the nodes. We then send some messages via the -producer, and we verify that the 3 subscribers receive all the sent -messages. +The `clustered-standalone` example demonstrates how to configure and starts 3 +cluster nodes on the same machine to form a cluster. A subscriber for a JMS +topic is created on each node, and we create a producer on only one of the +nodes. We then send some messages via the producer, and we verify that the 3 +subscribers receive all the sent messages. -Clustered Static Discovery --------------------------- +## Clustered Static Discovery -This example demonstrates how to configure a cluster using a list of -connectors rather than UDP for discovery +This example demonstrates how to configure a cluster using a list of connectors +rather than UDP for discovery -Clustered Static Cluster One Way --------------------------------- +## Clustered Static Cluster One Way -This example demonstrates how to set up a cluster where cluster -connections are one way, i.e. server A -\> Server B -\> Server C +This example demonstrates how to set up a cluster where cluster connections are +one way, i.e. server A -\> Server B -\> Server C -Clustered Topic ---------------- +## Clustered Topic The `clustered-topic` example demonstrates a JMS topic deployed on two -different nodes. The two nodes are configured to form a cluster. We then -create a subscriber on the topic on each node, and we create a producer -on only one of the nodes. We then send some messages via the producer, -and we verify that both subscribers receive all the sent messages. +different nodes. The two nodes are configured to form a cluster. We then create +a subscriber on the topic on each node, and we create a producer on only one of +the nodes. We then send some messages via the producer, and we verify that both +subscribers receive all the sent messages. -Message Consumer Rate Limiting ------------------------------- +## Message Consumer Rate Limiting -With Apache ActiveMQ Artemis you can specify a maximum consume rate at which a JMS -MessageConsumer will consume messages. This can be specified when -creating or deploying the connection factory. +With Apache ActiveMQ Artemis you can specify a maximum consume rate at which a +JMS MessageConsumer will consume messages. This can be specified when creating +or deploying the connection factory. -If this value is specified then Apache ActiveMQ Artemis will ensure that messages are -never consumed at a rate higher than the specified rate. This is a form -of consumer throttling. +If this value is specified then Apache ActiveMQ Artemis will ensure that +messages are never consumed at a rate higher than the specified rate. This is a +form of consumer throttling. -Dead Letter ------------ +## Dead Letter -The `dead-letter` example shows you how to define and deal with dead -letter messages. Messages can be delivered unsuccessfully (e.g. if the -transacted session used to consume them is rolled back). +The `dead-letter` example shows you how to define and deal with dead letter +messages. Messages can be delivered unsuccessfully (e.g. if the transacted +session used to consume them is rolled back). Such a message goes back to the JMS destination ready to be redelivered. -However, this means it is possible for a message to be delivered again -and again without any success and remain in the destination, clogging -the system. +However, this means it is possible for a message to be delivered again and +again without any success and remain in the destination, clogging the system. To prevent this, messaging systems define dead letter messages: after a -specified unsuccessful delivery attempts, the message is removed from -the destination and put instead in a dead letter destination where they -can be consumed for further investigation. +specified unsuccessful delivery attempts, the message is removed from the +destination and put instead in a dead letter destination where they can be +consumed for further investigation. -Delayed Redelivery ------------------- +## Delayed Redelivery -The `delayed-redelivery` example demonstrates how Apache ActiveMQ Artemis can be -configured to provide a delayed redelivery in the case a message needs -to be redelivered. +The `delayed-redelivery` example demonstrates how Apache ActiveMQ Artemis can +be configured to provide a delayed redelivery in the case a message needs to be +redelivered. -Delaying redelivery can often be useful in the case that clients -regularly fail or roll-back. Without a delayed redelivery, the system -can get into a "thrashing" state, with delivery being attempted, the -client rolling back, and delivery being re-attempted in quick -succession, using up valuable CPU and network resources. +Delaying redelivery can often be useful in the case that clients regularly fail +or roll-back. Without a delayed redelivery, the system can get into a +"thrashing" state, with delivery being attempted, the client rolling back, and +delivery being re-attempted in quick succession, using up valuable CPU and +network resources. -Divert ------- +## Divert -Apache ActiveMQ Artemis diverts allow messages to be transparently "diverted" or copied -from one address to another with just some simple configuration defined -on the server side. +Apache ActiveMQ Artemis diverts allow messages to be transparently "diverted" +or copied from one address to another with just some simple configuration +defined on the server side. -Durable Subscription --------------------- +## Durable Subscription -The `durable-subscription` example shows you how to use a durable -subscription with Apache ActiveMQ Artemis. Durable subscriptions are a standard part of -JMS, please consult the JMS 1.1 specification for full details. +The `durable-subscription` example shows you how to use a durable subscription +with Apache ActiveMQ Artemis. Durable subscriptions are a standard part of JMS, +please consult the JMS 1.1 specification for full details. -Unlike non-durable subscriptions, the key function of durable -subscriptions is that the messages contained in them persist longer than -the lifetime of the subscriber - i.e. they will accumulate messages sent -to the topic even if there is no active subscriber on them. They will -also survive server restarts or crashes. Note that for the messages to -be persisted, the messages sent to them must be marked as durable -messages. +Unlike non-durable subscriptions, the key function of durable subscriptions is +that the messages contained in them persist longer than the lifetime of the +subscriber - i.e. they will accumulate messages sent to the topic even if there +is no active subscriber on them. They will also survive server restarts or +crashes. Note that for the messages to be persisted, the messages sent to them +must be marked as durable messages. -Embedded --------- +## Embedded The `embedded` example shows how to embed a broker within your own code using POJO instantiation and no config files. -Embedded Simple ---------------- +## Embedded Simple The `embedded-simple` example shows how to embed a broker within your own code using regular Apache ActiveMQ Artemis XML files. -Exclusive Queue ---------------- +## Exclusive Queue -The `exlusive-queue` example shows you how to use Exclusive Queues, that -route all messages to only one consumer at a time. +The `exlusive-queue` example shows you how to use exclusive queues, that route +all messages to only one consumer at a time. -Message Expiration ------------------- +## Message Expiration -The `expiry` example shows you how to define and deal with message -expiration. Messages can be retained in the messaging system for a -limited period of time before being removed. JMS specification states -that clients should not receive messages that have been expired (but it -does not guarantee this will not happen). +The `expiry` example shows you how to define and deal with message expiration. +Messages can be retained in the messaging system for a limited period of time +before being removed. JMS specification states that clients should not receive +messages that have been expired (but it does not guarantee this will not +happen). -Apache ActiveMQ Artemis can assign an expiry address to a given queue so that when -messages are expired, they are removed from the queue and sent to the -expiry address. These "expired" messages can later be consumed from the -expiry address for further inspection. +Apache ActiveMQ Artemis can assign an expiry address to a given queue so that +when messages are expired, they are removed from the queue and sent to the +expiry address. These "expired" messages can later be consumed from the expiry +address for further inspection. -Apache ActiveMQ Artemis Resource Adapter example ---------------------------------- +## Apache ActiveMQ Artemis Resource Adapter example -This examples shows how to build the activemq resource adapters a rar -for deployment in other Application Server's +This examples shows how to build the activemq resource adapters a rar for +deployment in other Application Server's -HTTP Transport --------------- +## HTTP Transport -The `http-transport` example shows you how to configure Apache ActiveMQ Artemis to use -the HTTP protocol as its transport layer. +The `http-transport` example shows you how to configure Apache ActiveMQ Artemis +to use the HTTP protocol as its transport layer. -Instantiate JMS Objects Directly --------------------------------- +## Instantiate JMS Objects Directly -Usually, JMS Objects such as `ConnectionFactory`, `Queue` and `Topic` -instances are looked up from JNDI before being used by the client code. -This objects are called "administered objects" in JMS terminology. +Usually, JMS Objects such as `ConnectionFactory`, `Queue` and `Topic` instances +are looked up from JNDI before being used by the client code. This objects are +called "administered objects" in JMS terminology. -However, in some cases a JNDI server may not be available or desired. To -come to the rescue Apache ActiveMQ Artemis also supports the direct instantiation of -these administered objects on the client side so you don't have to use -JNDI for JMS. +However, in some cases a JNDI server may not be available or desired. To come +to the rescue Apache ActiveMQ Artemis also supports the direct instantiation of +these administered objects on the client side so you don't have to use JNDI for +JMS. -Interceptor ------------ +## Interceptor -Apache ActiveMQ Artemis allows an application to use an interceptor to hook into the -messaging system. Interceptors allow you to handle various message +Apache ActiveMQ Artemis allows an application to use an interceptor to hook +into the messaging system. Interceptors allow you to handle various message events in Apache ActiveMQ Artemis. -JAAS ----- +## Interceptor AMQP -The `jaas` example shows you how to configure Apache ActiveMQ Artemis to use JAAS for -security. Apache ActiveMQ Artemis can leverage JAAS to delegate user authentication and -authorization to existing security infrastructure. +Similar to the [Interceptor](#interceptor) example, but using AMQP interceptors. -JMS Auto Closable ------------------ +## Interceptor Client -The `jms-auto-closeable` example shows how JMS resources, such as -connections, sessions and consumers, in JMS 2 can be automatically -closed on error. +Similar to the [Interceptor](#interceptor) example, but using interceptors on +the **client** rather than the broker. -JMS Completion Listener ------------------------ +## Interceptor MQTT + +Similar to the [Interceptor](#interceptor) example, but using MQTT interceptors. + +## JAAS + +The `jaas` example shows you how to configure Apache ActiveMQ Artemis to use +JAAS for security. Apache ActiveMQ Artemis can leverage JAAS to delegate user +authentication and authorization to existing security infrastructure. + +## JMS Auto Closable + +The `jms-auto-closeable` example shows how JMS resources, such as connections, +sessions and consumers, in JMS 2 can be automatically closed on error. + +## JMS Completion Listener The `jms-completion-listener` example shows how to send a message -asynchronously to Apache ActiveMQ Artemis and use a CompletionListener to be notified -of the Broker receiving it. +asynchronously to Apache ActiveMQ Artemis and use a CompletionListener to be +notified of the Broker receiving it. -JMS Bridge ----------- +## JMS Bridge -The `jms-bridge` example shows how to setup a bridge between two -standalone Apache ActiveMQ Artemis servers. +The `jms-bridge` example shows how to setup a bridge between two standalone +Apache ActiveMQ Artemis servers. -JMS Context ------------ +## JMS Context The `jms-context` example shows how to send and receive a message to/from an address/queue using Apache ActiveMQ Artemis by using a JMS Context. -A JMSContext is part of JMS 2.0 and combines the JMS Connection and -Session Objects into a simple Interface. +A JMSContext is part of JMS 2.0 and combines the JMS Connection and Session +Objects into a simple Interface. -JMS Shared Consumer -------------------- +## JMS Shared Consumer -The `jms-shared-consumer` example shows you how can use shared consumers -to share a subscription on a topic. In JMS 1.1 this was not allowed and -so caused a scalability issue. In JMS 2 this restriction has been lifted -so you can share the load across different threads and connections. +The `jms-shared-consumer` example shows you how can use shared consumers to +share a subscription on a topic. In JMS 1.1 this was not allowed and so caused +a scalability issue. In JMS 2 this restriction has been lifted so you can share +the load across different threads and connections. -JMX Management --------------- +## JMX Management The `jmx` example shows how to manage Apache ActiveMQ Artemis using JMX. -Large Message -------------- +## Large Message The `large-message` example shows you how to send and receive very large -messages with Apache ActiveMQ Artemis. Apache ActiveMQ Artemis supports the sending and receiving of -huge messages, much larger than can fit in available RAM on the client -or server. Effectively the only limit to message size is the amount of -disk space you have on the server. +messages with Apache ActiveMQ Artemis. Apache ActiveMQ Artemis supports the +sending and receiving of huge messages, much larger than can fit in available +RAM on the client or server. Effectively the only limit to message size is the +amount of disk space you have on the server. Large messages are persisted on the server so they can survive a server -restart. In other words Apache ActiveMQ Artemis doesn't just do a simple socket stream -from the sender to the consumer. +restart. In other words Apache ActiveMQ Artemis doesn't just do a simple socket +stream from the sender to the consumer. -Last-Value Queue ----------------- +## Last-Value Queue -The `last-value-queue` example shows you how to define and deal with -last-value queues. Last-value queues are special queues which discard -any messages when a newer message with the same value for a well-defined -last-value property is put in the queue. In other words, a last-value -queue only retains the last value. +The `last-value-queue` example shows you how to define and deal with last-value +queues. Last-value queues are special queues which discard any messages when a +newer message with the same value for a well-defined last-value property is put +in the queue. In other words, a last-value queue only retains the last value. -A typical example for last-value queue is for stock prices, where you -are only interested by the latest price for a particular stock. +A typical example for last-value queue is for stock prices, where you are only +interested by the latest price for a particular stock. -Management ----------- +## Management -The `management` example shows how to manage Apache ActiveMQ Artemis using JMS Messages -to invoke management operations on the server. +The `management` example shows how to manage Apache ActiveMQ Artemis using JMS +Messages to invoke management operations on the server. -Management Notification ------------------------ +## Management Notification The `management-notification` example shows how to receive management -notifications from Apache ActiveMQ Artemis using JMS messages. Apache ActiveMQ Artemis servers emit -management notifications when events of interest occur (consumers are -created or closed, addresses are created or deleted, security +notifications from Apache ActiveMQ Artemis using JMS messages. Apache ActiveMQ +Artemis servers emit management notifications when events of interest occur +(consumers are created or closed, addresses are created or deleted, security authentication fails, etc.). -Message Counter ---------------- +## Message Counter -The `message-counters` example shows you how to use message counters to -obtain message information for a queue. +The `message-counters` example shows you how to use message counters to obtain +message information for a queue. -Message Group -------------- +## Message Group -The `message-group` example shows you how to configure and use message -groups with Apache ActiveMQ Artemis. Message groups allow you to pin messages so they -are only consumed by a single consumer. Message groups are sets of -messages that has the following characteristics: +The `message-group` example shows you how to configure and use message groups +with Apache ActiveMQ Artemis. Message groups allow you to pin messages so they +are only consumed by a single consumer. Message groups are sets of messages +that has the following characteristics: -- Messages in a message group share the same group id, i.e. they have - same JMSXGroupID string property values +- Messages in a message group share the same group id, i.e. they have same + JMSXGroupID string property values -- The consumer that receives the first message of a group will receive - all the messages that belongs to the group +- The consumer that receives the first message of a group will receive all the + messages that belongs to the group -Message Group -------------- +## Message Group -The `message-group2` example shows you how to configure and use message -groups with Apache ActiveMQ Artemis via a connection factory. +The `message-group2` example shows you how to configure and use message groups +with Apache ActiveMQ Artemis via a connection factory. -Message Priority ----------------- +## Message Priority -Message Priority can be used to influence the delivery order for -messages. +Message Priority can be used to influence the delivery order for messages. -It can be retrieved by the message's standard header field 'JMSPriority' -as defined in JMS specification version 1.1. +It can be retrieved by the message's standard header field 'JMSPriority' as +defined in JMS specification version 1.1. -The value is of type integer, ranging from 0 (the lowest) to 9 (the -highest). When messages are being delivered, their priorities will -effect their order of delivery. Messages of higher priorities will -likely be delivered before those of lower priorities. +The value is of type integer, ranging from 0 (the lowest) to 9 (the highest). +When messages are being delivered, their priorities will effect their order of +delivery. Messages of higher priorities will likely be delivered before those +of lower priorities. Messages of equal priorities are delivered in the natural order of their -arrival at their destinations. Please consult the JMS 1.1 specification -for full details. +arrival at their destinations. Please consult the JMS 1.1 specification for +full details. -Multiple Failover ------------------ +## Multiple Failover -This example demonstrates how to set up a live server with multiple -backups +This example demonstrates how to set up a live server with multiple backups -Multiple Failover Failback --------------------------- +## Multiple Failover Failback -This example demonstrates how to set up a live server with multiple -backups but forcing failover back to the original live server +This example demonstrates how to set up a live server with multiple backups but +forcing failover back to the original live server -No Consumer Buffering ---------------------- +## No Consumer Buffering -By default, Apache ActiveMQ Artemis consumers buffer messages from the server in a -client side buffer before you actually receive them on the client side. -This improves performance since otherwise every time you called -receive() or had processed the last message in a -`MessageListener onMessage()` method, the Apache ActiveMQ Artemis client would have to -go the server to request the next message, which would then get sent to -the client side, if one was available. +By default, Apache ActiveMQ Artemis consumers buffer messages from the server +in a client side buffer before you actually receive them on the client side. +This improves performance since otherwise every time you called receive() or +had processed the last message in a `MessageListener onMessage()` method, the +Apache ActiveMQ Artemis client would have to go the server to request the next +message, which would then get sent to the client side, if one was available. This would involve a network round trip for every message and reduce -performance. Therefore, by default, Apache ActiveMQ Artemis pre-fetches messages into a -buffer on each consumer. +performance. Therefore, by default, Apache ActiveMQ Artemis pre-fetches +messages into a buffer on each consumer. -In some case buffering is not desirable, and Apache ActiveMQ Artemis allows it to be -switched off. This example demonstrates that. +In some case buffering is not desirable, and Apache ActiveMQ Artemis allows it +to be switched off. This example demonstrates that. -Non-Transaction Failover With Server Data Replication ------------------------------------------------------ +## Non-Transaction Failover With Server Data Replication -The `non-transaction-failover` example demonstrates two servers coupled -as a live-backup pair for high availability (HA), and a client using a -*non-transacted* JMS session failing over from live to backup when the -live server is crashed. +The `non-transaction-failover` example demonstrates two servers coupled as a +live-backup pair for high availability (HA), and a client using a +*non-transacted* JMS session failing over from live to backup when the live +server is crashed. -Apache ActiveMQ Artemis implements failover of client connections between live and -backup servers. This is implemented by the replication of state between -live and backup nodes. When replication is configured and a live node -crashes, the client connections can carry and continue to send and -consume messages. When non-transacted sessions are used, once and only -once message delivery is not guaranteed and it is possible that some -messages will be lost or delivered twice. +Apache ActiveMQ Artemis implements failover of client connections between live +and backup servers. This is implemented by the replication of state between +live and backup nodes. When replication is configured and a live node crashes, +the client connections can carry and continue to send and consume messages. +When non-transacted sessions are used, once and only once message delivery is +not guaranteed and it is possible that some messages will be lost or delivered +twice. -OpenWire --------- +## OpenWire -The `Openwire` example shows how to configure an Apache ActiveMQ Artemis server to -communicate with an Apache ActiveMQ Artemis JMS client that uses open-wire protocol. +The `Openwire` example shows how to configure an Apache ActiveMQ Artemis server +to communicate with an Apache ActiveMQ Artemis JMS client that uses open-wire +protocol. You will find the queue example for open wire, and the chat example. The virtual-topic-mapping examples shows how to map the ActiveMQ 5.x Virtual Topic naming convention to work with the Artemis Address model. -Paging ------- +## Paging -The `paging` example shows how Apache ActiveMQ Artemis can support huge queues even -when the server is running in limited RAM. It does this by transparently +The `paging` example shows how Apache ActiveMQ Artemis can support huge queues +even when the server is running in limited RAM. It does this by transparently *paging* messages to disk, and *depaging* them when they are required. -Pre-Acknowledge ---------------- +## Pre-Acknowledge -Standard JMS supports three acknowledgement modes:` - AUTO_ACKNOWLEDGE`, `CLIENT_ACKNOWLEDGE`, and -`DUPS_OK_ACKNOWLEDGE`. For a full description on these modes please -consult the JMS specification, or any JMS tutorial. +Standard JMS supports three acknowledgement modes:` AUTO_ACKNOWLEDGE`, +`CLIENT_ACKNOWLEDGE`, and `DUPS_OK_ACKNOWLEDGE`. For a full description on +these modes please consult the JMS specification, or any JMS tutorial. -All of these standard modes involve sending acknowledgements from the -client to the server. However in some cases, you really don't mind -losing messages in event of failure, so it would make sense to -acknowledge the message on the server before delivering it to the -client. This example demonstrates how Apache ActiveMQ Artemis allows this with an extra -acknowledgement mode. +All of these standard modes involve sending acknowledgements from the client to +the server. However in some cases, you really don't mind losing messages in +event of failure, so it would make sense to acknowledge the message on the +server before delivering it to the client. This example demonstrates how Apache +ActiveMQ Artemis allows this with an extra acknowledgement mode. -Message Producer Rate Limiting ------------------------------- +## Message Producer Rate Limiting -The `producer-rte-limit` example demonstrates how, with Apache ActiveMQ Artemis, you -can specify a maximum send rate at which a JMS message producer will -send messages. +The `producer-rte-limit` example demonstrates how, with Apache ActiveMQ +Artemis, you can specify a maximum send rate at which a JMS message producer +will send messages. -Queue ------ +## Queue A simple example demonstrating a queue. -Message Redistribution ----------------------- +## Message Redistribution -The `queue-message-redistribution` example demonstrates message -redistribution between queues with the same name deployed in different -nodes of a cluster. +The `queue-message-redistribution` example demonstrates message redistribution +between queues with the same name deployed in different nodes of a cluster. -Queue Requestor ---------------- +## Queue Requestor A simple example demonstrating a JMS queue requestor. -Queue with Message Selector ---------------------------- +## Queue with Message Selector -The `queue-selector` example shows you how to selectively consume -messages using message selectors with queue consumers. +The `queue-selector` example shows you how to selectively consume messages +using message selectors with queue consumers. -Reattach Node example ---------------------- +## Reattach Node example -The `Reattach Node` example shows how a client can try to reconnect to -the same server instead of failing the connection immediately and -notifying any user ExceptionListener objects. Apache ActiveMQ Artemis can be configured -to automatically retry the connection, and reattach to the server when -it becomes available again across the network. +The `Reattach Node` example shows how a client can try to reconnect to the same +server instead of failing the connection immediately and notifying any user +ExceptionListener objects. Apache ActiveMQ Artemis can be configured to +automatically retry the connection, and reattach to the server when it becomes +available again across the network. -Replicated Failback example ---------------------------- +## Replicated Failback example -An example showing how failback works when using replication, In this -example a live server will replicate all its Journal to a backup server -as it updates it. When the live server crashes the backup takes over -from the live server and the client reconnects and carries on from where -it left off. +An example showing how failback works when using replication, In this example a +live server will replicate all its Journal to a backup server as it updates it. +When the live server crashes the backup takes over from the live server and the +client reconnects and carries on from where it left off. -Replicated Failback static example ----------------------------------- +## Replicated Failback static example -An example showing how failback works when using replication, but this -time with static connectors +An example showing how failback works when using replication, but this time +with static connectors -Replicated multiple failover example ------------------------------------- +## Replicated multiple failover example -An example showing how to configure multiple backups when using -replication +An example showing how to configure multiple backups when using replication -Replicated Failover transaction example ---------------------------------------- +## Replicated Failover transaction example -An example showing how failover works with a transaction when using -replication +An example showing how failover works with a transaction when using replication -Request-Reply example ---------------------- +## Request-Reply example A simple example showing the JMS request-response pattern. -Scheduled Message ------------------ +## Scheduled Message -The `scheduled-message` example shows you how to send a scheduled -message to an address/queue with Apache ActiveMQ Artemis. Scheduled messages won't get +The `scheduled-message` example shows you how to send a scheduled message to an +address/queue with Apache ActiveMQ Artemis. Scheduled messages won't get delivered until a specified time in the future. -Security --------- +## Security The `security` example shows you how configure and use role based queue security with Apache ActiveMQ Artemis. -Send Acknowledgements ---------------------- +## Send Acknowledgements -The `send-acknowledgements` example shows you how to use Apache ActiveMQ Artemis's -advanced *asynchronous send acknowledgements* feature to obtain -acknowledgement from the server that sends have been received and -processed in a separate stream to the sent messages. +The `send-acknowledgements` example shows you how to use Apache ActiveMQ +Artemis's advanced *asynchronous send acknowledgements* feature to obtain +acknowledgement from the server that sends have been received and processed in +a separate stream to the sent messages. -Slow Consumer -------------- +## Slow Consumer -The `slow-consumer` example shows you how to detect slow consumers and configure -a slow consumer policy in Apache ActiveMQ Artemis's +The `slow-consumer` example shows you how to detect slow consumers and +configure a slow consumer policy in Apache ActiveMQ Artemis's -Spring Integration ------------------- +## Spring Integration -This example shows how to use embedded JMS using Apache ActiveMQ Artemis's Spring -integration. +This example shows how to use embedded JMS using Apache ActiveMQ Artemis's +Spring integration. -SSL Transport -------------- +## SSL Transport -The `ssl-enabled` shows you how to configure SSL with Apache ActiveMQ Artemis to send -and receive message. +The `ssl-enabled` shows you how to configure SSL with Apache ActiveMQ Artemis +to send and receive message. -Static Message Selector ------------------------ +## Static Message Selector -The `static-selector` example shows you how to configure an Apache ActiveMQ Artemis core -queue with static message selectors (filters). +The `static-selector` example shows you how to configure an Apache ActiveMQ +Artemis core queue with static message selectors (filters). -Static Message Selector Using JMS ---------------------------------- +## Static Message Selector Using JMS -The `static-selector-jms` example shows you how to configure an Apache ActiveMQ Artemis -queue with static message selectors (filters) using JMS. +The `static-selector-jms` example shows you how to configure an Apache ActiveMQ +Artemis queue with static message selectors (filters) using JMS. -Stomp ------ +## Stomp -The `stomp` example shows you how to configure an Apache ActiveMQ Artemis server to send -and receive Stomp messages. +The `stomp` example shows you how to configure an Apache ActiveMQ Artemis +server to send and receive Stomp messages. -Stomp1.1 --------- +## Stomp1.1 -The `stomp` example shows you how to configure an Apache ActiveMQ Artemis server to send -and receive Stomp messages via a Stomp 1.1 connection. +The `stomp` example shows you how to configure an Apache ActiveMQ Artemis +server to send and receive Stomp messages via a Stomp 1.1 connection. -Stomp1.2 --------- +## Stomp1.2 -The `stomp` example shows you how to configure an Apache ActiveMQ Artemis server to send -and receive Stomp messages via a Stomp 1.2 connection. +The `stomp` example shows you how to configure an Apache ActiveMQ Artemis +server to send and receive Stomp messages via a Stomp 1.2 connection. -Stomp Over Web Sockets ----------------------- +## Stomp Over Web Sockets -The `stomp-websockets` example shows you how to configure an Apache ActiveMQ Artemis -server to send and receive Stomp messages directly from Web browsers +The `stomp-websockets` example shows you how to configure an Apache ActiveMQ +Artemis server to send and receive Stomp messages directly from Web browsers (provided they support Web Sockets). -Symmetric Cluster ------------------ +## Symmetric Cluster -The `symmetric-cluster` example demonstrates a symmetric cluster set-up -with Apache ActiveMQ Artemis. +The `symmetric-cluster` example demonstrates a symmetric cluster set-up with +Apache ActiveMQ Artemis. -Apache ActiveMQ Artemis has extremely flexible clustering which allows you to set-up -servers in many different topologies. The most common topology that +Apache ActiveMQ Artemis has extremely flexible clustering which allows you to +set-up servers in many different topologies. The most common topology that you'll perhaps be familiar with if you are used to application server clustering is a symmetric cluster. With a symmetric cluster, the cluster is homogeneous, i.e. each node is -configured the same as every other node, and every node is connected to -every other node in the cluster. +configured the same as every other node, and every node is connected to every +other node in the cluster. -Temporary Queue ---------------- +## Temporary Queue A simple example demonstrating how to use a JMS temporary queue. -Topic ------ +## Topic A simple example demonstrating a JMS topic. -Topic Hierarchy ---------------- +## Topic Hierarchy -Apache ActiveMQ Artemis supports topic hierarchies. With a topic hierarchy you can -register a subscriber with a wild-card and that subscriber will receive -any messages sent to an address that matches the wild card. +Apache ActiveMQ Artemis supports topic hierarchies. With a topic hierarchy you +can register a subscriber with a wild-card and that subscriber will receive any +messages sent to an address that matches the wild card. -Topic Selector 1 ----------------- +## Topic Selector 1 -The `topic-selector-example1` example shows you how to send message to a -JMS Topic, and subscribe them using selectors with Apache ActiveMQ Artemis. +The `topic-selector-example1` example shows you how to send message to a JMS +Topic, and subscribe them using selectors with Apache ActiveMQ Artemis. -Topic Selector 2 ----------------- +## Topic Selector 2 -The `topic-selector-example2` example shows you how to selectively -consume messages using message selectors with topic consumers. +The `topic-selector-example2` example shows you how to selectively consume +messages using message selectors with topic consumers. -Transaction Failover --------------------- +## Transaction Failover The `transaction-failover` example demonstrates two servers coupled as a -live-backup pair for high availability (HA), and a client using a -transacted JMS session failing over from live to backup when the live -server is crashed. +live-backup pair for high availability (HA), and a client using a transacted +JMS session failing over from live to backup when the live server is crashed. -Apache ActiveMQ Artemis implements failover of client connections between live and -backup servers. This is implemented by the sharing of a journal between -the servers. When a live node crashes, the client connections can carry -and continue to send and consume messages. When transacted sessions are -used, once and only once message delivery is guaranteed. +Apache ActiveMQ Artemis implements failover of client connections between live +and backup servers. This is implemented by the sharing of a journal between the +servers. When a live node crashes, the client connections can carry and +continue to send and consume messages. When transacted sessions are used, once +and only once message delivery is guaranteed. -Failover Without Transactions ------------------------------ +## Failover Without Transactions -The `stop-server-failover` example demonstrates failover of the JMS -connection from one node to another when the live server crashes using a -JMS non-transacted session. +The `stop-server-failover` example demonstrates failover of the JMS connection +from one node to another when the live server crashes using a JMS +non-transacted session. -Transactional Session ---------------------- +## Transactional Session -The `transactional` example shows you how to use a transactional Session -with Apache ActiveMQ Artemis. +The `transactional` example shows you how to use a transactional Session with +Apache ActiveMQ Artemis. -XA Heuristic ------------- +## XA Heuristic -The `xa-heuristic` example shows you how to make an XA heuristic -decision through Apache ActiveMQ Artemis Management Interface. A heuristic decision is -a unilateral decision to commit or rollback an XA transaction branch -after it has been prepared. +The `xa-heuristic` example shows you how to make an XA heuristic decision +through Apache ActiveMQ Artemis Management Interface. A heuristic decision is a +unilateral decision to commit or rollback an XA transaction branch after it has +been prepared. -XA Receive ----------- +## XA Receive -The `xa-receive` example shows you how message receiving behaves in an -XA transaction in Apache ActiveMQ Artemis. +The `xa-receive` example shows you how message receiving behaves in an XA +transaction in Apache ActiveMQ Artemis. -XA Send -------- +## XA Send The `xa-send` example shows you how message sending behaves in an XA transaction in Apache ActiveMQ Artemis. diff --git a/docs/user-manual/en/exclusive-queues.md b/docs/user-manual/en/exclusive-queues.md index 3dc751c3ad5..7e086ea174f 100644 --- a/docs/user-manual/en/exclusive-queues.md +++ b/docs/user-manual/en/exclusive-queues.md @@ -3,44 +3,43 @@ Exclusive queues are special queues which route all messages to only one consumer at a time. -This is useful when you want all messages to be processed serially by the same consumer, -when a producer does not specify [Message Grouping](message-grouping.md). +This is useful when you want all messages to be processed serially by the same +consumer, when a producer does not specify [Message Grouping](message-grouping.md). An example might be orders sent to an address and you need to consume them in the exact same order they were produced. -Obviously exclusive queues have a draw back that you cannot scale out the consumers to -improve consumption as only one consumer would technically be active. +Obviously exclusive queues have a draw back that you cannot scale out the +consumers to improve consumption as only one consumer would technically be active. Here we advise that you look at message groups first. ## Configuring Exclusive Queues -Exclusive queues can be statically configured using the `exclusive` boolean property: +Exclusive queues can be statically configured using the `exclusive` boolean +property: ```xml - - - ... -
- - - -
-
-
+
+ + + +
``` -Specified on creating a Queue by using the CORE api specifying the parameter `exclusive` to `true`. +Specified on creating a Queue by using the CORE api specifying the parameter +`exclusive` to `true`. -Or on auto-create when using the JMS Client by using address parameters when creating the destination used by the consumer. +Or on auto-create when using the JMS Client by using address parameters when +creating the destination used by the consumer. ```java Queue queue = session.createQueue("my.destination.name?exclusive=true"); Topic topic = session.createTopic("my.destination.name?exclusive=true"); ``` -Also the default for all queues under and address can be defaulted using the address-setting configuration: +Also the default for all queues under and address can be defaulted using the +`address-setting` configuration: ```xml @@ -48,12 +47,12 @@ Also the default for all queues under and address can be defaulted using the add ``` -By default, `default-exclusive-queue` is `false`. Address wildcards can be used -to configure exclusive queues for a set of addresses (see [here](wildcard-syntax.md)). +By default, `default-exclusive-queue` is `false`. Address +[wildcards](wildcard-syntax.md) can be used to configure exclusive queues for a +set of addresses. ## Example -See `Exclusive Queue` in [examples](examples.md). - -For additional examples see `org.apache.activemq.artemis.tests.integration.jms.client.ExclusiveTest` +See the [exclusive queue example](examples.md#exclusive) which shows how +exclusive queues are configured and used with JMS. diff --git a/docs/user-manual/en/filter-expressions.md b/docs/user-manual/en/filter-expressions.md index 0c9183c9d69..425b5c1b639 100644 --- a/docs/user-manual/en/filter-expressions.md +++ b/docs/user-manual/en/filter-expressions.md @@ -10,19 +10,19 @@ please the JMS javadoc for Filter expressions are used in several places in Apache ActiveMQ Artemis -- Predefined Queues. When pre-defining a queue, in - `broker.xml` in either the core or jms configuration a filter - expression can be defined for a queue. Only messages that match the - filter expression will enter the queue. +- Predefined Queues. When pre-defining a queue, in + `broker.xml` in either the core or jms configuration a filter + expression can be defined for a queue. Only messages that match the + filter expression will enter the queue. -- Core bridges can be defined with an optional filter expression, only - matching messages will be bridged (see [Core Bridges](core-bridges.md)). +- Core bridges can be defined with an optional filter expression, only + matching messages will be bridged (see [Core Bridges](core-bridges.md)). -- Diverts can be defined with an optional filter expression, only - matching messages will be diverted (see [Diverts](diverts.md)). +- Diverts can be defined with an optional filter expression, only + matching messages will be diverted (see [Diverts](diverts.md)). -- Filter are also used programmatically when creating consumers, - queues and in several places as described in [management](management.md). +- Filter are also used programmatically when creating consumers, + queues and in several places as described in [management](management.md). There are some differences between JMS selector expressions and Apache ActiveMQ Artemis core filter expressions. Whereas JMS selector expressions operate on a @@ -31,21 +31,21 @@ JMS message, Apache ActiveMQ Artemis core filter expressions operate on a core m The following identifiers can be used in a core filter expressions to refer to attributes of the core message in an expression: -- `AMQPriority`. To refer to the priority of a message. Message - priorities are integers with valid values from `0 - 9`. `0` is the - lowest priority and `9` is the highest. E.g. - `AMQPriority = 3 AND animal = 'aardvark'` +- `AMQPriority`. To refer to the priority of a message. Message + priorities are integers with valid values from `0 - 9`. `0` is the + lowest priority and `9` is the highest. E.g. + `AMQPriority = 3 AND animal = 'aardvark'` -- `AMQExpiration`. To refer to the expiration time of a message. The - value is a long integer. +- `AMQExpiration`. To refer to the expiration time of a message. The + value is a long integer. -- `AMQDurable`. To refer to whether a message is durable or not. The - value is a string with valid values: `DURABLE` or `NON_DURABLE`. +- `AMQDurable`. To refer to whether a message is durable or not. The + value is a string with valid values: `DURABLE` or `NON_DURABLE`. -- `AMQTimestamp`. The timestamp of when the message was created. The - value is a long integer. +- `AMQTimestamp`. The timestamp of when the message was created. The + value is a long integer. -- `AMQSize`. The size of a message in bytes. The value is an integer. +- `AMQSize`. The size of a message in bytes. The value is an integer. Any other identifiers used in core filter expressions will be assumed to be properties of the message. diff --git a/docs/user-manual/en/flow-control.md b/docs/user-manual/en/flow-control.md index 86654f2b078..44fbee3f166 100644 --- a/docs/user-manual/en/flow-control.md +++ b/docs/user-manual/en/flow-control.md @@ -37,11 +37,11 @@ bytes). The value can be: -- `-1` for an *unbounded* buffer +- `-1` for an *unbounded* buffer -- `0` to not buffer any messages. +- `0` to not buffer any messages. -- `>0` for a buffer with the given maximum size in bytes. +- `>0` for a buffer with the given maximum size in bytes. Setting the consumer window size can considerably improve performance depending on the messaging use case. As an example, let's consider the @@ -106,7 +106,7 @@ control. The default value is `-1`. Please see [the examples chapter](examples.md) for a working example of limiting consumer rate. -> **Note** +> **Note:** > > Rate limited flow control can be used in conjunction with window based > flow control. Rate limited flow control only effects how many messages @@ -198,7 +198,7 @@ to prevent that max size being exceeded. Note the policy must be set to `BLOCK` to enable blocking producer flow control. -> **Note** +> **Note:** > > Note that in the default configuration all addresses are set to block > producers after 10 MiB of message data is in the address. This means @@ -207,7 +207,7 @@ control. > want this behaviour increase the `max-size-bytes` parameter or change > the address full message policy. -> **Note** +> **Note:** > > Producer credits are allocated from the broker to the client. Flow control > credit checking (i.e. checking a producer has enough credit) is done on the diff --git a/docs/user-manual/en/graceful-shutdown.md b/docs/user-manual/en/graceful-shutdown.md index 6c5cba3d007..3869b13c91b 100644 --- a/docs/user-manual/en/graceful-shutdown.md +++ b/docs/user-manual/en/graceful-shutdown.md @@ -1,21 +1,19 @@ # Graceful Server Shutdown -In certain circumstances an administrator might not want to disconnect -all clients immediately when stopping the broker. In this situation the -broker can be configured to shutdown *gracefully* using the -`graceful-shutdown-enabled` boolean configuration parameter. +In certain circumstances an administrator might not want to disconnect all +clients immediately when stopping the broker. In this situation the broker can +be configured to shutdown *gracefully* using the `graceful-shutdown-enabled` +boolean configuration parameter. -When the `graceful-shutdown-enabled` configuration parameter is `true` -and the broker is shutdown it will first prevent any additional clients -from connecting and then it will wait for any existing connections to -be terminated by the client before completing the shutdown process. The -default value is `false`. +When the `graceful-shutdown-enabled` configuration parameter is `true` and the +broker is shutdown it will first prevent any additional clients from connecting +and then it will wait for any existing connections to be terminated by the +client before completing the shutdown process. The default value is `false`. Of course, it's possible a client could keep a connection to the broker -indefinitely effectively preventing the broker from shutting down -gracefully. To deal with this of situation the -`graceful-shutdown-timeout` configuration parameter is available. This -tells the broker (in milliseconds) how long to wait for all clients to -disconnect before forcefully disconnecting the clients and proceeding -with the shutdown process. The default value is `-1` which means the -broker will wait indefinitely for clients to disconnect. +indefinitely effectively preventing the broker from shutting down gracefully. +To deal with this of situation the `graceful-shutdown-timeout` configuration +parameter is available. This tells the broker (in milliseconds) how long to +wait for all clients to disconnect before forcefully disconnecting the clients +and proceeding with the shutdown process. The default value is `-1` which means +the broker will wait indefinitely for clients to disconnect. diff --git a/docs/user-manual/en/ha.md b/docs/user-manual/en/ha.md index 2fc0585e480..e78b1e4bb9c 100644 --- a/docs/user-manual/en/ha.md +++ b/docs/user-manual/en/ha.md @@ -52,14 +52,14 @@ This of course means there will be no Backup Strategy and is the default if none is provided, however this is used to configure `scale-down` which we will cover in a later chapter. -> **Note** +> **Note:** > > The `ha-policy` configurations replaces any current HA configuration > in the root of the `broker.xml` configuration. All old > configuration is now deprecated although best efforts will be made to > honour it if configured this way. -> **Note** +> **Note:** > > Only persistent message data will survive failover. Any non persistent > message data will not be available after failover. @@ -115,7 +115,7 @@ synchronizing the data with its live server. The time it will take for this to happen will depend on the amount of data to be synchronized and the connection speed. -> **Note** +> **Note:** > > In general, synchronization occurs in parallel with current network traffic so > this won't cause any blocking on current clients. However, there is a critical @@ -137,37 +137,37 @@ Cluster Connection also defines how backup servers will find the remote live servers to pair with. Refer to [Clusters](clusters.md) for details on how this is done, and how to configure a cluster connection. Notice that: -- Both live and backup servers must be part of the same cluster. - Notice that even a simple live/backup replicating pair will require - a cluster configuration. +- Both live and backup servers must be part of the same cluster. + Notice that even a simple live/backup replicating pair will require + a cluster configuration. -- Their cluster user and password must match. +- Their cluster user and password must match. Within a cluster, there are two ways that a backup server will locate a live server to replicate from, these are: -- `specifying a node group`. You can specify a group of live servers - that a backup server can connect to. This is done by configuring - `group-name` in either the `master` or the `slave` element of the - `broker.xml`. A Backup server will only connect to a - live server that shares the same node group name +- `specifying a node group`. You can specify a group of live servers + that a backup server can connect to. This is done by configuring + `group-name` in either the `master` or the `slave` element of the + `broker.xml`. A Backup server will only connect to a + live server that shares the same node group name -- `connecting to any live`. This will be the behaviour if `group-name` - is not configured allowing a backup server to connect to any live - server +- `connecting to any live`. This will be the behaviour if `group-name` + is not configured allowing a backup server to connect to any live + server -> **Note** +> **Note:** > > A `group-name` example: suppose you have 5 live servers and 6 backup > servers: > -> - `live1`, `live2`, `live3`: with `group-name=fish` +> - `live1`, `live2`, `live3`: with `group-name=fish` > -> - `live4`, `live5`: with `group-name=bird` +> - `live4`, `live5`: with `group-name=bird` > -> - `backup1`, `backup2`, `backup3`, `backup4`: with `group-name=fish` +> - `backup1`, `backup2`, `backup3`, `backup4`: with `group-name=fish` > -> - `backup5`, `backup6`: with `group-name=bird` +> - `backup5`, `backup6`: with `group-name=bird` > > After joining the cluster the backups with `group-name=fish` will > search for live servers with `group-name=fish` to pair with. Since @@ -183,7 +183,7 @@ until it finds a live server that has no current backup configured. If no live server is available it will wait until the cluster topology changes and repeats the process. -> **Note** +> **Note:** > > This is an important distinction from a shared-store backup, if a > backup starts and does not find a live server, the server will just @@ -240,101 +240,44 @@ The backup server must be similarly configured but as a `slave` The following table lists all the `ha-policy` configuration elements for HA strategy Replication for `master`: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
`check-for-live-server`Whether to check the cluster for a (live) server using our own server ID - when starting up. This option is only necessary for performing 'fail-back' - on replicating servers.
`cluster-name`Name of the cluster configuration to use for replication. This setting is - only necessary if you configure multiple cluster connections. If configured then - the connector configuration of the cluster configuration with this name will be - used when connecting to the cluster to discover if a live server is already running, - see `check-for-live-server`. If unset then the default cluster connections configuration - is used (the first one configured).
`group-name`If set, backup servers will only pair with live servers with matching group-name.
`initial-replication-sync-timeout`The amount of time the replicating server will wait at the completion of the initial - replication process for the replica to acknowledge it has received all the necessary - data. The default is 30,000 milliseconds. Note: during this interval any - journal related operations will be blocked.
+- `check-for-live-server` + + Whether to check the cluster for a (live) server using our own server ID when starting up. This option is only necessary for performing 'fail-back' on replicating servers. + +- `cluster-name` + + Name of the cluster configuration to use for replication. This setting is only necessary if you configure multiple cluster connections. If configured then the connector configuration of the cluster configuration with this name will be used when connecting to the cluster to discover if a live server is already running, see `check-for-live-server`. If unset then the default cluster connections configuration is used (the first one configured). + +- `group-name` + + If set, backup servers will only pair with live servers with matching group-name. + +- `initial-replication-sync-timeout` + + The amount of time the replicating server will wait at the completion of the initial replication process for the replica to acknowledge it has received all the necessary data. The default is 30,000 milliseconds. **Note:** during this interval any journal related operations will be blocked. The following table lists all the `ha-policy` configuration elements for HA strategy Replication for `slave`: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
`cluster-name`Name of the cluster configuration to use for replication. - This setting is only necessary if you configure multiple cluster - connections. If configured then the connector configuration of - the cluster configuration with this name will be used when - connecting to the cluster to discover if a live server is already - running, see `check-for-live-server`. If unset then the default - cluster connections configuration is used (the first one configured)
`group-name`If set, backup servers will only pair with live servers with matching group-name
`max-saved-replicated-journals-size`This specifies how many times a replicated backup server - can restart after moving its files on start. Once there are - this number of backup journal files the server will stop permanently - after if fails back.
`allow-failback`Whether a server will automatically stop when a another places a - request to take over its place. The use case is when the backup has - failed over
`initial-replication-sync-timeout`After failover and the slave has become live, this is - set on the new live server. It represents the amount of time - the replicating server will wait at the completion of the - initial replication process for the replica to acknowledge - it has received all the necessary data. The default is - 30,000 milliseconds. Note: during this interval any - journal related operations will be blocked.
+- `cluster-name` + + Name of the cluster configuration to use for replication. This setting is only necessary if you configure multiple cluster connections. If configured then the connector configuration of the cluster configuration with this name will be used when connecting to the cluster to discover if a live server is already running, see `check-for-live-server`. If unset then the default cluster connections configuration is used (the first one configured) + +- `group-name` + + If set, backup servers will only pair with live servers with matching group-name + +- `max-saved-replicated-journals-size` + + This specifies how many times a replicated backup server can restart after moving its files on start. Once there are this number of backup journal files the server will stop permanently after if fails back. + +- `allow-failback` + + Whether a server will automatically stop when a another places a request to take over its place. The use case is when the backup has failed over + +- `initial-replication-sync-timeout` + + After failover and the slave has become live, this is set on the new live server. It represents the amount of time the replicating server will wait at the completion of the initial replication process for the replica to acknowledge it has received all the necessary data. The default is 30,000 milliseconds. **Note:** during this interval any journal related operations will be blocked. ### Shared Store @@ -402,7 +345,7 @@ In order for live - backup groups to operate properly with a shared store, both servers must have configured the location of journal directory to point to the *same shared location* (as explained in [Configuring the message journal](persistence.md)) -> **Note** +> **Note:** > > todo write something about GFS @@ -504,67 +447,24 @@ automatically by setting the following property in the The following table lists all the `ha-policy` configuration elements for HA strategy shared store for `master`: - - - - - - - - - - - - - - - - - - - - - -
NameDescription
`failover-on-server-shutdown`If set to true then when this server is stopped - normally the backup will become live assuming failover. - If false then the backup server will remain passive. - Note that if false you want failover to occur the you - can use the the management API as explained at [Management](management.md)
`wait-for-activation`If set to true then server startup will wait until it is activated. - If set to false then server startup will be done in the background. - Default is true.
+- `failover-on-server-shutdown` + + If set to true then when this server is stopped normally the backup will become live assuming failover. If false then the backup server will remain passive. Note that if false you want failover to occur the you can use the the management API as explained at [Management](management.md). + +- `wait-for-activation` + + If set to true then server startup will wait until it is activated. If set to false then server startup will be done in the background. Default is true. The following table lists all the `ha-policy` configuration elements for HA strategy Shared Store for `slave`: - - - - - - - - - - - - - - - - - - - - - -
NameDescription
`failover-on-server-shutdown`In the case of a backup that has become live. then - when set to true then when this server is stopped normally - the backup will become liveassuming failover. If false then - the backup server will remain passive. Note that if false - you want failover to occur the you can use the the management - API as explained at [Management](management.md)
`allow-failback`Whether a server will automatically stop when a another - places a request to take over its place. The use case is - when the backup has failed over.
+- `failover-on-server-shutdown` + + In the case of a backup that has become live. then when set to true then when this server is stopped normally the backup will become liveassuming failover. If false then the backup server will remain passive. Note that if false you want failover to occur the you can use the the management API as explained at [Management](management.md). + +- `allow-failback` + + Whether a server will automatically stop when a another places a request to take over its place. The use case is when the backup has failed over. #### Colocated Backup Servers @@ -613,7 +513,7 @@ say 100 (which is the default) and a connector is using port 61616 then this will be set to 5545 for the first server created, 5645 for the second and so on. -> **Note** +> **Note:** > > for INVM connectors and Acceptors the id will have > `colocated_backup_n` appended, where n is the backup server number. @@ -648,40 +548,25 @@ creating server but have the new backups name appended. The following table lists all the `ha-policy` configuration elements for colocated policy: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
`request-backup`If true then the server will request a backup on another node
`backup-request-retries`How many times the live server will try to request a backup, -1 means for ever.
`backup-request-retry-interval`How long to wait for retries between attempts to request a backup server.
`max-backups`How many backups a live server can create
`backup-port-offset`The offset to use for the Connectors and Acceptors when creating a new backup server.
+- `request-backup` + + If true then the server will request a backup on another node + +- `backup-request-retries` + + How many times the live server will try to request a backup, -1 means for ever. + +- `backup-request-retry-interval` + + How long to wait for retries between attempts to request a backup server. + +- `max-backups` + + How many backups a live server can create + +- `backup-port-offset` + + The offset to use for the Connectors and Acceptors when creating a new backup server. ### Scaling Down @@ -814,9 +699,9 @@ be high enough to deal with the time needed to scale down. Apache ActiveMQ Artemis defines two types of client failover: -- Automatic client failover +- Automatic client failover -- Application-level client failover +- Application-level client failover Apache ActiveMQ Artemis also provides 100% transparent automatic reattachment of connections to the same server (e.g. in case of transient network @@ -970,7 +855,7 @@ response will come back. In this case it is not easy for the client to determine whether the transaction commit was actually processed on the live server before failure occurred. -> **Note** +> **Note:** > > If XA is being used either via JMS or through the core API then an > `XAException.XA_RETRY` is thrown. This is to inform Transaction @@ -988,7 +873,7 @@ retried, duplicate detection will ensure that any durable messages resent in the transaction will be ignored on the server to prevent them getting sent more than once. -> **Note** +> **Note:** > > By catching the rollback exceptions and retrying, catching unblocked > calls and enabling duplicate detection, once and only once delivery @@ -1025,28 +910,13 @@ following: JMSException error codes - - - - - - - - - - - - - - - - - - - - - -
Error codeDescription
FAILOVERFailover has occurred and we have successfully reattached or reconnected.
DISCONNECTNo failover has occurred and we are disconnected.
+- `FAILOVER` + + Failover has occurred and we have successfully reattached or reconnected. + +- `DISCONNECT` + + No failover has occurred and we are disconnected. ### Application-Level Failover @@ -1063,7 +933,7 @@ connection failure is detected. In your `ExceptionListener`, you would close your old JMS connections, potentially look up new connection factory instances from JNDI and creating new connections. -For a working example of application-level failover, please see [the examples](examples.md) chapter. +For a working example of application-level failover, please see [the Application-Layer Failover Example](examples.md#application-layer-failover). If you are using the core API, then the procedure is very similar: you would set a `FailureListener` on the core `ClientSession` instances. diff --git a/docs/user-manual/en/images/architecture1.jpg b/docs/user-manual/en/images/architecture1.jpg index d2b9de476269b64c7114bc07f9c9acb404133ecb..170dd5c0a02d2fcee3ff103c8cea775c7359b49d 100644 GIT binary patch literal 76604 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

Gt1V1wA^55c7My7 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

q zsplr3$fTka+3t1BIaBSz{U9}6g{?JRXT6THghoWz{4an5MqiGmZ5I){qsbJ#s7R+% zot?*pF~+=I9}-C;KT!l6-|bOW60YXf+KZl8gm;djoaX8_Quc_5aOeWx1EL4q)>-1b zV9A-vyVY=J;_`zAm=zuNCpm$LdZMN`F9Tt9E%RLYA9eHJ+hu~ehcDOP25@KoHz#Bpj_(U} zXG~BuU&H+Z@NjSGzuZ!kY|;k3zv5-G%oz#K|87(Dk25a_RJKYt#3;}7a8Mj*U+{4w0)^V9RQ=j*F2UaB5W z?B&h=?7(1k&fS+jD4AffAtyL#F=n2+#Qew3gy#8S9FiZf*YQI<;9HM z2U^kb9!M+%oHAex`a0cQH`^+@GNF_NBPJGDF%#lmCJd;jA-TcdKL=F7`Qh>zY&ykY zVeMMJf`8TZe7V}>BrYG3PUx2LN*kbIYeh8%uo%d-E6e+WV8njaixG2AQ#_T1W_-D` z4h=wsn(E(3T6qQx$2*^C$LlMVmmqNvGiZuA0i~5uMgsanp3- zC}Nnuy1|L8-JqXLAQP^ z>a0i4W(gw2z|^ypo$Z$M1hYV?8vxcmbCZR_BtW67U7e18f8i@I9d5Z*1bU}fe(v#< ziw&Ri8}NW?kkT$9T}9KX!jCHey!y7@qEfz~ADi5$<|A;}ob?>zJz9^zz6cYv*NQEM43&Xtw@FfYAzHGI3=a|BfbfqZb$hSp#nq7DP zady(>#DO&L=_t8VTr|NcwluLNzY|244x(%+_@Hk-7Y$(ZNIEPpX&;TFMrZ!|p+X@a zh4URUDhLMu(qwI&;0sVm`Mok+nDr21QaNhOoCjuo`a(}bSaEU-A|s>=CDSOw9vs=+ z9?SxxF7iFF6UzzyqQ>PwjBJ;npMIHtyqK+Wp%|Zc%#2I(v{7>ZZqK`}QAu7pWA@*1NjT;RF@hu6w!mmDEl` zun_c3klXokR|PM+sdEzkY4%~(ppgCs0QQi2ycnALAoEgCoFbTVV=ucO>-3){|064t zfq##BATMpv8{5}+&oIM>F{$po20{=2ubv~lvs&c(?Bn+TkB`V=erCSMx=0#Qg!?}` zYC&HUHbXsB=cL~MizDOX-G_c*1@6U#PfC2j`7-I{C8kgerm^SX@#4veByNyn?B?hN z-;Y&Bvr)F4C8jWrHdMThsim{QxX7&A@7r_0mXKA5-t{-cpD3 z7~4p=gEfo~Rvkr)$W50r%B>|K85?%iU3rTzP^h|#RQ-y2=u80XL7?$ICrbpakY9h* zDsjc-*vZ$8LCu0I$1-MWz zJ%03LOdD_3QBvD5=m-)WC@O}>z;BUGMr=l_ZBVXpsh?2*SjAUhRr}_JHYZdgSE4kW zQb1XyAlRP7-px;pcYIKjI&e(04eKOMxh>VsRJ2887YEAig0BbZ1JVf7$l*m-LmMwE|~12uuh=@rrt2WT_c_SB;uMIvaiKKFYF zT+2D0XYV5DsBo>ot%R+UF)kGJ-u-iEN(x~$J8uYM2sqX{Q?aYHM=W&yh1^;T=@%ec zI2Z`VDr_k0OrHIW&aGtB_&4`Br>gp`hXg7DaRIS#<=!%rg-KI^mPiJ9|9e>M<_=_-snaTO4WN0 z?Sp85@?i?>m!v>u@&d$#A+IOgopz#puk2;=;q-2BHY_l%*URH<;#H>j=BBF5Mlm)m z5+XkSIO%M$K7#70HH%}nUWM0`u;fG|>b|5y3qHY0@v)_Y6SOg`fC3SgZXI1lQh_zZw<4Ldd@IoS>j)dOZxFBC?Yf2&`kr@vDXHm_=bv_miZ zsgqXBA!PR~DsT+L3?;Y!7_>~DPk4oucRf()PSTjtnlSRcAGib(WVN8f#n-ahtXZ3A z{eisK_jrjeih+-1oo}np83;p!K#M+(0nRkH_U>bvsiQ+ zk!Qem1f{rwprgp2 UZP;B;_`Mu*zYP0sH-0Vu4|m0CZ~y=R literal 62657 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 diff --git a/docs/user-manual/en/images/architecture2.jpg b/docs/user-manual/en/images/architecture2.jpg index 391c1c01b3bbd265600c9b03dcca7fa6034ed615..cf30eeb3c187e06c3c27ef79fb74958a1049935b 100644 GIT binary patch literal 27525 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 diff --git a/docs/user-manual/en/images/architecture3.jpg b/docs/user-manual/en/images/architecture3.jpg index 7dccab73ed3ecee43dbaced89d8d9ef1f1fa43cf..8a45d0bcd037c97383a3cc3973615bca8e231c5c 100644 GIT binary patch literal 15699 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 literal 14069 zcmeHNc|26#`@b``8AfJC*=g)#-?Emm??Plrj9n|DtYytk$gYrG3Pok#DN9*GDP@cB zk-bPVzpTP5fKp-3L%E=C54fYz^IYrWP9mp_A}7a(9xk-c-c`*+{|=z9HN}u z`~t$l!u#394~Yp%@d^nGu7iM}P$-N9Mny_WCCEs}DEP|>TL&N@_!f9HcpxSKi~!*w zK-dO=2B+#q0=5%A0U;iEy^m^V4{lC)V0`RL0EPzwz;HY`jxkfSJ@6kR#7(kw-<^

O^m;s%FsB@ZHKltGXk4=NyP6 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 } ``` -Likewise for MQTT protocol, an interceptor must implement the interface `MQTTInterceptor`: +Likewise for MQTT protocol, an interceptor must implement the interface +`MQTTInterceptor`: -```java -package org.apache.activemq.artemis.core.protocol.mqtt; +```java package org.apache.activemq.artemis.core.protocol.mqtt; public interface MQTTInterceptor extends BaseInterceptor { @@ -46,16 +47,14 @@ public interface MQTTInterceptor extends BaseInterceptor The returned boolean value is important: -- if `true` is returned, the process continues normally +- if `true` is returned, the process continues normally -- if `false` is returned, the process is aborted, no other - interceptors will be called and the packet will not be processed - further by the server. +- if `false` is returned, the process is aborted, no other interceptors will be + called and the packet will not be processed further by the server. ## Configuring The Interceptors -Both incoming and outgoing interceptors are configured in -`broker.xml`: +Both incoming and outgoing interceptors are configured in `broker.xml`: ```xml @@ -69,39 +68,41 @@ Both incoming and outgoing interceptors are configured in ``` -See the documentation on [adding runtime dependencies](using-server.md) to +See the documentation on [adding runtime dependencies](using-server.md) to understand how to make your interceptor available to the broker. ## Interceptors on the Client Side -The interceptors can also be run on the Apache ActiveMQ Artemit client side to intercept packets -either sent by the client to the server or by the server to the client. -This is done by adding the interceptor to the `ServerLocator` with the -`addIncomingInterceptor(Interceptor)` or +The interceptors can also be run on the Apache ActiveMQ Artemit client side to +intercept packets either sent by the client to the server or by the server to +the client. This is done by adding the interceptor to the `ServerLocator` with +the `addIncomingInterceptor(Interceptor)` or `addOutgoingInterceptor(Interceptor)` methods. -As noted above, if an interceptor returns `false` then the sending of -the packet is aborted which means that no other interceptors are be -called and the packet is not be processed further by the client. -Typically this process happens transparently to the client (i.e. it has -no idea if a packet was aborted or not). However, in the case of an -outgoing packet that is sent in a `blocking` fashion a -`ActiveMQException` will be thrown to the caller. The exception is -thrown because blocking sends provide reliability and it is considered -an error for them not to succeed. `Blocking` sends occurs when, for +As noted above, if an interceptor returns `false` then the sending of the +packet is aborted which means that no other interceptors are be called and the +packet is not be processed further by the client. Typically this process +happens transparently to the client (i.e. it has no idea if a packet was +aborted or not). However, in the case of an outgoing packet that is sent in a +`blocking` fashion a `ActiveMQException` will be thrown to the caller. The +exception is thrown because blocking sends provide reliability and it is +considered an error for them not to succeed. `Blocking` sends occurs when, for example, an application invokes `setBlockOnNonDurableSend(true)` or -`setBlockOnDurableSend(true)` on its `ServerLocator` or if an -application is using a JMS connection factory retrieved from JNDI that -has either `block-on-durable-send` or `block-on-non-durable-send` set to -`true`. Blocking is also used for packets dealing with transactions -(e.g. commit, roll-back, etc.). The `ActiveMQException` thrown will -contain the name of the interceptor that returned false. +`setBlockOnDurableSend(true)` on its `ServerLocator` or if an application is +using a JMS connection factory retrieved from JNDI that has either +`block-on-durable-send` or `block-on-non-durable-send` set to `true`. Blocking +is also used for packets dealing with transactions (e.g. commit, roll-back, +etc.). The `ActiveMQException` thrown will contain the name of the interceptor +that returned false. + +As on the server, the client interceptor classes (and their dependencies) must +be added to the classpath to be properly instantiated and invoked. -As on the server, the client interceptor classes (and their -dependencies) must be added to the classpath to be properly instantiated -and invoked. +## Examples -## Example +See the following examples which show how to use interceptors: -See [the examples chapter](examples.md) for an example which shows how to use interceptors to add -properties to a message on the server. +- [Interceptor](examples.md#interceptor) +- [Interceptor AMQP](examples.md#interceptor-amqp) +- [Interceptor Client](examples.md#interceptor-client) +- [Interceptor MQTT](examples.md#interceptor-mqtt) diff --git a/docs/user-manual/en/jms-bridge.md b/docs/user-manual/en/jms-bridge.md index f859e7b8c71..327d37f0e84 100644 --- a/docs/user-manual/en/jms-bridge.md +++ b/docs/user-manual/en/jms-bridge.md @@ -2,237 +2,222 @@ Apache ActiveMQ Artemis includes a fully functional JMS message bridge. -The function of the bridge is to consume messages from a source queue or -topic, and send them to a target queue or topic, typically on a -different server. +The function of the bridge is to consume messages from a source queue or topic, +and send them to a target queue or topic, typically on a different server. -> *Notice:* -> The JMS Bridge is not intended as a replacement for transformation and more expert systems such as Camel. -> The JMS Bridge may be useful for fast transfers as this chapter covers, but keep in mind that more complex scenarios requiring transformations will require you to use a more advanced transformation system that will play on use cases that will go beyond Apache ActiveMQ Artemis. - -The source and target servers do not have to be in the same cluster -which makes bridging suitable for reliably sending messages from one -cluster to another, for instance across a WAN, and where the connection -may be unreliable. - -A bridge can be deployed as a standalone application, with Apache ActiveMQ Artemis -standalone server or inside a JBoss AS instance. The source and the -target can be located in the same virtual machine or another one. - -The bridge can also be used to bridge messages from other non Apache ActiveMQ Artemis -JMS servers, as long as they are JMS 1.1 compliant. - -> **Note** +> **Note:** > -> Do not confuse a JMS bridge with a core bridge. A JMS bridge can be -> used to bridge any two JMS 1.1 compliant JMS providers and uses the -> JMS API. A core bridge (described in [Core Bridges](core-bridges.md)) is used to bridge any two -> Apache ActiveMQ Artemis instances and uses the core API. Always use a core bridge if -> you can in preference to a JMS bridge. The core bridge will typically -> provide better performance than a JMS bridge. Also the core bridge can -> provide *once and only once* delivery guarantees without using XA. - -The bridge has built-in resilience to failure so if the source or target -server connection is lost, e.g. due to network failure, the bridge will -retry connecting to the source and/or target until they come back -online. When it comes back online it will resume operation as normal. - -The bridge can be configured with an optional JMS selector, so it will -only consume messages matching that JMS selector - -It can be configured to consume from a queue or a topic. When it -consumes from a topic it can be configured to consume using a non -durable or durable subscription - -Typically, the bridge is deployed by the JBoss Micro Container via a -beans configuration file. This would typically be deployed inside the -JBoss Application Server and the following example shows an example of a -beans file that bridges 2 destinations which are actually on the same -server. - -The JMS Bridge is a simple POJO so can be deployed with most frameworks, -simply instantiate the `org.apache.activemq.artemis.api.jms.bridge.impl.JMSBridgeImpl` +> The JMS Bridge is not intended as a replacement for transformation and more +> expert systems such as Camel. The JMS Bridge may be useful for fast +> transfers as this chapter covers, but keep in mind that more complex +> scenarios requiring transformations will require you to use a more advanced +> transformation system that will play on use cases that will go beyond Apache +> ActiveMQ Artemis. + +The source and target servers do not have to be in the same cluster which makes +bridging suitable for reliably sending messages from one cluster to another, +for instance across a WAN, and where the connection may be unreliable. + +A bridge can be deployed as a standalone application or as a web application +managed by the embedded Jetty instance bootstrapped with Apache ActiveMQ +Artemis. The source and the target can be located in the same virtual machine +or another one. + +The bridge can also be used to bridge messages from other non Apache ActiveMQ +Artemis JMS servers, as long as they are JMS 1.1 compliant. + +> **Note:** +> +> Do not confuse a JMS bridge with a core bridge. A JMS bridge can be used to +> bridge any two JMS 1.1 compliant JMS providers and uses the JMS API. A [core +> bridge](core-bridges.md)) is used to bridge any two Apache ActiveMQ Artemis +> instances and uses the core API. Always use a core bridge if you can in +> preference to a JMS bridge. The core bridge will typically provide better +> performance than a JMS bridge. Also the core bridge can provide *once and +> only once* delivery guarantees without using XA. + +The bridge has built-in resilience to failure so if the source or target server +connection is lost, e.g. due to network failure, the bridge will retry +connecting to the source and/or target until they come back online. When it +comes back online it will resume operation as normal. + +The bridge can be configured with an optional JMS selector, so it will only +consume messages matching that JMS selector + +It can be configured to consume from a queue or a topic. When it consumes from +a topic it can be configured to consume using a non durable or durable +subscription + +The JMS Bridge is a simple POJO so can be deployed with most frameworks, simply +instantiate the `org.apache.activemq.artemis.api.jms.bridge.impl.JMSBridgeImpl` class and set the appropriate parameters. ## JMS Bridge Parameters -The main bean deployed is the `JMSBridge` bean. The bean is configurable -by the parameters passed to its constructor. +The main POJO is the `JMSBridge`. It is is configurable by the parameters +passed to its constructor. -> **Note** -> -> To let a parameter be unspecified (for example, if the authentication -> is anonymous or no message selector is provided), use ` />` for the unspecified parameter value. - -- Source Connection Factory Factory +- Source Connection Factory Factory - This injects the `SourceCFF` bean (also defined in the beans file). - This bean is used to create the *source* `ConnectionFactory` + This injects the `SourceCFF` bean (also defined in the beans file). This + bean is used to create the *source* `ConnectionFactory` -- Target Connection Factory Factory +- Target Connection Factory Factory - This injects the `TargetCFF` bean (also defined in the beans file). - This bean is used to create the *target* `ConnectionFactory` + This injects the `TargetCFF` bean (also defined in the beans file). This + bean is used to create the *target* `ConnectionFactory` -- Source Destination Factory Factory +- Source Destination Factory Factory - This injects the `SourceDestinationFactory` bean (also defined in - the beans file). This bean is used to create the *source* - `Destination` + This injects the `SourceDestinationFactory` bean (also defined in the beans + file). This bean is used to create the *source* `Destination` -- Target Destination Factory Factory +- Target Destination Factory Factory - This injects the `TargetDestinationFactory` bean (also defined in - the beans file). This bean is used to create the *target* - `Destination` + This injects the `TargetDestinationFactory` bean (also defined in the beans + file). This bean is used to create the *target* `Destination` -- Source User Name +- Source User Name - this parameter is the username for creating the *source* connection + this parameter is the username for creating the *source* connection -- Source Password +- Source Password - this parameter is the parameter for creating the *source* connection + this parameter is the parameter for creating the *source* connection -- Target User Name +- Target User Name - this parameter is the username for creating the *target* connection + this parameter is the username for creating the *target* connection -- Target Password +- Target Password - this parameter is the password for creating the *target* connection + this parameter is the password for creating the *target* connection -- Selector +- Selector - This represents a JMS selector expression used for consuming - messages from the source destination. Only messages that match the - selector expression will be bridged from the source to the target - destination + This represents a JMS selector expression used for consuming + messages from the source destination. Only messages that match the + selector expression will be bridged from the source to the target + destination - The selector expression must follow the [JMS selector - syntax](https://docs.oracle.com/javaee/7/api/javax/jms/Message.html) + The selector expression must follow the [JMS selector + syntax](https://docs.oracle.com/javaee/7/api/javax/jms/Message.html) -- Failure Retry Interval +- Failure Retry Interval - This represents the amount of time in ms to wait between trying to - recreate connections to the source or target servers when the bridge - has detected they have failed + This represents the amount of time in ms to wait between trying to recreate + connections to the source or target servers when the bridge has detected they + have failed -- Max Retries +- Max Retries - This represents the number of times to attempt to recreate - connections to the source or target servers when the bridge has - detected they have failed. The bridge will give up after trying this - number of times. `-1` represents 'try forever' + This represents the number of times to attempt to recreate connections to the + source or target servers when the bridge has detected they have failed. The + bridge will give up after trying this number of times. `-1` represents 'try + forever' -- Quality Of Service +- Quality Of Service - This parameter represents the desired quality of service mode + This parameter represents the desired quality of service mode - Possible values are: + Possible values are: - - `AT_MOST_ONCE` + - `AT_MOST_ONCE` - - `DUPLICATES_OK` + - `DUPLICATES_OK` - - `ONCE_AND_ONLY_ONCE` + - `ONCE_AND_ONLY_ONCE` - See Quality Of Service section for a explanation of these modes. + See Quality Of Service section for a explanation of these modes. -- Max Batch Size +- Max Batch Size - This represents the maximum number of messages to consume from the - source destination before sending them in a batch to the target - destination. Its value must `>= 1` + This represents the maximum number of messages to consume from the source + destination before sending them in a batch to the target destination. Its value + must `>= 1` -- Max Batch Time +- Max Batch Time - This represents the maximum number of milliseconds to wait before - sending a batch to target, even if the number of messages consumed - has not reached `MaxBatchSize`. Its value must be `-1` to represent - 'wait forever', or `>= 1` to specify an actual time + This represents the maximum number of milliseconds to wait before sending a + batch to target, even if the number of messages consumed has not reached + `MaxBatchSize`. Its value must be `-1` to represent 'wait forever', or `>= 1` + to specify an actual time -- Subscription Name +- Subscription Name - If the source destination represents a topic, and you want to - consume from the topic using a durable subscription then this - parameter represents the durable subscription name + If the source destination represents a topic, and you want to consume from + the topic using a durable subscription then this parameter represents the + durable subscription name -- Client ID +- Client ID - If the source destination represents a topic, and you want to - consume from the topic using a durable subscription then this - attribute represents the the JMS client ID to use when - creating/looking up the durable subscription + If the source destination represents a topic, and you want to consume from + the topic using a durable subscription then this attribute represents the the + JMS client ID to use when creating/looking up the durable subscription -- Add MessageID In Header +- Add MessageID In Header - If `true`, then the original message's message ID will be appended - in the message sent to the destination in the header - `ACTIVEMQ_BRIDGE_MSG_ID_LIST`. If the message is bridged more than - once, each message ID will be appended. This enables a distributed - request-response pattern to be used + If `true`, then the original message's message ID will be appended in the + message sent to the destination in the header `ACTIVEMQ_BRIDGE_MSG_ID_LIST`. If + the message is bridged more than once, each message ID will be appended. This + enables a distributed request-response pattern to be used - > **Note** - > - > when you receive the message you can send back a response using - > the correlation id of the first message id, so when the original - > sender gets it back it will be able to correlate it. + > **Note:** + > + > when you receive the message you can send back a response using the + > correlation id of the first message id, so when the original sender gets it + > back it will be able to correlate it. -- MBean Server +- MBean Server - To manage the JMS Bridge using JMX, set the MBeanServer where the - JMS Bridge MBean must be registered (e.g. the JVM Platform - MBeanServer or JBoss AS MBeanServer) + To manage the JMS Bridge using JMX, set the MBeanServer where the JMS Bridge + MBean must be registered (e.g. the JVM Platform MBeanServer) -- ObjectName +- ObjectName - If you set the MBeanServer, you also need to set the ObjectName used - to register the JMS Bridge MBean (must be unique) + If you set the MBeanServer, you also need to set the ObjectName used to + register the JMS Bridge MBean (must be unique) The "transactionManager" property points to a JTA transaction manager implementation and should be set if you need to use the 'ONCE_AND_ONCE_ONLY' -Quality of Service. Apache ActiveMQ Artemis doesn't ship with such an implementation, but -if you are running within an Application Server you can inject the Transaction -Manager that is shipped. +Quality of Service. Apache ActiveMQ Artemis doesn't ship with such an +implementation, but if you are running within an Application Server you can +inject the Transaction Manager that is shipped. ## Source and Target Connection Factories -The source and target connection factory factories are used to create -the connection factory used to create the connection for the source or -target server. +The source and target connection factory factories are used to create the +connection factory used to create the connection for the source or target +server. -The configuration example above uses the default implementation provided -by Apache ActiveMQ Artemis that looks up the connection factory using JNDI. For other -Application Servers or JMS providers a new implementation may have to be +The configuration example above uses the default implementation provided by +Apache ActiveMQ Artemis that looks up the connection factory using JNDI. For +other Application Servers or JMS providers a new implementation may have to be provided. This can easily be done by implementing the interface `org.apache.activemq.artemis.jms.bridge.ConnectionFactoryFactory`. ## Source and Target Destination Factories -Again, similarly, these are used to create or lookup up the -destinations. +Again, similarly, these are used to create or lookup up the destinations. -In the configuration example above, we have used the default provided by -Apache ActiveMQ Artemis that looks up the destination using JNDI. +In the configuration example above, we have used the default provided by Apache +ActiveMQ Artemis that looks up the destination using JNDI. A new implementation can be provided by implementing `org.apache.activemq.artemis.jms.bridge.DestinationFactory` interface. ## Quality Of Service -The quality of service modes used by the bridge are described here in -more detail. +The quality of service modes used by the bridge are described here in more +detail. ### AT_MOST_ONCE -With this QoS mode messages will reach the destination from the source -at most once. The messages are consumed from the source and acknowledged -before sending to the destination. Therefore there is a possibility that -if failure occurs between removing them from the source and them -arriving at the destination they could be lost. Hence delivery will -occur at most once. +With this QoS mode messages will reach the destination from the source at most +once. The messages are consumed from the source and acknowledged before sending +to the destination. Therefore there is a possibility that if failure occurs +between removing them from the source and them arriving at the destination they +could be lost. Hence delivery will occur at most once. This mode is available for both durable and non-durable messages. @@ -240,71 +225,51 @@ This mode is available for both durable and non-durable messages. With this QoS mode, the messages are consumed from the source and then acknowledged after they have been successfully sent to the destination. -Therefore there is a possibility that if failure occurs after sending to -the destination but before acknowledging them, they could be sent again -when the system recovers. I.e. the destination might receive duplicates -after a failure. +Therefore there is a possibility that if failure occurs after sending to the +destination but before acknowledging them, they could be sent again when the +system recovers. I.e. the destination might receive duplicates after a failure. This mode is available for both durable and non-durable messages. ### ONCE_AND_ONLY_ONCE -This QoS mode ensures messages will reach the destination from the -source once and only once. (Sometimes this mode is known as "exactly -once"). If both the source and the destination are on the same Apache ActiveMQ Artemis -server instance then this can be achieved by sending and acknowledging -the messages in the same local transaction. If the source and -destination are on different servers this is achieved by enlisting the -sending and consuming sessions in a JTA transaction. The JTA transaction -is controlled by a JTA Transaction Manager which will need to be set -via the settransactionManager method on the Bridge. +This QoS mode ensures messages will reach the destination from the source once +and only once. (Sometimes this mode is known as "exactly once"). If both the +source and the destination are on the same Apache ActiveMQ Artemis server +instance then this can be achieved by sending and acknowledging the messages in +the same local transaction. If the source and destination are on different +servers this is achieved by enlisting the sending and consuming sessions in a +JTA transaction. The JTA transaction is controlled by a JTA Transaction Manager +which will need to be set via the settransactionManager method on the Bridge. This mode is only available for durable messages. -> **Note** +> **Note:** > -> For a specific application it may possible to provide once and only -> once semantics without using the ONCE\_AND\_ONLY\_ONCE QoS level. This -> can be done by using the DUPLICATES\_OK mode and then checking for -> duplicates at the destination and discarding them. Some JMS servers -> provide automatic duplicate message detection functionality, or this -> may be possible to implement on the application level by maintaining a -> cache of received message ids on disk and comparing received messages -> to them. The cache would only be valid for a certain period of time so -> this approach is not as watertight as using ONCE\_AND\_ONLY\_ONCE but -> may be a good choice depending on your specific application. +> For a specific application it may possible to provide once and only once +> semantics without using the ONCE\_AND\_ONLY\_ONCE QoS level. This can be done +> by using the DUPLICATES\_OK mode and then checking for duplicates at the +> destination and discarding them. Some JMS servers provide automatic duplicate +> message detection functionality, or this may be possible to implement on the +> application level by maintaining a cache of received message ids on disk and +> comparing received messages to them. The cache would only be valid for a +> certain period of time so this approach is not as watertight as using +> ONCE\_AND\_ONLY\_ONCE but may be a good choice depending on your specific +> application. ### Time outs and the JMS bridge -There is a possibility that the target or source server will not be -available at some point in time. If this occurs then the bridge will try -`Max Retries` to reconnect every `Failure Retry Interval` milliseconds -as specified in the JMS Bridge definition. - -However since a third party JNDI is used, in this case the JBoss naming -server, it is possible for the JNDI lookup to hang if the network were -to disappear during the JNDI lookup. To stop this from occurring the -JNDI definition can be configured to time out if this occurs. To do this -set the `jnp.timeout` and the `jnp.sotimeout` on the Initial Context -definition. The first sets the connection timeout for the initial -connection and the second the read timeout for the socket. - -> **Note** -> -> Once the initial JNDI connection has succeeded all calls are made -> using RMI. If you want to control the timeouts for the RMI connections -> then this can be done via system properties. JBoss uses Sun's RMI and -> the properties can be found -> [here](https://docs.oracle.com/javase/8/docs/technotes/guides/rmi/sunrmiproperties.html). -> The default connection timeout is 10 seconds and the default read -> timeout is 18 seconds. +There is a possibility that the target or source server will not be available +at some point in time. If this occurs then the bridge will try `Max Retries` to +reconnect every `Failure Retry Interval` milliseconds as specified in the JMS +Bridge definition. -If you implement your own factories for looking up JMS resources then -you will have to bear in mind timeout issues. +If you implement your own factories for looking up JMS resources then you will +have to bear in mind timeout issues. ### Examples -Please see [the examples chapter](examples.md) which shows how to configure and use a JMS Bridge with -JBoss AS to send messages to the source destination and consume them -from the target destination and how to configure and use a JMS Bridge between -two standalone Apache ActiveMQ Artemis servers. +Please see [JMS Bridge Example](examples.md#jms-bridge) which shows how to +programmatically instantiate and configure a JMS Bridge to send messages to the +source destination and consume them from the target destination between two +standalone Apache ActiveMQ Artemis brokers. diff --git a/docs/user-manual/en/jms-core-mapping.md b/docs/user-manual/en/jms-core-mapping.md index d7fe3fc5bfd..21c3d0c3008 100644 --- a/docs/user-manual/en/jms-core-mapping.md +++ b/docs/user-manual/en/jms-core-mapping.md @@ -1,15 +1,15 @@ # Mapping JMS Concepts to the Core API -This chapter describes how JMS destinations are mapped to Apache ActiveMQ Artemis -addresses. +This chapter describes how JMS destinations are mapped to Apache ActiveMQ +Artemis addresses. -Apache ActiveMQ Artemis core is JMS-agnostic. It does not have any concept of a JMS -topic. A JMS topic is implemented in core as an address with name=(the topic name) -and with a MULTICAST routing type with zero or more queues bound to it. Each queue bound to that address -represents a topic subscription. +Apache ActiveMQ Artemis core is JMS-agnostic. It does not have any concept of a +JMS topic. A JMS topic is implemented in core as an address with name=(the +topic name) and with a MULTICAST routing type with zero or more queues bound to +it. Each queue bound to that address represents a topic subscription. -Likewise, a JMS queue is implemented as an address with name=(the JMS queue name) with an ANYCAST routing type assocatied -with it. +Likewise, a JMS queue is implemented as an address with name=(the JMS queue +name) with an ANYCAST routing type associated with it. -Note. That whilst it is possible to configure a JMS topic and queue with the same name, it is not a recommended -configuration for use with cross protocol. +**Note:** While it is possible to configure a JMS topic and queue with the same +name, it is not a recommended configuration for use with cross protocol. \ No newline at end of file diff --git a/docs/user-manual/en/large-messages.md b/docs/user-manual/en/large-messages.md index 0ecb8666b9d..26188af19a8 100644 --- a/docs/user-manual/en/large-messages.md +++ b/docs/user-manual/en/large-messages.md @@ -1,154 +1,128 @@ # Large Messages -Apache ActiveMQ Artemis supports sending and receiving of huge messages, even when the -client and server are running with limited memory. The only realistic -limit to the size of a message that can be sent or consumed is the -amount of disk space you have available. We have tested sending and -consuming messages up to 8 GiB in size with a client and server running -in just 50MiB of RAM! - -To send a large message, the user can set an `InputStream` on a message -body, and when that message is sent, Apache ActiveMQ Artemis will read the -`InputStream`. A `FileInputStream` could be used for example to send a -huge message from a huge file on disk. - -As the `InputStream` is read the data is sent to the server as a stream -of fragments. The server persists these fragments to disk as it receives -them and when the time comes to deliver them to a consumer they are read -back of the disk, also in fragments and sent down the wire. When the -consumer receives a large message it initially receives just the message -with an empty body, it can then set an `OutputStream` on the message to -stream the huge message body to a file on disk or elsewhere. At no time -is the entire message body stored fully in memory, either on the client -or the server. +Apache ActiveMQ Artemis supports sending and receiving of huge messages, even +when the client and server are running with limited memory. The only realistic +limit to the size of a message that can be sent or consumed is the amount of +disk space you have available. We have tested sending and consuming messages up +to 8 GiB in size with a client and server running in just 50MiB of RAM! + +To send a large message, the user can set an `InputStream` on a message body, +and when that message is sent, Apache ActiveMQ Artemis will read the +`InputStream`. A `FileInputStream` could be used for example to send a huge +message from a huge file on disk. + +As the `InputStream` is read the data is sent to the server as a stream of +fragments. The server persists these fragments to disk as it receives them and +when the time comes to deliver them to a consumer they are read back of the +disk, also in fragments and sent down the wire. When the consumer receives a +large message it initially receives just the message with an empty body, it can +then set an `OutputStream` on the message to stream the huge message body to a +file on disk or elsewhere. At no time is the entire message body stored fully +in memory, either on the client or the server. ## Configuring the server -Large messages are stored on a disk directory on the server side, as -configured on the main configuration file. +Large messages are stored on a disk directory on the server side, as configured +on the main configuration file. -The configuration property `large-messages-directory` specifies where -large messages are stored. For JDBC persistence the `large-message-table` -should be configured. +The configuration property `large-messages-directory` specifies where large +messages are stored. For JDBC persistence the `large-message-table` should be +configured. ```xml - ... - /data/large-messages - ... - + ... + /data/large-messages + ... + + ``` -By default the large message directory is `data/largemessages` and `large-message-table` is -configured as "LARGE_MESSAGE_TABLE". +By default the large message directory is `data/largemessages` and +`large-message-table` is configured as "LARGE_MESSAGE_TABLE". -For the best performance we recommend using file store with large messages directory stored -on a different physical volume to the message journal or paging directory. +For the best performance we recommend using file store with large messages +directory stored on a different physical volume to the message journal or +paging directory. ## Configuring the Client -Any message larger than a certain size is considered a large message. -Large messages will be split up and sent in fragments. This is -determined by the URL parameter `minLargeMessageSize` +Any message larger than a certain size is considered a large message. Large +messages will be split up and sent in fragments. This is determined by the URL +parameter `minLargeMessageSize` -> **Note** +> **Note:** > -> Apache ActiveMQ Artemis messages are encoded using 2 bytes per character so if the -> message data is filled with ASCII characters (which are 1 byte) the -> size of the resulting Apache ActiveMQ Artemis message would roughly double. This is -> important when calculating the size of a "large" message as it may -> appear to be less than the `minLargeMessageSize` before it is sent, -> but it then turns into a "large" message once it is encoded. +> Apache ActiveMQ Artemis messages are encoded using 2 bytes per character so +> if the message data is filled with ASCII characters (which are 1 byte) the +> size of the resulting Apache ActiveMQ Artemis message would roughly double. +> This is important when calculating the size of a "large" message as it may +> appear to be less than the `minLargeMessageSize` before it is sent, but it +> then turns into a "large" message once it is encoded. The default value is 100KiB. -[Configuring the transport directly from the client side](configuring-transports.md) -will provide more information on how to instantiate the core session factory -or JMS connection factory. +[Configuring the transport directly from the client +side](configuring-transports.md#configuring-the-transport-directly-from-the-client) +will provide more information on how to instantiate the core session factory or +JMS connection factory. ## Compressed Large Messages You can choose to send large messages in compressed form using `compressLargeMessages` URL parameter. -If you specify the boolean URL parameter `compressLargeMessages` as true, -The system will use the ZIP algorithm to compress the message body as -the message is transferred to the server's side. Notice that there's no -special treatment at the server's side, all the compressing and uncompressing -is done at the client. +If you specify the boolean URL parameter `compressLargeMessages` as true, The +system will use the ZIP algorithm to compress the message body as the message +is transferred to the server's side. Notice that there's no special treatment +at the server's side, all the compressing and uncompressing is done at the +client. -If the compressed size of a large message is below `minLargeMessageSize`, -it is sent to server as regular messages. This means that the message won't -be written into the server's large-message data directory, thus reducing the -disk I/O. +If the compressed size of a large message is below `minLargeMessageSize`, it is +sent to server as regular messages. This means that the message won't be +written into the server's large-message data directory, thus reducing the disk +I/O. ## Streaming large messages -Apache ActiveMQ Artemis supports setting the body of messages using input and output -streams (`java.lang.io`) +Apache ActiveMQ Artemis supports setting the body of messages using input and +output streams (`java.lang.io`) -These streams are then used directly for sending (input streams) and -receiving (output streams) messages. +These streams are then used directly for sending (input streams) and receiving +(output streams) messages. -When receiving messages there are 2 ways to deal with the output stream; -you may choose to block while the output stream is recovered using the -method `ClientMessage.saveOutputStream` or alternatively using the -method `ClientMessage.setOutputstream` which will asynchronously write -the message to the stream. If you choose the latter the consumer must be -kept alive until the message has been fully received. +When receiving messages there are 2 ways to deal with the output stream; you +may choose to block while the output stream is recovered using the method +`ClientMessage.saveOutputStream` or alternatively using the method +`ClientMessage.setOutputstream` which will asynchronously write the message to +the stream. If you choose the latter the consumer must be kept alive until the +message has been fully received. -You can use any kind of stream you like. The most common use case is to -send files stored in your disk, but you could also send things like JDBC -Blobs, `SocketInputStream`, things you recovered from `HTTPRequests` -etc. Anything as long as it implements `java.io.InputStream` for sending -messages or `java.io.OutputStream` for receiving them. +You can use any kind of stream you like. The most common use case is to send +files stored in your disk, but you could also send things like JDBC Blobs, +`SocketInputStream`, things you recovered from `HTTPRequests` etc. Anything as +long as it implements `java.io.InputStream` for sending messages or +`java.io.OutputStream` for receiving them. ### Streaming over Core API -The following table shows a list of methods available at `ClientMessage` -which are also available through JMS by the use of object properties. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionJMS Equivalent
setBodyInputStream(InputStream)Set the InputStream used to read a message body when sending it.JMS_AMQ_InputStream
setOutputStream(OutputStream)Set the OutputStream that will receive the body of a message. This method does not block.JMS_AMQ_OutputStream
saveOutputStream(OutputStream)Save the body of the message to the `OutputStream`. It will block until the entire content is transferred to the `OutputStream`.JMS_AMQ_SaveStream
+The following table shows a list of methods available at `ClientMessage` which +are also available through JMS by the use of object properties. + +Name | Description | JMS Equivalent +---|---|--- +setBodyInputStream(InputStream)|Set the InputStream used to read a message body when sending it.|JMS_AMQ_InputStream +setOutputStream(OutputStream)|Set the OutputStream that will receive the body of a message. This method does not block.|JMS_AMQ_OutputStream +saveOutputStream(OutputStream)|Save the body of the message to the `OutputStream`. It will block until the entire content is transferred to the `OutputStream`.|JMS_AMQ_SaveStream To set the output stream when receiving a core message: ``` java ClientMessage msg = consumer.receive(...); - // This will block here until the stream was transferred msg.saveOutputStream(someOutputStream); @@ -165,16 +139,15 @@ ClientMessage msg = session.createMessage(); msg.setInputStream(dataInputStream); ``` -Notice also that for messages with more than 2GiB the getBodySize() will -return invalid values since this is an integer (which is also exposed to -the JMS API). On those cases you can use the message property -_AMQ_LARGE_SIZE. +Notice also that for messages with more than 2GiB the getBodySize() will return +invalid values since this is an integer (which is also exposed to the JMS API). +On those cases you can use the message property _AMQ_LARGE_SIZE. ### Streaming over JMS -When using JMS, Apache ActiveMQ Artemis maps the streaming methods on the core API (see -ClientMessage API table above) by setting object properties . You can use the method -`Message.setObjectProperty` to set the input and output streams. +When using JMS, Apache ActiveMQ Artemis maps the streaming methods on the core +API (see ClientMessage API table above) by setting object properties . You can +use the method `Message.setObjectProperty` to set the input and output streams. The `InputStream` can be defined through the JMS Object Property JMS_AMQ_InputStream on messages being sent: @@ -215,16 +188,16 @@ using the property JMS_AMQ_OutputStream. messageReceived.setObjectProperty("JMS_AMQ_OutputStream", bufferedOutput); ``` -> **Note** +> **Note:** > > When using JMS, Streaming large messages are only supported on > `StreamMessage` and `BytesMessage`. ### Streaming Alternative -If you choose not to use the `InputStream` or `OutputStream` capability -of Apache ActiveMQ Artemis You could still access the data directly in an alternative -fashion. +If you choose not to use the `InputStream` or `OutputStream` capability of +Apache ActiveMQ Artemis You could still access the data directly in an +alternative fashion. On the Core API just get the bytes of the body as you normally would. @@ -241,6 +214,7 @@ for (int i = 0 ; i < msg.getBodySize(); i += bytes.length) If using JMS API, `BytesMessage` and `StreamMessage` also supports it transparently. + ``` java BytesMessage rm = (BytesMessage)cons.receive(10000); @@ -255,5 +229,5 @@ for (int i = 0; i < rm.getBodyLength(); i += 1024) ## Large message example -Please see the [examples](examples.md) chapter for an example which shows -how large message is configured and used with JMS. +Please see the [Large Message Example](examples.md#large-message) which shows +how large messages are configured and used with JMS. diff --git a/docs/user-manual/en/last-value-queues.md b/docs/user-manual/en/last-value-queues.md index f242ff5210f..ea7cfc9815b 100644 --- a/docs/user-manual/en/last-value-queues.md +++ b/docs/user-manual/en/last-value-queues.md @@ -14,26 +14,26 @@ Last-Value queues can be statically configured via the `last-value` boolean property: ```xml - - - ... -

- - - -
- -
+
+ + + +
``` -Specified on creating a Queue by using the CORE api specifying the parameter `lastValue` to `true`. +Specified on creating a queue by using the CORE api specifying the parameter +`lastValue` to `true`. -Or on auto-create when using the JMS Client by using address parameters when creating the destination used by the consumer. +Or on auto-create when using the JMS Client by using address parameters when +creating the destination used by the consumer. - Queue queue = session.createQueue("my.destination.name?last-value=true"); - Topic topic = session.createTopic("my.destination.name?last-value=true"); +```java +Queue queue = session.createQueue("my.destination.name?last-value=true"); +Topic topic = session.createTopic("my.destination.name?last-value=true"); +``` -Also the default for all queues under and address can be defaulted using the address-setting configuration: +Also the default for all queues under and address can be defaulted using the +`address-setting` configuration: ```xml @@ -45,7 +45,8 @@ By default, `default-last-value-queue` is false. Address wildcards can be used to configure Last-Value queues for a set of addresses (see [here](wildcard-syntax.md)). -Note that address-setting `last-value-queue` config is deprecated, please use `default-last-value-queue` instead. +Note that `address-setting` `last-value-queue` config is deprecated, please use +`default-last-value-queue` instead. ## Last-Value Property @@ -77,5 +78,5 @@ System.out.format("Received message: %s\n", messageReceived.getText()); ## Example -See the [examples](examples.md) chapter for an example which shows how last value queues are configured -and used with JMS. +See the [last-value queue example](examples.md#last-value-queue) which shows +how last value queues are configured and used with JMS. diff --git a/docs/user-manual/en/libaio.md b/docs/user-manual/en/libaio.md index ad4aba5c7ca..51023efca1c 100644 --- a/docs/user-manual/en/libaio.md +++ b/docs/user-manual/en/libaio.md @@ -13,8 +13,8 @@ please see [Persistence](persistence.md). These are the native libraries distributed by Apache ActiveMQ Artemis: -- libartemis-native-64.so - x86 64 bits -- We distributed a 32-bit version until early 2017. While it's not available on the distribution any longer it should still be possible to compile to a 32-bit environment if needed. +- libartemis-native-64.so - x86 64 bits +- We distributed a 32-bit version until early 2017. While it's not available on the distribution any longer it should still be possible to compile to a 32-bit environment if needed. When using libaio, Apache ActiveMQ Artemis will always try loading these files as long as they are on the [library path](using-server.md#library-path) @@ -28,12 +28,15 @@ You can install libaio using the following steps as the root user: Using yum, (e.g. on Fedora or Red Hat Enterprise Linux): - yum install libaio +``` +yum install libaio +``` Using aptitude, (e.g. on Ubuntu or Debian system): - apt-get install libaio - +``` +apt-get install libaio +``` ## Compiling the native libraries @@ -44,26 +47,26 @@ those platforms with the release. ## Compilation dependencies -> **Note** +> **Note:** > > The native layer is only available on Linux. If you are > in a platform other than Linux the native compilation will not work These are the required linux packages to be installed for the compilation to work: -- gcc - C Compiler +- gcc - C Compiler -- gcc-c++ or g++ - Extension to gcc with support for C++ +- gcc-c++ or g++ - Extension to gcc with support for C++ -- libtool - Tool for link editing native libraries +- libtool - Tool for link editing native libraries -- libaio - library to disk asynchronous IO kernel functions +- libaio - library to disk asynchronous IO kernel functions -- libaio-dev - Compilation support for libaio +- libaio-dev - Compilation support for libaio -- cmake +- cmake -- A full JDK installed with the environment variable JAVA\_HOME set to +- A full JDK installed with the environment variable JAVA\_HOME set to its location To perform this installation on RHEL or Fedora, you can simply type this at a command line: @@ -74,7 +77,7 @@ Or on Debian systems: sudo apt-get install libtool gcc-g++ gcc libaio libaio- cmake -> **Note** +> **Note:** > > You could find a slight variation of the package names depending on > the version and Linux distribution. (for example gcc-c++ on Fedora diff --git a/docs/user-manual/en/logging.md b/docs/user-manual/en/logging.md index 9cad81767ce..662218c2b0b 100644 --- a/docs/user-manual/en/logging.md +++ b/docs/user-manual/en/logging.md @@ -7,63 +7,34 @@ the console and to a file. There are 6 loggers available which are as follows: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LoggerLogger Description
org.jboss.loggingLogs any calls not handled by the Apache ActiveMQ Artemis loggers
org.apache.activemq.artemis.core.serverLogs the core server
org.apache.activemq.artemis.utilsLogs utility calls
org.apache.activemq.artemis.journalLogs Journal calls
org.apache.activemq.artemis.jmsLogs JMS calls
org.apache.activemq.artemis.integration.bootstrap Logs bootstrap calls
- - : Global Configuration Properties +Logger | Description +---|--- +org.jboss.logging|Logs any calls not handled by the Apache ActiveMQ Artemis loggers +org.apache.activemq.artemis.core.server|Logs the core server +org.apache.activemq.artemis.utils|Logs utility calls +org.apache.activemq.artemis.journal|Logs Journal calls +org.apache.activemq.artemis.jms|Logs JMS calls +org.apache.activemq.artemis.integration.bootstrap|Logs bootstrap calls + ## Logging in a client or with an Embedded server Firstly, if you want to enable logging on the client side you need to -include the JBoss logging jars in your library. If you are using maven -add the following dependencies. - - - org.jboss.logmanager - jboss-logmanager - 1.5.3.Final - - - org.apache.activemq - activemq-core-client - 1.0.0.Final - +include the JBoss logging jars in your library. If you are using Maven +the simplest way is to use the "all" client jar. + +```xml + + org.jboss.logmanager + jboss-logmanager + 2.0.3.Final + + + org.apache.activemq + activemq-core-client + 2.5.0 + +``` There are 2 properties you need to set when starting your java program, the first is to set the Log Manager to use the JBoss Log Manager, this @@ -74,41 +45,43 @@ The second is to set the location of the logging.properties file to use, this is done via the `-Dlogging.configuration` for instance `-Dlogging.configuration=file:///home/user/projects/myProject/logging.properties`. -> **Note** +> **Note:** > -> The value for this needs to be valid URL +> The `logging.configuration` system property needs to be valid URL The following is a typical `logging.properties for a client` - # Root logger option - loggers=org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms,org.apache.activemq.artemis.ra - - # Root logger level - logger.level=INFO - # Apache ActiveMQ Artemis logger levels - logger.org.apache.activemq.artemis.core.server.level=INFO - logger.org.apache.activemq.artemis.utils.level=INFO - logger.org.apache.activemq.artemis.jms.level=DEBUG - - # Root logger handlers - logger.handlers=FILE,CONSOLE - - # Console handler configuration - handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler - handler.CONSOLE.properties=autoFlush - handler.CONSOLE.level=FINE - handler.CONSOLE.autoFlush=true - handler.CONSOLE.formatter=PATTERN - - # File handler configuration - handler.FILE=org.jboss.logmanager.handlers.FileHandler - handler.FILE.level=FINE - handler.FILE.properties=autoFlush,fileName - handler.FILE.autoFlush=true - handler.FILE.fileName=activemq.log - handler.FILE.formatter=PATTERN - - # Formatter pattern configuration - formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter - formatter.PATTERN.properties=pattern - formatter.PATTERN.pattern=%d{HH:mm:ss,SSS} %-5p [%c] %s%E%n +``` +# Root logger option +loggers=org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms,org.apache.activemq.artemis.ra + +# Root logger level +logger.level=INFO +# Apache ActiveMQ Artemis logger levels +logger.org.apache.activemq.artemis.core.server.level=INFO +logger.org.apache.activemq.artemis.utils.level=INFO +logger.org.apache.activemq.artemis.jms.level=DEBUG + +# Root logger handlers +logger.handlers=FILE,CONSOLE + +# Console handler configuration +handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler +handler.CONSOLE.properties=autoFlush +handler.CONSOLE.level=FINE +handler.CONSOLE.autoFlush=true +handler.CONSOLE.formatter=PATTERN + +# File handler configuration +handler.FILE=org.jboss.logmanager.handlers.FileHandler +handler.FILE.level=FINE +handler.FILE.properties=autoFlush,fileName +handler.FILE.autoFlush=true +handler.FILE.fileName=activemq.log +handler.FILE.formatter=PATTERN + +# Formatter pattern configuration +formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter +formatter.PATTERN.properties=pattern +formatter.PATTERN.pattern=%d{HH:mm:ss,SSS} %-5p [%c] %s%E%n +``` \ No newline at end of file diff --git a/docs/user-manual/en/management-console.md b/docs/user-manual/en/management-console.md index f542a01def3..d0ec2d8eaf5 100644 --- a/docs/user-manual/en/management-console.md +++ b/docs/user-manual/en/management-console.md @@ -4,7 +4,6 @@ Apache ActiveMQ Artemis ships by default with a management console. It is powere Its purpose is to expose the [Management API](management.md "Management API") via a user friendly web ui. - ## Login To access the management console use a browser and go to the URL [http://localhost:8161/console](). @@ -30,29 +29,27 @@ Once logged in you should be presented with a screen similar to. On the top right is small menu area you will see some icons. -- `question mark` This will load the artemis documentation in the console main window -- `person` will provide a drop down menu with -- - `about` this will load an about screen, here you will be able to see and validate versions -- - `log out` self descriptive. +- `question mark` This will load the artemis documentation in the console main window +- `person` will provide a drop down menu with +- `about` this will load an about screen, here you will be able to see and validate versions +- `log out` self descriptive. #### Navigation Tabs Running below the Navigation Menu you will see several default feature tabs. -- `Artemis` This is the core tab for Apache ActiveMQ Artemis specific functionality. The rest of this document will focus on this. +- `Artemis` This is the core tab for Apache ActiveMQ Artemis specific functionality. The rest of this document will focus on this. -- `Connect` This allows you to connect to a remote broker from the same console. +- `Connect` This allows you to connect to a remote broker from the same console. -- `Dashboard` Here you can create and save graphs and tables of metrics available via JMX, a default jvm health dashboard is provided. +- `Dashboard` Here you can create and save graphs and tables of metrics available via JMX, a default jvm health dashboard is provided. -- `JMX` This exposes the raw Jolokia JMX so you can browse/access all the JMX endpoints exposed by the JVM. +- `JMX` This exposes the raw Jolokia JMX so you can browse/access all the JMX endpoints exposed by the JVM. -- `Threads` This allows you to monitor the thread usage and their state. +- `Threads` This allows you to monitor the thread usage and their state. You can install further hawtio plugins if you wish to have further functionality. - - ## Artemis Tab Click `Artemis` in the top navigation bar to see the Artemis specific plugin. (The Artemis tab won't appear if there is no broker in this JVM). The Artemis plugin works very much the same as the JMX plugin however with a focus on interacting with an Artemis broker. @@ -71,8 +68,6 @@ This expands to show the current configured available `addresses`. Under the address you can expand to find the `queues` for the address exposing attributes - - ### Key Operations #### Creating a new Address diff --git a/docs/user-manual/en/management.md b/docs/user-manual/en/management.md index 65b0b53c58e..ca523b7a20e 100644 --- a/docs/user-manual/en/management.md +++ b/docs/user-manual/en/management.md @@ -1,53 +1,56 @@ # Management -Apache ActiveMQ Artemis has an extensive *management API* that allows a user to modify a -server configuration, create new resources (e.g. addresses and queues), -inspect these resources (e.g. how many messages are currently held in a -queue) and interact with it (e.g. to remove messages from a queue). Apache ActiveMQ Artemis -also allows clients to subscribe to management notifications. +Apache ActiveMQ Artemis has an extensive *management API* that allows a user to +modify a server configuration, create new resources (e.g. addresses and +queues), inspect these resources (e.g. how many messages are currently held in +a queue) and interact with it (e.g. to remove messages from a queue). Apache +ActiveMQ Artemis also allows clients to subscribe to management notifications. There are four ways to access Apache ActiveMQ Artemis management API: -- Using JMX -- *JMX* is the standard way to manage Java applications +- Using JMX -- *JMX* is the standard way to manage Java applications -- Using Jolokia -- Jolokia exposes the JMX API of an application through a *REST interface* +- Using Jolokia -- Jolokia exposes the JMX API of an application through a + *REST interface* -- Using the Core Client -- management operations are sent to Apache ActiveMQ Artemis - server using *Core Client messages* +- Using the Core Client -- management operations are sent to Apache ActiveMQ + Artemis server using *Core Client messages* -- Using the Core JMS Client -- management operations are sent to Apache ActiveMQ Artemis - server using *Core JMS Client messages* +- Using any JMS Client -- management operations are sent to Apache ActiveMQ + Artemis server using *JMS Client messages* -Although there are four different ways to manage Apache ActiveMQ Artemis, each API supports -the same functionality. If it is possible to manage a resource using JMX -it is also possible to achieve the same result using Core messages. +Although there are four different ways to manage Apache ActiveMQ Artemis, each +API supports the same functionality. If it is possible to manage a resource +using JMX it is also possible to achieve the same result using Core messages. -Besides the programmatic management interfaces, a *Web Console* and a Command Line -*management utility* are also available to administrators of ActiveMQ Artemis. +Besides these four management interfaces, a [Web Console](management-console.md) +and a Command Line *management utility* are also available to administrators of +ActiveMQ Artemis. -The choice depends on your requirements, your application settings and -your environment to decide which way suits you best. +The choice depends on your requirements, your application settings, and your +environment to decide which way suits you best. -> **Note** +> **Note:** > -> In version 2 of Apache ActiveMQ Artemis the syntax used for MBean Object names has changed significantly due to changes -> in the addressing scheme. See the documentation for each individual resource for details on the new syntax. +> In version 2 of Apache ActiveMQ Artemis the syntax used for MBean Object +> names has changed significantly due to changes in the addressing scheme. See +> the documentation for each individual resource for details on the new syntax. ## The Management API -Regardless of the way you *invoke* management operations, the management -API is the same. +Regardless of the way you *invoke* management operations, the management API is +the same. -For each *managed resource*, there exists a Java interface describing -what operations can be invoked for this type of resource. +For each *managed resource*, there exists a Java interface describing what +operations can be invoked for this type of resource. -To learn about available *management operations*, see the Javadoc -for these interfaces. They are located in the +To learn about available *management operations*, see the Javadoc for these +interfaces. They are located in the `org.apache.activemq.artemis.api.core.management` package and they are named with the word `Control` at the end. -The way to invoke management operations depends on whether JMX, Core -messages, or Core JMS messages are used. +The way to invoke management operations depends on whether JMX, Core messages, +or JMS messages are used. ### Management API @@ -57,224 +60,219 @@ For full details of the API please consult the Javadoc. In summary: The `ActiveMQServerControl` interface is the entry point for broker management. -- Listing, creating, deploying and destroying queues +- Listing, creating, deploying and destroying queues - A list of deployed queues can be retrieved using the - `getQueueNames()` method. + A list of deployed queues can be retrieved using the `getQueueNames()` + method. - Queues can be created or destroyed using the management - operations `createQueue()` or `deployQueue()` or `destroyQueue()` + Queues can be created or destroyed using the management operations + `createQueue()` or `deployQueue()` or `destroyQueue()`. - `createQueue` will fail if the queue already exists while - `deployQueue` will do nothing. + `createQueue` will fail if the queue already exists while `deployQueue` will + do nothing. -- Listing and closing remote connections +- Listing and closing remote connections - Client's remote addresses can be retrieved using - `listRemoteAddresses()`. It is also possible to close the - connections associated with a remote address using the - `closeConnectionsForAddress()` method. + Client's remote addresses can be retrieved using `listRemoteAddresses()`. It + is also possible to close the connections associated with a remote address + using the `closeConnectionsForAddress()` method. - Alternatively, connection IDs can be listed using - `listConnectionIDs()` and all the sessions for a given connection ID - can be listed using `listSessions()`. + Alternatively, connection IDs can be listed using `listConnectionIDs()` and + all the sessions for a given connection ID can be listed using + `listSessions()`. -- Transaction heuristic operations +- Transaction heuristic operations - In case of a server crash, when the server restarts, it it possible - that some transaction requires manual intervention. The - `listPreparedTransactions()` method lists the transactions which are - in the prepared states (the transactions are represented as opaque - Base64 Strings.) To commit or rollback a given prepared transaction, - the `commitPreparedTransaction()` or `rollbackPreparedTransaction()` - method can be used to resolve heuristic transactions. Heuristically - completed transactions can be listed using the - `listHeuristicCommittedTransactions()` and - `listHeuristicRolledBackTransactions` methods. + In case of a server crash, when the server restarts, it it possible that some + transaction requires manual intervention. The `listPreparedTransactions()` + method lists the transactions which are in the prepared states (the + transactions are represented as opaque Base64 Strings.) To commit or rollback a + given prepared transaction, the `commitPreparedTransaction()` or + `rollbackPreparedTransaction()` method can be used to resolve heuristic + transactions. Heuristically completed transactions can be listed using the + `listHeuristicCommittedTransactions()` and + `listHeuristicRolledBackTransactions` methods. -- Enabling and resetting Message counters +- Enabling and resetting Message counters - Message counters can be enabled or disabled using the - `enableMessageCounters()` or `disableMessageCounters()` method. To - reset message counters, it is possible to invoke - `resetAllMessageCounters()` and `resetAllMessageCounterHistories()` - methods. + Message counters can be enabled or disabled using the + `enableMessageCounters()` or `disableMessageCounters()` method. To reset + message counters, it is possible to invoke `resetAllMessageCounters()` and + `resetAllMessageCounterHistories()` methods. -- Retrieving the server configuration and attributes +- Retrieving the server configuration and attributes - The `ActiveMQServerControl` exposes Apache ActiveMQ Artemis server configuration - through all its attributes (e.g. `getVersion()` method to retrieve - the server's version, etc.) + The `ActiveMQServerControl` exposes Apache ActiveMQ Artemis server + configuration through all its attributes (e.g. `getVersion()` method to + retrieve the server's version, etc.) -- Listing, creating and destroying Core bridges and diverts +- Listing, creating and destroying Core bridges and diverts - A list of deployed core bridges (resp. diverts) can be retrieved - using the `getBridgeNames()` (resp. `getDivertNames()`) method. + A list of deployed core bridges (resp. diverts) can be retrieved using the + `getBridgeNames()` (resp. `getDivertNames()`) method. - Core bridges (resp. diverts) can be created or destroyed using the - management operations `createBridge()` and `destroyBridge()` (resp. - `createDivert()` and `destroyDivert()`). + Core bridges (resp. diverts) can be created or destroyed using the management + operations `createBridge()` and `destroyBridge()` (resp. `createDivert()` and + `destroyDivert()`). -- It is possible to stop the server and force failover to occur with - any currently attached clients. +- It is possible to stop the server and force failover to occur with any + currently attached clients. - To do this use the `forceFailover()` operation. + To do this use the `forceFailover()` operation. - > **Note** - > - > Since this method actually stops the server you will probably - > receive some sort of error depending on which management service - > you use to call it. + > **Note:** + > + > Since this method actually stops the server you will probably receive some + > sort of error depending on which management service you use to call it. #### Address Management Individual addresses can be managed using the `AddressControl` interface. -- Modifying roles and permissions for an address +- Modifying roles and permissions for an address - You can add or remove roles associated to a queue using the - `addRole()` or `removeRole()` methods. You can list all the roles - associated to the queue with the `getRoles()` method + You can add or remove roles associated to a queue using the `addRole()` or + `removeRole()` methods. You can list all the roles associated to the queue with + the `getRoles()` method #### Queue Management -The bulk of the management API deals with queues. The -`QueueControl` interface defines the queue management operations. +The bulk of the management API deals with queues. The `QueueControl` interface +defines the queue management operations. -Most of the management operations on queues take either a single message -ID (e.g. to remove a single message) or a filter (e.g. to expire all -messages with a given property.) +Most of the management operations on queues take either a single message ID +(e.g. to remove a single message) or a filter (e.g. to expire all messages with +a given property.) -> **Note** +> **Note:** > -> Passing `null` or an empty string in the `filter` parameter means that -> the management operation will be performed on *all messages* in a queue. +> Passing `null` or an empty string in the `filter` parameter means that the +> management operation will be performed on *all messages* in a queue. -- Expiring, sending to a dead letter address and moving messages +- Expiring, sending to a dead letter address and moving messages - Messages can be expired from a queue by using the `expireMessages()` - method. If an expiry address is defined, messages will be sent to - it, otherwise they are discarded. The queue's expiry address can be - set with the `setExpiryAddress()` method. + Messages can be expired from a queue by using the `expireMessages()` method. + If an expiry address is defined, messages will be sent to it, otherwise they + are discarded. The queue's expiry address can be set with the + `setExpiryAddress()` method. - Messages can also be sent to a dead letter address with the - `sendMessagesToDeadLetterAddress()` method. It returns the number of - messages which are sent to the dead letter address. If a dead letter - address is not defined, message are removed from the queue and - discarded. The queue's dead letter address can be set with the - `setDeadLetterAddress()` method. + Messages can also be sent to a dead letter address with the + `sendMessagesToDeadLetterAddress()` method. It returns the number of messages + which are sent to the dead letter address. If a dead letter address is not + defined, message are removed from the queue and discarded. The queue's dead + letter address can be set with the `setDeadLetterAddress()` method. - Messages can also be moved from a queue to another queue by using - the `moveMessages()` method. + Messages can also be moved from a queue to another queue by using the + `moveMessages()` method. -- Listing and removing messages +- Listing and removing messages - Messages can be listed from a queue by using the `listMessages()` - method which returns an array of `Map`, one `Map` for each message. + Messages can be listed from a queue by using the `listMessages()` method + which returns an array of `Map`, one `Map` for each message. - Messages can also be removed from the queue by using the - `removeMessages()` method which returns a `boolean` for the single - message ID variant or the number of removed messages for the filter - variant. The `removeMessages()` method takes a `filter` argument to - remove only filtered messages. Setting the filter to an empty string - will in effect remove all messages. + Messages can also be removed from the queue by using the `removeMessages()` + method which returns a `boolean` for the single message ID variant or the + number of removed messages for the filter variant. The `removeMessages()` + method takes a `filter` argument to remove only filtered messages. Setting the + filter to an empty string will in effect remove all messages. -- Counting messages +- Counting messages - The number of messages in a queue is returned by the - `getMessageCount()` method. Alternatively, the `countMessages()` - will return the number of messages in the queue which *match a given - filter*. + The number of messages in a queue is returned by the `getMessageCount()` + method. Alternatively, the `countMessages()` will return the number of messages + in the queue which *match a given filter*. -- Changing message priority +- Changing message priority - The message priority can be changed by using the - `changeMessagesPriority()` method which returns a `boolean` for the - single message ID variant or the number of updated messages for the - filter variant. + The message priority can be changed by using the `changeMessagesPriority()` + method which returns a `boolean` for the single message ID variant or the + number of updated messages for the filter variant. -- Message counters +- Message counters - Message counters can be listed for a queue with the - `listMessageCounter()` and `listMessageCounterHistory()` methods - (see Message Counters section). The message counters can also be - reset for a single queue using the `resetMessageCounter()` method. + Message counters can be listed for a queue with the `listMessageCounter()` + and `listMessageCounterHistory()` methods (see Message Counters section). The + message counters can also be reset for a single queue using the + `resetMessageCounter()` method. -- Retrieving the queue attributes +- Retrieving the queue attributes - The `QueueControl` exposes queue settings through its - attributes (e.g. `getFilter()` to retrieve the queue's filter if it - was created with one, `isDurable()` to know whether the queue is - durable or not, etc.) + The `QueueControl` exposes queue settings through its attributes (e.g. + `getFilter()` to retrieve the queue's filter if it was created with one, + `isDurable()` to know whether the queue is durable or not, etc.) -- Pausing and resuming Queues +- Pausing and resuming Queues - The `QueueControl` can pause and resume the underlying queue. When a - queue is paused, it will receive messages but will not deliver them. - When it's resumed, it'll begin delivering the queued messages, if - any. + The `QueueControl` can pause and resume the underlying queue. When a queue is + paused, it will receive messages but will not deliver them. When it's resumed, + it'll begin delivering the queued messages, if any. #### Other Resources Management -Apache ActiveMQ Artemis allows to start and stop its remote resources (acceptors, -diverts, bridges, etc.) so that a server can be taken off line for a -given period of time without stopping it completely (e.g. if other -management operations must be performed such as resolving heuristic -transactions). These resources are: +Apache ActiveMQ Artemis allows to start and stop its remote resources +(acceptors, diverts, bridges, etc.) so that a server can be taken off line for +a given period of time without stopping it completely (e.g. if other management +operations must be performed such as resolving heuristic transactions). These +resources are: -- Acceptors +- Acceptors - They can be started or stopped using the `start()` or. `stop()` - method on the `AcceptorControl` interface. The acceptors parameters - can be retrieved using the `AcceptorControl` attributes (see [Understanding Acceptors](configuring-transports.md)) + They can be started or stopped using the `start()` or. `stop()` method on the + `AcceptorControl` interface. The acceptors parameters can be retrieved using + the `AcceptorControl` attributes (see [Understanding + Acceptors](configuring-transports.md)) -- Diverts +- Diverts - They can be started or stopped using the `start()` or `stop()` - method on the `DivertControl` interface. Diverts - parameters can be retrieved using the `DivertControl` attributes - (see [Diverting and Splitting Message Flows)](diverts.md)) + They can be started or stopped using the `start()` or `stop()` method on the + `DivertControl` interface. Diverts parameters can be retrieved using the + `DivertControl` attributes (see [Diverting and Splitting Message + Flows)](diverts.md)) -- Bridges +- Bridges - They can be started or stopped using the `start()` (resp. `stop()`) - method on the `BridgeControl` interface. Bridges parameters can be retrieved - using the `BridgeControl` attributes (see [Core bridges](core-bridges.md)) + They can be started or stopped using the `start()` (resp. `stop()`) method on + the `BridgeControl` interface. Bridges parameters can be retrieved using the + `BridgeControl` attributes (see [Core bridges](core-bridges.md)) -- Broadcast groups +- Broadcast groups - They can be started or stopped using the `start()` or `stop()` - method on the `BroadcastGroupControl` interface. Broadcast groups - parameters can be retrieved using the `BroadcastGroupControl` - attributes (see [Clusters](clusters.md)) + They can be started or stopped using the `start()` or `stop()` method on the + `BroadcastGroupControl` interface. Broadcast groups parameters can be retrieved + using the `BroadcastGroupControl` attributes (see [Clusters](clusters.md)) -- Cluster connections +- Cluster connections - They can be started or stopped using the `start()` or `stop()` - method on the `ClusterConnectionControl` interface. Cluster - connections parameters can be retrieved using the - `ClusterConnectionControl` attributes (see [Clusters](clusters.md)) + They can be started or stopped using the `start()` or `stop()` method on the + `ClusterConnectionControl` interface. Cluster connections parameters can be + retrieved using the `ClusterConnectionControl` attributes (see + [Clusters](clusters.md)) ## Using Management Via JMX Apache ActiveMQ Artemis can be managed using [JMX](http://www.oracle.com/technetwork/java/javase/tech/javamanagement-140525.html). -The management API is exposed by Apache ActiveMQ Artemis using MBeans interfaces. -Apache ActiveMQ Artemis registers its resources with the domain `org.apache.activemq.artemis`. +The management API is exposed by Apache ActiveMQ Artemis using MBeans +interfaces. Apache ActiveMQ Artemis registers its resources with the domain +`org.apache.activemq.artemis`. -For example, the `ObjectName` to manage the anycast queue `exampleQueue` on the address `exampleAddress` is: +For example, the `ObjectName` to manage the anycast queue `exampleQueue` on the +address `exampleAddress` is: - org.apache.activemq.artemis:broker=,component=addresses,address="exampleAddress",subcomponent=queues,routing-type="anycast",queue="exampleQueue" +``` +org.apache.activemq.artemis:broker=,component=addresses,address="exampleAddress",subcomponent=queues,routing-type="anycast",queue="exampleQueue" +``` and the MBean is: - org.apache.activemq.artemis.api.core.management.QueueControl +``` +org.apache.activemq.artemis.api.core.management.QueueControl +``` The MBean `ObjectName`'s are built using the helper class `org.apache.activemq.artemis.api.core.management.ObjectNameBuilder`. You can -also use `jconsole` to find the `ObjectName` of the MBean you want to -manage. +also use `jconsole` to find the `ObjectName` of the MBean you want to manage. Example usage of the `ObjectNameBuilder` to obtain `ActiveMQServerControl`'s name: @@ -284,39 +282,43 @@ objectNameBuilder = ObjectNameBuilder.create(ArtemisResolver.DEFAULT_DOMAIN, bro serverObjectName = objectNameBuilder.getActiveMQServerObjectName() ``` -Managing Apache ActiveMQ Artemis using JMX is identical to management of any Java -Applications using JMX. It can be done by reflection or by creating +Managing Apache ActiveMQ Artemis using JMX is identical to management of any +Java Applications using JMX. It can be done by reflection or by creating proxies of the MBeans. ### Configuring JMX -By default, JMX is enabled to manage Apache ActiveMQ Artemis. It can be disabled by -setting `jmx-management-enabled` to `false` in -`broker.xml`: +By default, JMX is enabled to manage Apache ActiveMQ Artemis. It can be +disabled by setting `jmx-management-enabled` to `false` in `broker.xml`: - - false - +```xml + +false +``` + #### Role Based Authorisation for JMX -Although by default Artemis uses the Java Virtual Machine's `Platform MBeanServer` -this is guarded using role based authentication that leverages Artemis's JAAS plugin support. -This is configured via the `authorisation` element in the `management.xml` configuration file -and can be used to restrict access to attributes and methods on mbeans. +Although by default Artemis uses the Java Virtual Machine's `Platform +MBeanServer` this is guarded using role based authentication that leverages +Artemis's JAAS plugin support. This is configured via the `authorisation` +element in the `management.xml` configuration file and can be used to restrict +access to attributes and methods on mbeans. -There are 3 elements within the `authorisation` element, `whitelist`, `default-access` and -`role-access`, Lets discuss each in turn. +There are 3 elements within the `authorisation` element, `whitelist`, +`default-access` and `role-access`, Lets discuss each in turn. -Whitelist contains a list of mBeans that will by pass the authentication, this is typically -used for any mbeans that are needed by the console to run etc. The default configuration is: +Whitelist contains a list of mBeans that will by pass the authentication, this +is typically used for any mbeans that are needed by the console to run etc. The +default configuration is: ```xml ``` -This means that any mbean with the domain `hawtio` will be allowed access without authorisation. for instance -`hawtio:plugin=artemis`. You can also use wildcards for the mBean properties so the following would also match. +This means that any mbean with the domain `hawtio` will be allowed access +without authorisation. for instance `hawtio:plugin=artemis`. You can also use +wildcards for the mBean properties so the following would also match. ```xml @@ -324,32 +326,35 @@ This means that any mbean with the domain `hawtio` will be allowed access withou ``` -The `role-access`defines how roles are mapped to particular mBeans and its attributes and methods, -the default configuration looks like: +The `role-access`defines how roles are mapped to particular mBeans and its +attributes and methods, the default configuration looks like: ```xml - - - - - - - + + + + + + + ``` -This contains 1 match and will be applied to any mBean that has the domain `org.apache.activemq.artemis`. -Any access to any mBeans that have this domain are controlled by the `access` elements which contain a -method and a set of roles. The method being invoked will be used to pick the closest matching method and -the roles for this will be applied for access. For instance if you try the invoke a method called `listMessages` on an mBean -with the `org.apache.activemq.artemis` domain then this would match the `access` with the method of `list*`. -You could also explicitly configure this by using the full method name, like so: +This contains 1 match and will be applied to any mBean that has the domain +`org.apache.activemq.artemis`. Any access to any mBeans that have this domain +are controlled by the `access` elements which contain a method and a set of +roles. The method being invoked will be used to pick the closest matching +method and the roles for this will be applied for access. For instance if you +try the invoke a method called `listMessages` on an mBean with the +`org.apache.activemq.artemis` domain then this would match the `access` with +the method of `list*`. You could also explicitly configure this by using the +full method name, like so: ```xml ``` -You can also match specific mBeans within a domain by adding a key attribute that is used to match one of the properties -on the mBean, like: +You can also match specific mBeans within a domain by adding a key attribute +that is used to match one of the properties on the mBean, like: ```xml @@ -360,9 +365,11 @@ on the mBean, like: ``` -You could also match a specific queue for instance : +You could also match a specific queue for instance: -`org.apache.activemq.artemis:broker=,component=addresses,address="exampleAddress",subcomponent=queues,routing-type="anycast",queue="exampleQueue"` +``` +org.apache.activemq.artemis:broker=,component=addresses,address="exampleAddress",subcomponent=queues,routing-type="anycast",queue="exampleQueue" +``` by configuring: @@ -376,85 +383,111 @@ by configuring: ``` -Access to JMX mBean attributes are converted to method calls so these are controlled via the `set*`, `get*` and `is*`. -The `*` access is the catch all for everything other method that isn't specifically matched. +Access to JMX mBean attributes are converted to method calls so these are +controlled via the `set*`, `get*` and `is*`. The `*` access is the catch all +for everything other method that isn't specifically matched. -The `default-access` element is basically the catch all for every method call that isn't handled via the `role-access` configuration. -This has teh same semantics as a `match` element. +The `default-access` element is basically the catch all for every method call +that isn't handled via the `role-access` configuration. This has teh same +semantics as a `match` element. -> **Note** +> **Note:** > -> If JMX is enabled, Apache ActiveMQ Artemis can *not* be managed locally using `jconsole` when connecting as a local process, -> this is because jconsole does not using any authentication when connecting this way. If you want to use jconsole you will -either have to disable authentication, by removing the `authentication` element or enable remote access. +> If JMX is enabled, Apache ActiveMQ Artemis can *not* be managed locally using +> `jconsole` when connecting as a local process, this is because jconsole does +> not using any authentication when connecting this way. If you want to use +> jconsole you will either have to disable authentication, by removing the +> `authentication` element or enable remote access. #### Configuring remote JMX Access By default remote JMX access to Artemis is disabled for security reasons. -Artemis has a JMX agent which allows access to JMX mBeans remotely. This is configured via the `connector` element in the -`management.xml` configuration file. To enable this you simply add the following xml: +Artemis has a JMX agent which allows access to JMX mBeans remotely. This is +configured via the `connector` element in the `management.xml` configuration +file. To enable this you simply add the following xml: ```xml ``` -This exposes the agent remotely on the port 1099. If you were connecting via jconsole you would connect as a remote process -using the service url `service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi` and an appropriate user name and password. +This exposes the agent remotely on the port 1099. If you were connecting via +jconsole you would connect as a remote process using the service url +`service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi` and an appropriate user +name and password. You can also configure the connector using the following: -- connector-host +- `connector-host` - The host to expose the agent on -- connector-port + The host to expose the agent on. + +- `connector-port` - The port to expose the agent on -- jmx-realm + The port to expose the agent on. - The jmx realm to use for authentication, defaults to `activemq` to match the JAAS configuration. -- object-name +- `jmx-realm` - The object name to expose the remote connector on, default is `connector:name=rmi` -- secured + The jmx realm to use for authentication, defaults to `activemq` to match the + JAAS configuration. + +- `object-name` - Whether the connector is secured using SSL -- key-store-path + The object name to expose the remote connector on; default is + `connector:name=rmi`. + +- `secured` - The location of the keystore -- key-store-password + Whether the connector is secured using SSL. + +- `key-store-path` - The keystore password -- key-store-provider - The provider, JKS by default -- trust-store-path + The location of the keystore. - The location of the truststore -- trust-store-password +- `key-store-password` - The trustore password -- trust-store-provider + The keystore password. - The provider, JKS by default +- `key-store-provider` -> **Note** + The provider; `JKS` by default. + +- `trust-store-path` + + The location of the truststore. + +- `trust-store-password` + + The trustore password. + +- `trust-store-provider` + + The provider; `JKS` by default. + +> **Note:** > -> It is important to note that the rmi registry will pick an ip address to bind to, If you have a multi IP addresses/NICs -> present on the system then you can choose the ip address to use by adding the following to artemis.profile +> It is important to note that the rmi registry will pick an ip address to bind +> to, If you have a multi IP addresses/NICs present on the system then you can +> choose the ip address to use by adding the following to artemis.profile > `-Djava.rmi.server.hostname=localhost` -> **Note** +> **Note:** > -> Remote connections using the default JVM Agent not enabled by default as Artemis exposes the mBean Server via its own configuration. -> This is so Artemis can leverage the JAAS authentication layer via JMX. If you want to expose this then you will need to -> disable both the connector and the authorisation by removing them from the `management.xml` configuration. -> Please refer to [Java Management guide](https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html) -> to configure the server for remote management (system properties must be set in `artemis.profile`). - -By default, Apache ActiveMQ Artemis server uses the JMX domain "org.apache.activemq.artemis". -To manage several Apache ActiveMQ Artemis servers from the *same* MBeanServer, the JMX -domain can be configured for each individual Apache ActiveMQ Artemis server by setting -`jmx-domain` in `broker.xml`: +> Remote connections using the default JVM Agent not enabled by default as +> Artemis exposes the mBean Server via its own configuration. This is so +> Artemis can leverage the JAAS authentication layer via JMX. If you want to +> expose this then you will need to disable both the connector and the +> authorisation by removing them from the `management.xml` configuration. +> Please refer to [Java Management +> guide](https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html) +> to configure the server for remote management (system properties must be set +> in `artemis.profile`). + +By default, Apache ActiveMQ Artemis server uses the JMX domain +"org.apache.activemq.artemis". To manage several Apache ActiveMQ Artemis +servers from the *same* MBeanServer, the JMX domain can be configured for each +individual Apache ActiveMQ Artemis server by setting `jmx-domain` in +`broker.xml`: ```xml @@ -463,81 +496,87 @@ domain can be configured for each individual Apache ActiveMQ Artemis server by s ### Example -See the [Examples](examples.md) chapter for an example which shows how to use a remote connection to JMX -and MBean proxies to manage Apache ActiveMQ Artemis. +See the [JMX Management Example](examples.md#jmx-management) which shows how to +use a remote connection to JMX and MBean proxies to manage Apache ActiveMQ +Artemis. ### Exposing JMX using Jolokia The default Broker configuration ships with the [Jolokia](https://jolokia.org) -HTTP agent deployed as a Web Application. Jolokia is a remote -JMX-over-HTTP bridge that exposes MBeans. For a full guide as -to how to use it refer to [Jolokia Documentation](https://jolokia.org/documentation.html), -however a simple example to query the broker's version would -be to use a browser and go to the URL [http://username:password@localhost:8161/console/jolokia/read/org.apache.activemq.artemis:broker="0.0.0.0"/Version](). +HTTP agent deployed as a web application. Jolokia is a remote JMX-over-HTTP +bridge that exposes MBeans. For a full guide as to how to use it refer to +[Jolokia Documentation](https://jolokia.org/documentation.html), however a +simple example to query the broker's version would be to use a browser and go +to the URL +[http://username:password@localhost:8161/console/jolokia/read/org.apache.activemq.artemis:broker="0.0.0.0"/Version](). This would give you back something like the following: - {"request":{"mbean":"org.apache.activemq.artemis:broker=\"0.0.0.0\"","attribute":"Version","type":"read"},"value":"2.0.0-SNAPSHOT","timestamp":1487017918,"status":200} +``` +{"request":{"mbean":"org.apache.activemq.artemis:broker=\"0.0.0.0\"","attribute":"Version","type":"read"},"value":"2.0.0-SNAPSHOT","timestamp":1487017918,"status":200} +``` ### JMX and the Console -The console that ships with Artemis uses Jolokia under the covers which in turn uses JMX. This will use the authentication -configuration in the `management.xml` file as described in the previous section. This means that when mBeans are accessed -via the console the credentials used to log into the console and the roles associated with them. By default access to the -console is only allow via users with the amq role. This is configured in the `artemis.profile` via the system property `-Dhawtio.role=amq`. -You can configure multiple roles by changing this to `-Dhawtio.roles=amq,view,update`. +The console that ships with Artemis uses Jolokia under the covers which in turn +uses JMX. This will use the authentication configuration in the +`management.xml` file as described in the previous section. This means that +when mBeans are accessed via the console the credentials used to log into the +console and the roles associated with them. By default access to the console is +only allow via users with the amq role. This is configured in the +`artemis.profile` via the system property `-Dhawtio.role=amq`. You can +configure multiple roles by changing this to `-Dhawtio.roles=amq,view,update`. -If a user doesn't have the correct role to invoke a specific operation then this will display an authorisation exception -in the console. +If a user doesn't have the correct role to invoke a specific operation then +this will display an authorisation exception in the console. ## Using Management Message API -The management message API in ActiveMQ Artemis is accessed by sending Core Client messages -to a special address, the *management address*. +The management message API in ActiveMQ Artemis is accessed by sending Core +Client messages to a special address, the *management address*. *Management messages* are regular Core Client messages with well-known -properties that the server needs to understand to interact with the -management API: +properties that the server needs to understand to interact with the management +API: -- The name of the managed resource +- The name of the managed resource -- The name of the management operation +- The name of the management operation -- The parameters of the management operation +- The parameters of the management operation -When such a management message is sent to the management address, -Apache ActiveMQ Artemis server will handle it, extract the information, invoke the +When such a management message is sent to the management address, Apache +ActiveMQ Artemis server will handle it, extract the information, invoke the operation on the managed resources and send a *management reply* to the management message's reply-to address (specified by `ClientMessageImpl.REPLYTO_HEADER_NAME`). -A `ClientConsumer` can be used to consume the management reply and -retrieve the result of the operation (if any) stored in the reply's -body. For portability, results are returned as a [JSON](https://json.org) -String rather than Java Serialization (the +A `ClientConsumer` can be used to consume the management reply and retrieve the +result of the operation (if any) stored in the reply's body. For portability, +results are returned as a [JSON](https://json.org) String rather than Java +Serialization (the `org.apache.activemq.artemis.api.core.management.ManagementHelper` can be used to convert the JSON string to Java objects). -These steps can be simplified to make it easier to invoke management -operations using Core messages: +These steps can be simplified to make it easier to invoke management operations +using Core messages: -1. Create a `ClientRequestor` to send messages to the management - address and receive replies +1. Create a `ClientRequestor` to send messages to the management address and + receive replies -2. Create a `ClientMessage` +2. Create a `ClientMessage` -3. Use the helper class - `org.apache.activemq.artemis.api.core.management.ManagementHelper` to fill - the message with the management properties +3. Use the helper class + `org.apache.activemq.artemis.api.core.management.ManagementHelper` to fill + the message with the management properties -4. Send the message using the `ClientRequestor` +4. Send the message using the `ClientRequestor` -5. Use the helper class - `org.apache.activemq.artemis.api.core.management.ManagementHelper` to - retrieve the operation result from the management reply +5. Use the helper class + `org.apache.activemq.artemis.api.core.management.ManagementHelper` to + retrieve the operation result from the management reply. -For example, to find out the number of messages in the queue -`exampleQueue`: +For example, to find out the number of messages in the queue `exampleQueue`: ```java ClientSession session = ... @@ -550,35 +589,38 @@ int count = (Integer) ManagementHelper.getResult(reply); System.out.println("There are " + count + " messages in exampleQueue"); ``` -Management operation name and parameters must conform to the Java -interfaces defined in the `management` packages. +Management operation name and parameters must conform to the Java interfaces +defined in the `management` packages. Names of the resources are built using the helper class `org.apache.activemq.artemis.api.core.management.ResourceNames` and are straightforward (e.g. `queue.exampleQueue` for `QueueControl` of the Queue `exampleQueue`, or `broker` for the `ActiveMQServerControl`). -> *NOTE* +> **Note:** > -> The `ManagementHelper` class can be used only with Core JMS messages. -> When called with a message from a different JMS library, an exception will be thrown. +> The `ManagementHelper` class can be used only with Core JMS messages. When +> called with a message from a different JMS library, an exception will be +> thrown. ### Configuring Management The management address to send management messages is configured in `broker.xml`: - activemq.management +```xml +activemq.management +``` By default, the address is `activemq.management`. -The management address requires a *special* user permission `manage` to -be able to receive and handle management messages. This is also -configured in broker.xml: +The management address requires a *special* user permission `manage` to be able +to receive and handle management messages. This is also configured in +broker.xml: ```xml - + @@ -586,49 +628,50 @@ configured in broker.xml: ### Example -See the [Examples](examples.md) chapter for an example which shows -how to use JMS messages to manage the Apache ActiveMQ Artemis server. +See the [Management Example](examples.md#management) which shows how to use JMS +messages to manage the Apache ActiveMQ Artemis server. ## Management Notifications -Apache ActiveMQ Artemis emits *notifications* to inform listeners of potentially -interesting events (creation of new resources, security violation, +Apache ActiveMQ Artemis emits *notifications* to inform listeners of +potentially interesting events (creation of new resources, security violation, etc.). These notifications can be received by two different ways: -- JMX notifications +- JMX notifications -- Notification messages +- Notification messages ### JMX Notifications -If JMX is enabled (see Configuring JMX section), JMX notifications can be received by -subscribing to `org.apache.activemq.artemis:type=Broker,brokerName=,module=Core,serviceType=Server` for -notifications on resources. +If JMX is enabled (see Configuring JMX section), JMX notifications can be +received by subscribing to +`org.apache.activemq.artemis:type=Broker,brokerName=,module=Core,serviceType=Server` for notifications on resources. ### Notification Messages -Apache ActiveMQ Artemis defines a special *management notification address*. -Queues can be bound to this address so that clients will receive -management notifications as messages. +Apache ActiveMQ Artemis defines a special *management notification address*. +Queues can be bound to this address so that clients will receive management +notifications as messages. -A client which wants to receive management notifications must -create a queue bound to the management notification address. It can -then receive the notifications from its queue. +A client which wants to receive management notifications must create a queue +bound to the management notification address. It can then receive the +notifications from its queue. -Notifications messages are regular messages with additional -properties corresponding to the notification (its type, when it -occurred, the resources which were concerned, etc.). +Notifications messages are regular messages with additional properties +corresponding to the notification (its type, when it occurred, the resources +which were concerned, etc.). -Since notifications are regular messages, it is possible to use -message selectors to filter out notifications and receives only a subset -of all the notifications emitted by the server. +Since notifications are regular messages, it is possible to use message +selectors to filter out notifications and receives only a subset of all the +notifications emitted by the server. #### Configuring The Management Notification Address -The management notification address to receive management notifications -is configured in `broker.xml`: +The management notification address to receive management notifications is +configured in `broker.xml`: ```xml activemq.notifications @@ -645,195 +688,186 @@ Topic notificationsTopic = ActiveMQJMSClient.createTopic("activemq.notifications Session session = ... MessageConsumer notificationConsumer = session.createConsumer(notificationsTopic); -notificationConsumer.setMessageListener(new MessageListener() -{ - public void onMessage(Message notif) - { - System.out.println("------------------------"); - System.out.println("Received notification:"); - try - { - Enumeration propertyNames = notif.getPropertyNames(); - while (propertyNames.hasMoreElements()) - { - String propertyName = (String)propertyNames.nextElement(); - System.out.format(" %s: %s\n", propertyName, notif.getObjectProperty(propertyName)); - } - } - catch (JMSException e) - { - } - System.out.println("------------------------"); +notificationConsumer.setMessageListener(new MessageListener() { + public void onMessage(Message notif) { + System.out.println("------------------------"); + System.out.println("Received notification:"); + try { + Enumeration propertyNames = notif.getPropertyNames(); + while (propertyNames.hasMoreElements()) { + String propertyName = (String)propertyNames.nextElement(); + System.out.format(" %s: %s\n", propertyName, notif.getObjectProperty(propertyName)); + } + } catch (JMSException e) { + } + System.out.println("------------------------"); } }); ``` ### Example -See the [Examples](examples.md) chapter for an example which shows how to use a JMS `MessageListener` to receive management notifications from ActiveMQ Artemis server. +See the [Management Notification Example](examples.md#management-notification) +which shows how to use a JMS `MessageListener` to receive management +notifications from ActiveMQ Artemis server. ### Notification Types and Headers -Below is a list of all the different kinds of notifications as well as -which headers are on the messages. Every notification has a -`_AMQ_NotifType` (value noted in parentheses) and `_AMQ_NotifTimestamp` -header. The timestamp is the un-formatted result of a call to -`java.lang.System.currentTimeMillis()`. +Below is a list of all the different kinds of notifications as well as which +headers are on the messages. Every notification has a `_AMQ_NotifType` (value +noted in parentheses) and `_AMQ_NotifTimestamp` header. The timestamp is the +un-formatted result of a call to `java.lang.System.currentTimeMillis()`. -- `BINDING_ADDED` (0) +- `BINDING_ADDED` (0) - `_AMQ_Binding_Type`, `_AMQ_Address`, `_AMQ_ClusterName`, - `_AMQ_RoutingName`, `_AMQ_Binding_ID`, `_AMQ_Distance`, - `_AMQ_FilterString` + `_AMQ_Binding_Type`, `_AMQ_Address`, `_AMQ_ClusterName`, + `_AMQ_RoutingName`, `_AMQ_Binding_ID`, `_AMQ_Distance`, + `_AMQ_FilterString` -- `BINDING_REMOVED` (1) +- `BINDING_REMOVED` (1) - `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, - `_AMQ_Binding_ID`, `_AMQ_Distance`, `_AMQ_FilterString` + `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, + `_AMQ_Binding_ID`, `_AMQ_Distance`, `_AMQ_FilterString` -- `CONSUMER_CREATED` (2) +- `CONSUMER_CREATED` (2) - `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, `_AMQ_Distance`, - `_AMQ_ConsumerCount`, `_AMQ_User`, `_AMQ_RemoteAddress`, - `_AMQ_SessionName`, `_AMQ_FilterString` + `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, `_AMQ_Distance`, + `_AMQ_ConsumerCount`, `_AMQ_User`, `_AMQ_RemoteAddress`, + `_AMQ_SessionName`, `_AMQ_FilterString` -- `CONSUMER_CLOSED` (3) +- `CONSUMER_CLOSED` (3) - `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, `_AMQ_Distance`, - `_AMQ_ConsumerCount`, `_AMQ_User`, `_AMQ_RemoteAddress`, - `_AMQ_SessionName`, `_AMQ_FilterString` + `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, `_AMQ_Distance`, + `_AMQ_ConsumerCount`, `_AMQ_User`, `_AMQ_RemoteAddress`, + `_AMQ_SessionName`, `_AMQ_FilterString` -- `SECURITY_AUTHENTICATION_VIOLATION` (6) +- `SECURITY_AUTHENTICATION_VIOLATION` (6) - `_AMQ_User` + `_AMQ_User` -- `SECURITY_PERMISSION_VIOLATION` (7) +- `SECURITY_PERMISSION_VIOLATION` (7) - `_AMQ_Address`, `_AMQ_CheckType`, `_AMQ_User` + `_AMQ_Address`, `_AMQ_CheckType`, `_AMQ_User` -- `DISCOVERY_GROUP_STARTED` (8) +- `DISCOVERY_GROUP_STARTED` (8) - `name` + `name` -- `DISCOVERY_GROUP_STOPPED` (9) +- `DISCOVERY_GROUP_STOPPED` (9) - `name` + `name` -- `BROADCAST_GROUP_STARTED` (10) +- `BROADCAST_GROUP_STARTED` (10) - `name` + `name` -- `BROADCAST_GROUP_STOPPED` (11) +- `BROADCAST_GROUP_STOPPED` (11) - `name` + `name` -- `BRIDGE_STARTED` (12) +- `BRIDGE_STARTED` (12) - `name` + `name` -- `BRIDGE_STOPPED` (13) +- `BRIDGE_STOPPED` (13) - `name` + `name` -- `CLUSTER_CONNECTION_STARTED` (14) +- `CLUSTER_CONNECTION_STARTED` (14) - `name` + `name` -- `CLUSTER_CONNECTION_STOPPED` (15) +- `CLUSTER_CONNECTION_STOPPED` (15) - `name` + `name` -- `ACCEPTOR_STARTED` (16) +- `ACCEPTOR_STARTED` (16) - `factory`, `id` + `factory`, `id` -- `ACCEPTOR_STOPPED` (17) +- `ACCEPTOR_STOPPED` (17) - `factory`, `id` + `factory`, `id` -- `PROPOSAL` (18) +- `PROPOSAL` (18) - `_JBM_ProposalGroupId`, `_JBM_ProposalValue`, `_AMQ_Binding_Type`, - `_AMQ_Address`, `_AMQ_Distance` + `_JBM_ProposalGroupId`, `_JBM_ProposalValue`, `_AMQ_Binding_Type`, + `_AMQ_Address`, `_AMQ_Distance` -- `PROPOSAL_RESPONSE` (19) +- `PROPOSAL_RESPONSE` (19) - `_JBM_ProposalGroupId`, `_JBM_ProposalValue`, - `_JBM_ProposalAltValue`, `_AMQ_Binding_Type`, `_AMQ_Address`, - `_AMQ_Distance` + `_JBM_ProposalGroupId`, `_JBM_ProposalValue`, + `_JBM_ProposalAltValue`, `_AMQ_Binding_Type`, `_AMQ_Address`, + `_AMQ_Distance` -- `CONSUMER_SLOW` (21) +- `CONSUMER_SLOW` (21) - `_AMQ_Address`, `_AMQ_ConsumerCount`, `_AMQ_RemoteAddress`, - `_AMQ_ConnectionName`, `_AMQ_ConsumerName`, `_AMQ_SessionName` + `_AMQ_Address`, `_AMQ_ConsumerCount`, `_AMQ_RemoteAddress`, + `_AMQ_ConnectionName`, `_AMQ_ConsumerName`, `_AMQ_SessionName` ## Message Counters -Message counters can be used to obtain information on queues *over time* -as Apache ActiveMQ Artemis keeps a history on queue metrics. +Message counters can be used to obtain information on queues *over time* as +Apache ActiveMQ Artemis keeps a history on queue metrics. -They can be used to show *trends* on queues. For example, using the -management API, it would be possible to query the number of messages in -a queue at regular interval. However, this would not be enough to know -if the queue is used: the number of messages can remain constant because -nobody is sending or receiving messages from the queue or because there -are as many messages sent to the queue than messages consumed from it. -The number of messages in the queue remains the same in both cases but -its use is widely different. +They can be used to show *trends* on queues. For example, using the management +API, it would be possible to query the number of messages in a queue at regular +interval. However, this would not be enough to know if the queue is used: the +number of messages can remain constant because nobody is sending or receiving +messages from the queue or because there are as many messages sent to the queue +than messages consumed from it. The number of messages in the queue remains +the same in both cases but its use is widely different. Message counters give additional information about the queues: -- `count` +- `count` - The *total* number of messages added to the queue since the server - was started + The *total* number of messages added to the queue since the server was + started -- `countDelta` +- `countDelta` - the number of messages added to the queue *since the last message - counter update* + the number of messages added to the queue *since the last message counter + update* -- `messageCount` +- `messageCount` - The *current* number of messages in the queue + The *current* number of messages in the queue -- `messageCountDelta` +- `messageCountDelta` - The *overall* number of messages added/removed from the queue *since - the last message counter update*. For example, if - `messageCountDelta` is equal to `-10` this means that overall 10 - messages have been removed from the queue (e.g. 2 messages were - added and 12 were removed) + The *overall* number of messages added/removed from the queue *since the last + message counter update*. For example, if `messageCountDelta` is equal to `-10` + this means that overall 10 messages have been removed from the queue (e.g. 2 + messages were added and 12 were removed) -- `lastAddTimestamp` +- `lastAddTimestamp` - The timestamp of the last time a message was added to the queue + The timestamp of the last time a message was added to the queue -- `udpateTimestamp` +- `udpateTimestamp` - The timestamp of the last message counter update + The timestamp of the last message counter update -These attributes can be used to determine other meaningful data as well. -For example, to know specifically how many messages were *consumed* from -the queue since the last update simply subtract the `messageCountDelta` -from `countDelta`. + These attributes can be used to determine other meaningful data as well. For + example, to know specifically how many messages were *consumed* from the queue + since the last update simply subtract the `messageCountDelta` from + `countDelta`. ### Configuring Message Counters -By default, message counters are disabled as it might have a small -negative effect on memory. +By default, message counters are disabled as it might have a small negative +effect on memory. -To enable message counters, you can set it to `true` in -`broker.xml`: +To enable message counters, you can set it to `true` in `broker.xml`: ```xml true ``` -Message counters keep a history of the queue metrics (10 days by -default) and sample all the queues at regular interval (10 seconds by -default). If message counters are enabled, these values should be -configured to suit your messaging use case in -`broker.xml`: +Message counters keep a history of the queue metrics (10 days by default) and +sample all the queues at regular interval (10 seconds by default). If message +counters are enabled, these values should be configured to suit your messaging +use case in `broker.xml`: ```xml @@ -842,8 +876,8 @@ configured to suit your messaging use case in 60000 ``` -Message counters can be retrieved using the Management API. For example, -to retrieve message counters on a queue using JMX: +Message counters can be retrieved using the Management API. For example, to +retrieve message counters on a queue using JMX: ```java // retrieve a connection to Apache ActiveMQ Artemis's MBeanServer @@ -863,4 +897,5 @@ messageCounter.getMessageCountDelta()); ### Example -See the [Examples](examples.md) chapter for an example which shows how to use message counters to retrieve information on a queue. +See the [Message Counter Example](examples.md#message-counter) which shows how +to use message counters to retrieve information on a queue. diff --git a/docs/user-manual/en/masking-passwords.md b/docs/user-manual/en/masking-passwords.md index 4ec64bae9c9..c7332c78b94 100644 --- a/docs/user-manual/en/masking-passwords.md +++ b/docs/user-manual/en/masking-passwords.md @@ -1,65 +1,67 @@ # Masking Passwords -By default all passwords in Apache ActiveMQ Artemis server's configuration files are in -plain text form. This usually poses no security issues as those files -should be well protected from unauthorized accessing. However, in some -circumstances a user doesn't want to expose its passwords to more eyes -than necessary. +By default all passwords in Apache ActiveMQ Artemis server's configuration +files are in plain text form. This usually poses no security issues as those +files should be well protected from unauthorized accessing. However, in some +circumstances a user doesn't want to expose its passwords to more eyes than +necessary. Apache ActiveMQ Artemis can be configured to use 'masked' passwords in its -configuration files. A masked password is an obscure string -representation of a real password. To mask a password a user will use an -'encoder'. The encoder takes in the real password and outputs the masked -version. A user can then replace the real password in the configuration -files with the new masked password. When Apache ActiveMQ Artemis loads a masked -password, it uses a suitable 'decoder' to decode it into real password. +configuration files. A masked password is an obscure string representation of a +real password. To mask a password a user will use an 'encoder'. The encoder +takes in the real password and outputs the masked version. A user can then +replace the real password in the configuration files with the new masked +password. When Apache ActiveMQ Artemis loads a masked password, it uses a +suitable 'decoder' to decode it into real password. -Apache ActiveMQ Artemis provides a default password encoder and decoder. Optionally -users can use or implement their own encoder and decoder for masking the -passwords. +Apache ActiveMQ Artemis provides a default password encoder and decoder. +Optionally users can use or implement their own encoder and decoder for masking +the passwords. -In general, a masked password can be identified using one of two ways. The first one -is the ENC() syntax, i.e. any string value wrapped in ENC() is to be treated as -a masked password. For example +In general, a masked password can be identified using one of two ways. The +first one is the `ENC()` syntax, i.e. any string value wrapped in `ENC()` is to +be treated as a masked password. For example `ENC(xyz)` The above indicates that the password is masked and the masked value is `xyz`. -The ENC() syntax is the preferred way to indicating a masked password and is +The `ENC()` syntax is the **preferred way** of masking a password and is universally supported in every password configuration in Artemis. -The other way is to use a `mask-password` attribute to tell that a password -in a configuration file should be treated as 'masked'. For example: +The other way is to use a `mask-password` attribute to tell that a password in +a configuration file should be treated as 'masked'. For example: ```xml true xyz ``` -This method is now deprecated and exists only to maintain backward-compatibility. -Newer configurations may not support it. + +This method is now **deprecated** and exists only to maintain +backward-compatibility. Newer configurations may not support it. ### Password Masking in Server Configuration File #### General Masking Configuration -Besides supporting the ENC() syntax, the server configuration file (i.e. broker.xml) -has a property that defines the default masking behaviors over the entire file scope. +Besides supporting the `ENC()` syntax, the server configuration file (i.e. +broker.xml) has a property that defines the default masking behaviors over the +entire file scope. -`mask-password`: this boolean type property indicates if a password -should be masked or not. Set it to "true" if you want your passwords -masked. The default value is "false". +`mask-password`: this boolean type property indicates if a password should be +masked or not. Set it to "true" if you want your passwords masked. The default +value is "false". `password-codec`: this string type property identifies the name of the class which will be used to decode the masked password within the broker. If not -specified then the default `org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` -will be used. +specified then the default +`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` will be used. #### Specific Masking Behaviors ##### cluster-password -If it is specified in ENC() syntax it will be treated as masked, or +If it is specified in `ENC()` syntax it will be treated as masked, or If `mask-password` is `true` the `cluster-password` will be treated as masked. @@ -73,76 +75,77 @@ will have different password masking needs. When a `connector` or `acceptor` is initialised, Apache ActiveMQ Artemis will add the aforementioned `mask-password` and `password-codec` values to the -`connector` or `acceptor` parameters using the keys `activemq.usemaskedpassword` -and `activemq.passwordcodec` respectively. The Netty and InVM implementations -will use these as needed and any other implementations will have access to -these to use if they so wish. +`connector` or `acceptor` parameters using the keys +`activemq.usemaskedpassword` and `activemq.passwordcodec` respectively. The +Netty and InVM implementations will use these as needed and any other +implementations will have access to these to use if they so wish. -The preferred way, however, is to use the ENC() syntax. +The preferred way, however, is to use the `ENC()` syntax. ##### Core Bridges -Core Bridges are configured in the server configuration file and so the -masking of its `password` properties follows the same rules as that of -`cluster-password`. It supports ENC() syntax. +Core Bridges are configured in the server configuration file and so the masking +of its `password` properties follows the same rules as that of +`cluster-password`. It supports `ENC()` syntax. -For using `mask-password` property, the following table summarizes the +For using `mask-password` property, the following table summarizes the relations among the above-mentioned properties - mask-password | cluster-password | acceptor/connector passwords | bridge password - :------------- | :---------------- | :--------------------------- | :--------------- - absent | plain text | plain text | plain text - false | plain text | plain text | plain text - true | masked | masked | masked +mask-password | cluster-password | acceptor/connector passwords | bridge password +--- | --- | --- | --- +absent|plain text|plain text|plain text +false|plain text|plain text|plain text +true|masked|masked|masked -It is recommended that you use the `ENC()` syntax for new applications/deployments. +It is recommended that you use the `ENC()` syntax for new +applications/deployments. #### Examples -Note: In the following examples if related attributed or properties are +**Note:** In the following examples if related attributed or properties are absent, it means they are not specified in the configure file. -example 1 +- Unmasked -```xml -bbc -``` + ```xml + bbc + ``` -This indicates the cluster password is a plain text value ("bbc"). + This indicates the cluster password is a plain text value `bbc`. -example 2 +- Masked 1 -```xml -ENC(xyz) -``` + ```xml + ENC(xyz) + ``` -This indicates the cluster password is a masked value ("xyz"). + This indicates the cluster password is a masked value `xyz`. -example 3 +- Masked 2 -```xml -true -80cf731af62c290 -``` + ```xml + true + 80cf731af62c290 + ``` -This indicates the cluster password is a masked value and Apache ActiveMQ Artemis will -use its built-in decoder to decode it. All other passwords in the -configuration file, Connectors, Acceptors and Bridges, will also use -masked passwords. + This indicates the cluster password is a masked value and Apache ActiveMQ + Artemis will use its built-in decoder to decode it. All other passwords in the + configuration file, Connectors, Acceptors and Bridges, will also use masked + passwords. #### Passwords in bootstrap.xml The broker embeds a web-server for hosting some web applications such as a -management console. It is configured in bootstrap.xml as a web -component. The web server can be secured using https protocol, and it can be -configured with a keystore password and/or truststore password which by -default are specified in plain text forms. +management console. It is configured in bootstrap.xml as a web component. The +web server can be secured using https protocol, and it can be configured with a +keystore password and/or truststore password which by default are specified in +plain text forms. -To mask these passwords you need to use ENC() syntax. The `mask-password` is -not supported here. +To mask these passwords you need to use `ENC()` syntax. The `mask-password` +boolean is not supported here. -You can also set the `passwordCodec` attribute if you want to use a password codec -other than the default one. For example +You can also set the `passwordCodec` attribute if you want to use a password +codec other than the default one. For example ```xml @@ -206,16 +208,17 @@ Example 2 Using the "UseMaskedPassword" property: ``` -With this configuration, both passwords in ra.xml and all of its MDBs -will have to be in masked form. +With this configuration, both passwords in ra.xml and all of its MDBs will have +to be in masked form. ### Passwords in artemis-users.properties Apache ActiveMQ Artemis's built-in security manager uses plain properties files -where the user passwords are specified in a hashed form by default. Note, the passwords -are technically *hashed* rather than masked in this context. The default `PropertiesLoginModule` -will not decode the passwords in `artemis-users.properties` but will instead hash the input -and compare the two hashed values for password verification. +where the user passwords are specified in a hashed form by default. Note, the +passwords are technically *hashed* rather than masked in this context. The +default `PropertiesLoginModule` will not decode the passwords in +`artemis-users.properties` but will instead hash the input and compare the two +hashed values for password verification. Please use Artemis CLI command to add a password. For example: @@ -223,26 +226,27 @@ Please use Artemis CLI command to add a password. For example: ./artemis user add --username guest --password guest --role admin ``` -This will use the default `org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` -to perform a "one-way" hash of the password and alter both the `artemis-users.properties` +This will use the default +`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` to perform a +"one-way" hash of the password and alter both the `artemis-users.properties` and `artemis-roles.properties` files with the specified values. -Passwords in `artemis-users.properties` are automatically detected as hashed or not -by looking for the syntax `ENC()`. The `mask-password` parameter does not need -to be `true` to use hashed passwords here. +Passwords in `artemis-users.properties` are automatically detected as hashed or +not by looking for the syntax `ENC()`. The `mask-password` parameter does +not need to be `true` to use hashed passwords here. ### Password in login.config -Artemis supports LDAP login modules to be configured in JAAS configuration -file (default name is `login.config`). When connecting to a LDAP server usually -you need to supply a connection password in the config file. By default this +Artemis supports LDAP login modules to be configured in JAAS configuration file +(default name is `login.config`). When connecting to a LDAP server usually you +need to supply a connection password in the config file. By default this password is in plain text form. -To mask it you need to configure the passwords in your login module -using ENC() syntax. To specify a codec using the following property: +To mask it you need to configure the passwords in your login module using +`ENC()` syntax. To specify a codec using the following property: -`passwordCodec` - the password codec class name. (the default codec -will be used if it is absent) +`passwordCodec` - the password codec class name. (the default codec will be +used if it is absent) For example: @@ -270,24 +274,24 @@ LDAPLoginExternalPasswordCodec { ### Choosing a decoder for password masking -As described in the previous sections, all password masking requires a -decoder. A decoder uses an algorithm to convert a masked password into -its original clear text form in order to be used in various security -operations. The algorithm used for decoding must match that for -encoding. Otherwise the decoding may not be successful. +As described in the previous sections, all password masking requires a decoder. +A decoder uses an algorithm to convert a masked password into its original +clear text form in order to be used in various security operations. The +algorithm used for decoding must match that for encoding. Otherwise the +decoding may not be successful. For user's convenience Apache ActiveMQ Artemis provides a default decoder. However a user can implement their own if they wish. #### The Default Decoder -Whenever no decoder is specified in the configuration file, the default -decoder is used. The class name for the default decoder is -`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec`. It has hashing, -encoding, and decoding capabilities. It uses `java.crypto.Cipher` utilities -to hash or encode a plaintext password and also to decode a masked string using -same algorithm and key. Using this decoder/encoder is pretty straightforward. To -get a mask for a password, just run the `mask` command: +Whenever no decoder is specified in the configuration file, the default decoder +is used. The class name for the default decoder is +`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec`. It has +hashing, encoding, and decoding capabilities. It uses `java.crypto.Cipher` +utilities to hash or encode a plaintext password and also to decode a masked +string using same algorithm and key. Using this decoder/encoder is pretty +straightforward. To get a mask for a password, just run the `mask` command: ```sh ./artemis mask @@ -304,18 +308,19 @@ plaintext password in broker.xml with it. #### Using a custom decoder -It is possible to use a custom decoder rather than the built-in one. -Simply make sure the decoder is in Apache ActiveMQ Artemis's classpath. The custom decoder -can also be service loaded rather than class loaded, if the decoder's service provider is installed in the classpath. -Then configure the server to use it as follows: +It is possible to use a custom decoder rather than the built-in one. Simply +make sure the decoder is in Apache ActiveMQ Artemis's classpath. The custom +decoder can also be service loaded rather than class loaded, if the decoder's +service provider is installed in the classpath. Then configure the server to +use it as follows: ```xml com.foo.SomeDecoder;key1=value1;key2=value2 ``` -If your decoder needs params passed to it you can do this via key/value -pairs when configuring. For instance if your decoder needs say a -"key-location" parameter, you can define like so: +If your decoder needs params passed to it you can do this via key/value pairs +when configuring. For instance if your decoder needs say a "key-location" +parameter, you can define like so: ```xml com.foo.NewDecoder;key-location=/some/url/to/keyfile @@ -328,15 +333,14 @@ Then configure your cluster-password like this: ``` When Apache ActiveMQ Artemis reads the cluster-password it will initialize the -NewDecoder and use it to decode "mask\_password". It also process all -passwords using the new defined decoder. +NewDecoder and use it to decode "mask\_password". It also process all passwords +using the new defined decoder. #### Implementing Custom Codecs -To use a different decoder than the built-in one, you either pick one -from existing libraries or you implement it yourself. All decoders must -implement the `org.apache.activemq.artemis.utils.SensitiveDataCodec` -interface: +To use a different decoder than the built-in one, you either pick one from +existing libraries or you implement it yourself. All decoders must implement +the `org.apache.activemq.artemis.utils.SensitiveDataCodec` interface: ```java public interface SensitiveDataCodec @@ -347,8 +351,8 @@ public interface SensitiveDataCodec } ``` -This is a generic type interface but normally for a password you just -need String type. So a new decoder would be defined like +This is a generic type interface but normally for a password you just need +String type. So a new decoder would be defined like ```java public class MyNewDecoder implements SensitiveDataCodec @@ -367,4 +371,5 @@ public class MyNewDecoder implements SensitiveDataCodec ``` Last but not least, once you get your own decoder please [add it to the -classpath](using-server.md#adding-runtime-dependencies) otherwise the broker will fail to load it! \ No newline at end of file +classpath](using-server.md#adding-runtime-dependencies) otherwise the broker +will fail to load it! diff --git a/docs/user-manual/en/maven-plugin.md b/docs/user-manual/en/maven-plugin.md index 19bda2f22bf..143db9d23b1 100644 --- a/docs/user-manual/en/maven-plugin.md +++ b/docs/user-manual/en/maven-plugin.md @@ -13,17 +13,17 @@ You could for example use these maven plugins on your testsuite or deployment au There are three goals that you can use -- create +- `create` -This will create a server accordingly to your arguments. You can do some extra tricks here such as installing extra libraries for external modules. + This will create a server accordingly to your arguments. You can do some extra tricks here such as installing extra libraries for external modules. -- cli +- `cli` -This will perform any CLI operation. This is basically a maven expression of the CLI classes + This will perform any CLI operation. This is basically a maven expression of the CLI classes -- runClient +- `runClient` -This is a simple wrapper around classes implementing a static main call. Notice that this won't spawn a new VM or new Thread. + This is a simple wrapper around classes implementing a static main call. Notice that this won't spawn a new VM or new Thread. ## Declaration @@ -31,11 +31,14 @@ This is a simple wrapper around classes implementing a static main call. Notice On your pom, use the plugins section: ```xml - - - - org.apache.activemq - artemis-maven-plugin + + + + org.apache.activemq + artemis-maven-plugin + + + ``` ## create goal @@ -43,7 +46,7 @@ On your pom, use the plugins section: I won't detail every operation of the create plugin here, but I will try to describe the main parameters: Name | Description -:--- | :--- +--- | --- configuration | A place that will hold any file to replace on the configuration. For instance if you are providing your own broker.xml. Default is "${basedir}/target/classes/activemq/server0" home | The location where you downloaded and installed artemis. Default is "${activemq.basedir}" alternateHome | This is used case you have two possible locations for your home (e.g. one under compile and one under production @@ -54,16 +57,15 @@ liblist[] | A list of libraries to be installed under ./lib. ex: "org.jgroups:jg Example: ```xml - - - create - - create - - - ${noServer} - - + + create + + create + + + ${noServer} + + ``` @@ -72,7 +74,7 @@ Example: Some properties for the CLI Name | Description -:--- | :--- +--- | --- configuration | A place that will hold any file to replace on the configuration. For instance if you are providing your own broker.xml. Default is "${basedir}/target/classes/activemq/server0" home | The location where you downloaded and installed artemis. Default is "${activemq.basedir}" alternateHome | This is used case you have two possible locations for your home (e.g. one under compile and one under production @@ -105,7 +107,7 @@ Example: This is a simple solution for running classes implementing the main method. Name | Description -:--- | :--- +--- | --- clientClass | A class implement a static void main(String arg[]) args | A string array of arguments passed to the method diff --git a/docs/user-manual/en/message-expiry.md b/docs/user-manual/en/message-expiry.md index 99cb73f1cfa..68463f591cf 100644 --- a/docs/user-manual/en/message-expiry.md +++ b/docs/user-manual/en/message-expiry.md @@ -2,19 +2,19 @@ Messages can be set with an optional *time to live* when sending them. -Apache ActiveMQ Artemis will not deliver a message to a consumer after it's time to -live has been exceeded. If the message hasn't been delivered by the time -that time to live is reached the server can discard it. +Apache ActiveMQ Artemis will not deliver a message to a consumer after it's +time to live has been exceeded. If the message hasn't been delivered by the +time that time to live is reached the server can discard it. -Apache ActiveMQ Artemis's addresses can be assigned a expiry address so that, when -messages are expired, they are removed from the queue and sent to the -expiry address. Many different queues can be bound to an expiry address. -These *expired* messages can later be consumed for further inspection. +Apache ActiveMQ Artemis's addresses can be assigned a expiry address so that, +when messages are expired, they are removed from the queue and sent to the +expiry address. Many different queues can be bound to an expiry address. These +*expired* messages can later be consumed for further inspection. ## Core API -Using Apache ActiveMQ Artemis Core API, you can set an expiration time directly on the -message: +Using Apache ActiveMQ Artemis Core API, you can set an expiration time directly +on the message: ```java // message will expire in 5000ms from now @@ -28,23 +28,23 @@ JMS MessageProducer allows to set a TimeToLive for the messages it sent: producer.setTimeToLive(5000); ``` -Expired messages which are consumed from an expiry address have the -following properties: +Expired messages which are consumed from an expiry address have the following +properties: -- `_AMQ_ORIG_ADDRESS` +- `_AMQ_ORIG_ADDRESS` - a String property containing the *original address* of the expired - message + a String property containing the *original address* of the expired + message -- `_AMQ_ORIG_QUEUE` +- `_AMQ_ORIG_QUEUE` - a String property containing the *original queue* of the expired - message + a String property containing the *original queue* of the expired + message -- `_AMQ_ACTUAL_EXPIRY` +- `_AMQ_ACTUAL_EXPIRY` - a Long property containing the *actual expiration time* of the - expired message + a Long property containing the *actual expiration time* of the + expired message ## Configuring Expiry Addresses @@ -57,29 +57,29 @@ Expiry address are defined in the address-setting configuration:
``` -If messages are expired and no expiry address is specified, messages are -simply removed from the queue and dropped. Address wildcards can be used -to configure expiry address for a set of addresses (see [Understanding the Wildcard Syntax](wildcard-syntax.md)). +If messages are expired and no expiry address is specified, messages are simply +removed from the queue and dropped. Address [wildcards](wildcard-syntax.md) can +be used to configure expiry address for a set of addresses. ## Configuring The Expiry Reaper Thread -A reaper thread will periodically inspect the queues to check if -messages have expired. +A reaper thread will periodically inspect the queues to check if messages have +expired. The reaper thread can be configured with the following properties in `broker.xml` -- `message-expiry-scan-period` +- `message-expiry-scan-period` - How often the queues will be scanned to detect expired messages (in - milliseconds, default is 30000ms, set to `-1` to disable the reaper - thread) + How often the queues will be scanned to detect expired messages (in + milliseconds, default is 30000ms, set to `-1` to disable the reaper thread) -- `message-expiry-thread-priority` +- `message-expiry-thread-priority` - The reaper thread priority (it must be between 1 and 10, 10 being the - highest priority, default is 3) + The reaper thread priority (it must be between 1 and 10, 10 being the highest + priority, default is 3) ## Example -See the [examples.md](examples.md) chapter for an example which shows how message expiry is configured and used with JMS. +See the [Message Expiration Example](examples.md#message-expiration) which +shows how message expiry is configured and used with JMS. diff --git a/docs/user-manual/en/message-grouping.md b/docs/user-manual/en/message-grouping.md index 5fb42552472..050832747fa 100644 --- a/docs/user-manual/en/message-grouping.md +++ b/docs/user-manual/en/message-grouping.md @@ -1,106 +1,106 @@ # Message Grouping -Message groups are sets of messages that have the following -characteristics: +Message groups are sets of messages that have the following characteristics: -- Messages in a message group share the same group id, i.e. they have - same group identifier property (`JMSXGroupID` for JMS, - `_AMQ_GROUP_ID` for Apache ActiveMQ Artemis Core API). +- Messages in a message group share the same group id, i.e. they have same + group identifier property (`JMSXGroupID` for JMS, `_AMQ_GROUP_ID` for Apache + ActiveMQ Artemis Core API). -- Messages in a message group are always consumed by the same - consumer, even if there are many consumers on a queue. They pin all - messages with the same group id to the same consumer. If that - consumer closes another consumer is chosen and will receive all - messages with the same group id. +- Messages in a message group are always consumed by the same consumer, even if + there are many consumers on a queue. They pin all messages with the same + group id to the same consumer. If that consumer closes another consumer is + chosen and will receive all messages with the same group id. -Message groups are useful when you want all messages for a certain value -of the property to be processed serially by the same consumer. +Message groups are useful when you want all messages for a certain value of the +property to be processed serially by the same consumer. -An example might be orders for a certain stock. You may want orders for -any particular stock to be processed serially by the same consumer. To -do this you can create a pool of consumers (perhaps one for each stock, -but less will work too), then set the stock name as the value of the -_AMQ_GROUP_ID property. +An example might be orders for a certain stock. You may want orders for any +particular stock to be processed serially by the same consumer. To do this you +can create a pool of consumers (perhaps one for each stock, but less will work +too), then set the stock name as the value of the _AMQ_GROUP_ID property. This will ensure that all messages for a particular stock will always be processed by the same consumer. -> **Note** +> **Note:** > -> Grouped messages can impact the concurrent processing of non-grouped -> messages due to the underlying FIFO semantics of a queue. For example, -> if there is a chunk of 100 grouped messages at the head of a queue -> followed by 1,000 non-grouped messages then all the grouped messages -> will need to be sent to the appropriate client (which is consuming -> those grouped messages serially) before any of the non-grouped -> messages can be consumed. The functional impact in this scenario is a -> temporary suspension of concurrent message processing while all the -> grouped messages are processed. This can be a performance bottleneck -> so keep it in mind when determining the size of your message groups, -> and consider whether or not you should isolate your grouped messages +> Grouped messages can impact the concurrent processing of non-grouped messages +> due to the underlying FIFO semantics of a queue. For example, if there is a +> chunk of 100 grouped messages at the head of a queue followed by 1,000 +> non-grouped messages then all the grouped messages will need to be sent to +> the appropriate client (which is consuming those grouped messages serially) +> before any of the non-grouped messages can be consumed. The functional impact +> in this scenario is a temporary suspension of concurrent message processing +> while all the grouped messages are processed. This can be a performance +> bottleneck so keep it in mind when determining the size of your message +> groups, and consider whether or not you should isolate your grouped messages > from your non-grouped messages. ## Using Core API -The property name used to identify the message group is `"_AMQ_GROUP_ID"` -(or the constant `MessageImpl.HDR_GROUP_ID`). Alternatively, you can set -`autogroup` to true on the `SessionFactory` which will pick a random -unique id. +The property name used to identify the message group is `"_AMQ_GROUP_ID"` (or +the constant `MessageImpl.HDR_GROUP_ID`). Alternatively, you can set +`autogroup` to true on the `SessionFactory` which will pick a random unique id. ## Using JMS The property name used to identify the message group is `JMSXGroupID`. - // send 2 messages in the same group to ensure the same - // consumer will receive both - Message message = ... - message.setStringProperty("JMSXGroupID", "Group-0"); - producer.send(message); +```java +// send 2 messages in the same group to ensure the same +// consumer will receive both +Message message = ... +message.setStringProperty("JMSXGroupID", "Group-0"); +producer.send(message); - message = ... - message.setStringProperty("JMSXGroupID", "Group-0"); - producer.send(message); +message = ... +message.setStringProperty("JMSXGroupID", "Group-0"); +producer.send(message); +``` Alternatively, you can set `autogroup` to true on the -`ActiveMQConnectonFactory` which will pick a random unique id. This can -also be set in the JNDI context environment, e.g. `jndi.properties`. -Here's a simple example using the "ConnectionFactory" connection factory -which is available in the context by default - - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - connectionFactory.myConnectionFactory=tcp://localhost:61616?autoGroup=true +`ActiveMQConnectonFactory` which will pick a random unique id. This can also be +set in the JNDI context environment, e.g. `jndi.properties`. Here's a simple +example using the "ConnectionFactory" connection factory which is available in +the context by default + +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.myConnectionFactory=tcp://localhost:61616?autoGroup=true +``` -Alternatively you can set the group id via the connection factory. All -messages sent with producers created via this connection factory will -set the `JMSXGroupID` to the specified value on all messages sent. This -can also be set in the JNDI context environment, e.g. `jndi.properties`. -Here's a simple example using the "ConnectionFactory" connection factory -which is available in the context by default: +Alternatively you can set the group id via the connection factory. All messages +sent with producers created via this connection factory will set the +`JMSXGroupID` to the specified value on all messages sent. This can also be set +in the JNDI context environment, e.g. `jndi.properties`. Here's a simple +example using the "ConnectionFactory" connection factory which is available in +the context by default: - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - connectionFactory.myConnectionFactory=tcp://localhost:61616?groupID=Group-0 +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.myConnectionFactory=tcp://localhost:61616?groupID=Group-0 +``` ## Example -See the [examples](examples.md} chapter for an example which shows how message groups are configured and used with JMS and via a connection factory. +See the [Message Group Example](examples.md#message-group) which shows how +message groups are configured and used with JMS and via a connection factory. ## Clustered Grouping Using message groups in a cluster is a bit more complex. This is because -messages with a particular group id can arrive on any node so each node -needs to know about which group id's are bound to which consumer on -which node. The consumer handling messages for a particular group id may -be on a different node of the cluster, so each node needs to know this -information so it can route the message correctly to the node which has -that consumer. +messages with a particular group id can arrive on any node so each node needs +to know about which group id's are bound to which consumer on which node. The +consumer handling messages for a particular group id may be on a different node +of the cluster, so each node needs to know this information so it can route the +message correctly to the node which has that consumer. -To solve this there is the notion of a grouping handler. Each node will -have its own grouping handler and when a messages is sent with a group -id assigned, the handlers will decide between them which route the -message should take. +To solve this there is the notion of a grouping handler. Each node will have +its own grouping handler and when a messages is sent with a group id assigned, +the handlers will decide between them which route the message should take. -Here is a sample config for each type of handler. This should be -configured in `broker.xml`. +Here is a sample config for each type of handler. This should be configured in +`broker.xml`. ```xml @@ -116,71 +116,66 @@ configured in `broker.xml`. ``` - - `type` two types of handlers are supported - `LOCAL` and `REMOTE`. - Each cluster should choose 1 node to have a `LOCAL` grouping handler - and all the other nodes should have `REMOTE` handlers. It's the `LOCAL` - handler that actually makes the decision as to what route should be - used, all the other `REMOTE` handlers converse with this. - - - `address` refers to a [cluster connection and the address - it uses](clusters.md#configuring-cluster-connections). Refer to the - clustering section on how to configure clusters. +- `type` two types of handlers are supported - `LOCAL` and `REMOTE`. Each + cluster should choose 1 node to have a `LOCAL` grouping handler and all the + other nodes should have `REMOTE` handlers. It's the `LOCAL` handler that + actually makes the decision as to what route should be used, all the other + `REMOTE` handlers converse with this. + +- `address` refers to a [cluster connection and the address it + uses](clusters.md#configuring-cluster-connections). Refer to the clustering + section on how to configure clusters. - - `timeout` how long to wait for a decision to be made. An exception - will be thrown during the send if this timeout is reached, this - ensures that strict ordering is kept. - -The decision as to where a message should be routed to is initially -proposed by the node that receives the message. The node will pick a -suitable route as per the normal clustered routing conditions, i.e. -round robin available queues, use a local queue first and choose a queue -that has a consumer. If the proposal is accepted by the grouping -handlers the node will route messages to this queue from that point on, -if rejected an alternative route will be offered and the node will again -route to that queue indefinitely. All other nodes will also route to the -queue chosen at proposal time. Once the message arrives at the queue -then normal single server message group semantics take over and the +- `timeout` how long to wait for a decision to be made. An exception will be + thrown during the send if this timeout is reached, this ensures that strict + ordering is kept. + +The decision as to where a message should be routed to is initially proposed by +the node that receives the message. The node will pick a suitable route as per +the normal clustered routing conditions, i.e. round robin available queues, +use a local queue first and choose a queue that has a consumer. If the proposal +is accepted by the grouping handlers the node will route messages to this queue +from that point on, if rejected an alternative route will be offered and the +node will again route to that queue indefinitely. All other nodes will also +route to the queue chosen at proposal time. Once the message arrives at the +queue then normal single server message group semantics take over and the message is pinned to a consumer on that queue. -You may have noticed that there is a single point of failure with the -single local handler. If this node crashes then no decisions will be -able to be made. Any messages sent will be not be delivered and an -exception thrown. To avoid this happening Local Handlers can be -replicated on another backup node. Simple create your back up node and -configure it with the same Local handler. +You may have noticed that there is a single point of failure with the single +local handler. If this node crashes then no decisions will be able to be made. +Any messages sent will be not be delivered and an exception thrown. To avoid +this happening Local Handlers can be replicated on another backup node. Simple +create your back up node and configure it with the same Local handler. ## Clustered Grouping Best Practices Some best practices should be followed when using clustered grouping: -1. Make sure your consumers are distributed evenly across the different - nodes if possible. This is only an issue if you are creating and - closing consumers regularly. Since messages are always routed to the - same queue once pinned, removing a consumer from this queue may - leave it with no consumers meaning the queue will just keep - receiving the messages. Avoid closing consumers or make sure that - you always have plenty of consumers, i.e., if you have 3 nodes have - 3 consumers. - -2. Use durable queues if possible. If queues are removed once a group - is bound to it, then it is possible that other nodes may still try - to route messages to it. This can be avoided by making sure that the - queue is deleted by the session that is sending the messages. This - means that when the next message is sent it is sent to the node - where the queue was deleted meaning a new proposal can successfully - take place. Alternatively you could just start using a different - group id. - -3. Always make sure that the node that has the Local Grouping Handler - is replicated. These means that on failover grouping will still - occur. - -4. In case you are using group-timeouts, the remote node should have a - smaller group-timeout with at least half of the value on the main - coordinator. This is because this will determine how often the - last-time-use value should be updated with a round trip for a - request to the group between the nodes. +1. Make sure your consumers are distributed evenly across the different nodes + if possible. This is only an issue if you are creating and closing + consumers regularly. Since messages are always routed to the same queue once + pinned, removing a consumer from this queue may leave it with no consumers + meaning the queue will just keep receiving the messages. Avoid closing + consumers or make sure that you always have plenty of consumers, i.e., if you + have 3 nodes have 3 consumers. + +2. Use durable queues if possible. If queues are removed once a group is bound + to it, then it is possible that other nodes may still try to route messages + to it. This can be avoided by making sure that the queue is deleted by the + session that is sending the messages. This means that when the next message is + sent it is sent to the node where the queue was deleted meaning a new proposal + can successfully take place. Alternatively you could just start using a + different group id. + +3. Always make sure that the node that has the Local Grouping Handler is + replicated. These means that on failover grouping will still occur. + +4. In case you are using group-timeouts, the remote node should have a smaller + group-timeout with at least half of the value on the main coordinator. This + is because this will determine how often the last-time-use value should be + updated with a round trip for a request to the group between the nodes. ## Clustered Grouping Example -See the [examples](examples.md) chapter for an example of how to configure message groups with a ActiveMQ Artemis Cluster. +See the [Clustered Grouping Example](examples.md#clustered-grouping) which +shows how to configure message groups with a ActiveMQ Artemis Cluster. diff --git a/docs/user-manual/en/messaging-concepts.md b/docs/user-manual/en/messaging-concepts.md index 582b85d754d..ab829cccdf0 100644 --- a/docs/user-manual/en/messaging-concepts.md +++ b/docs/user-manual/en/messaging-concepts.md @@ -1,240 +1,240 @@ # Messaging Concepts -Apache ActiveMQ Artemis is an asynchronous messaging system, an example of [Message -Oriented -Middleware](https://en.wikipedia.org/wiki/Message-oriented_middleware) , -we'll just call them messaging systems in the remainder of this book. +Apache ActiveMQ Artemis is an asynchronous messaging system, an example of +[Message Oriented +Middleware](https://en.wikipedia.org/wiki/Message-oriented_middleware) , we'll +just call them messaging systems in the remainder of this book. -We'll first present a brief overview of what kind of things messaging -systems do, where they're useful and the kind of concepts you'll hear -about in the messaging world. +We'll first present a brief overview of what kind of things messaging systems +do, where they're useful and the kind of concepts you'll hear about in the +messaging world. If you're already familiar with what a messaging system is and what it's capable of, then you can skip this chapter. ## General Concepts -Messaging systems allow you to loosely couple heterogeneous systems -together, whilst typically providing reliability, transactions and many -other features. +Messaging systems allow you to loosely couple heterogeneous systems together, +whilst typically providing reliability, transactions and many other features. Unlike systems based on a [Remote Procedure Call](https://en.wikipedia.org/wiki/Remote_procedure_call) (RPC) pattern, -messaging systems primarily use an asynchronous message passing pattern -with no tight relationship between requests and responses. Most -messaging systems also support a request-response mode but this is not a -primary feature of messaging systems. - -Designing systems to be asynchronous from end-to-end allows you to -really take advantage of your hardware resources, minimizing the amount -of threads blocking on IO operations, and to use your network bandwidth -to its full capacity. With an RPC approach you have to wait for a -response for each request you make so are limited by the network round -trip time, or *latency* of your network. With an asynchronous system you -can pipeline flows of messages in different directions, so are limited -by the network *bandwidth* not the latency. This typically allows you to -create much higher performance applications. +messaging systems primarily use an asynchronous message passing pattern with no +tight relationship between requests and responses. Most messaging systems also +support a request-response mode but this is not a primary feature of messaging +systems. + +Designing systems to be asynchronous from end-to-end allows you to really take +advantage of your hardware resources, minimizing the amount of threads blocking +on IO operations, and to use your network bandwidth to its full capacity. With +an RPC approach you have to wait for a response for each request you make so +are limited by the network round trip time, or *latency* of your network. With +an asynchronous system you can pipeline flows of messages in different +directions, so are limited by the network *bandwidth* not the latency. This +typically allows you to create much higher performance applications. Messaging systems decouple the senders of messages from the consumers of -messages. The senders and consumers of messages are completely -independent and know nothing of each other. This allows you to create -flexible, loosely coupled systems. - -Often, large enterprises use a messaging system to implement a message -bus which loosely couples heterogeneous systems together. Message buses -often form the core of an [Enterprise Service -Bus](https://en.wikipedia.org/wiki/Enterprise_service_bus). (ESB). Using -a message bus to de-couple disparate systems can allow the system to -grow and adapt more easily. It also allows more flexibility to add new -systems or retire old ones since they don't have brittle dependencies on -each other. +messages. The senders and consumers of messages are completely independent and +know nothing of each other. This allows you to create flexible, loosely coupled +systems. + +Often, large enterprises use a messaging system to implement a message bus +which loosely couples heterogeneous systems together. Message buses often form +the core of an [Enterprise Service +Bus](https://en.wikipedia.org/wiki/Enterprise_service_bus). (ESB). Using a +message bus to de-couple disparate systems can allow the system to grow and +adapt more easily. It also allows more flexibility to add new systems or retire +old ones since they don't have brittle dependencies on each other. ## Messaging styles -Messaging systems normally support two main styles of asynchronous -messaging: [message queue](https://en.wikipedia.org/wiki/Message_queue) -messaging (also known as *point-to-point messaging*) and [publish -subscribe](https://en.wikipedia.org/wiki/Publish_subscribe) messaging. -We'll summarise them briefly here: +Messaging systems normally support two main styles of asynchronous messaging: +[message queue](https://en.wikipedia.org/wiki/Message_queue) messaging (also +known as *point-to-point messaging*) and [publish +subscribe](https://en.wikipedia.org/wiki/Publish_subscribe) messaging. We'll +summarise them briefly here: ### Point-to-Point -With this type of messaging you send a message to a queue. The message -is then typically persisted to provide a guarantee of delivery, then -some time later the messaging system delivers the message to a consumer. -The consumer then processes the message and when it is done, it -acknowledges the message. Once the message is acknowledged it disappears -from the queue and is not available to be delivered again. If the system -crashes before the messaging server receives an acknowledgement from the -consumer, then on recovery, the message will be available to be -delivered to a consumer again. - -With point-to-point messaging, there can be many consumers on the queue -but a particular message will only ever be consumed by a maximum of one -of them. Senders (also known as *producers*) to the queue are completely -decoupled from receivers (also known as *consumers*) of the queue - they -do not know of each other's existence. - -A classic example of point to point messaging would be an order queue in -a company's book ordering system. Each order is represented as a message -which is sent to the order queue. Let's imagine there are many front end -ordering systems which send orders to the order queue. When a message -arrives on the queue it is persisted - this ensures that if the server -crashes the order is not lost. Let's also imagine there are many -consumers on the order queue - each representing an instance of an order -processing component - these can be on different physical machines but -consuming from the same queue. The messaging system delivers each -message to one and only one of the ordering processing components. -Different messages can be processed by different order processors, but a -single order is only processed by one order processor - this ensures +With this type of messaging you send a message to a queue. The message is then +typically persisted to provide a guarantee of delivery, then some time later +the messaging system delivers the message to a consumer. The consumer then +processes the message and when it is done, it acknowledges the message. Once +the message is acknowledged it disappears from the queue and is not available +to be delivered again. If the system crashes before the messaging server +receives an acknowledgement from the consumer, then on recovery, the message +will be available to be delivered to a consumer again. + +With point-to-point messaging, there can be many consumers on the queue but a +particular message will only ever be consumed by a maximum of one of them. +Senders (also known as *producers*) to the queue are completely decoupled from +receivers (also known as *consumers*) of the queue - they do not know of each +other's existence. + +A classic example of point to point messaging would be an order queue in a +company's book ordering system. Each order is represented as a message which is +sent to the order queue. Let's imagine there are many front end ordering +systems which send orders to the order queue. When a message arrives on the +queue it is persisted - this ensures that if the server crashes the order is +not lost. Let's also imagine there are many consumers on the order queue - each +representing an instance of an order processing component - these can be on +different physical machines but consuming from the same queue. The messaging +system delivers each message to one and only one of the ordering processing +components. Different messages can be processed by different order processors, +but a single order is only processed by one order processor - this ensures orders aren't processed twice. -As an order processor receives a message, it fulfills the order, sends -order information to the warehouse system and then updates the order -database with the order details. Once it's done that it acknowledges the -message to tell the server that the order has been processed and can be -forgotten about. Often the send to the warehouse system, update in -database and acknowledgement will be completed in a single transaction -to ensure [ACID](https://en.wikipedia.org/wiki/ACID) properties. +As an order processor receives a message, it fulfills the order, sends order +information to the warehouse system and then updates the order database with +the order details. Once it's done that it acknowledges the message to tell the +server that the order has been processed and can be forgotten about. Often the +send to the warehouse system, update in database and acknowledgement will be +completed in a single transaction to ensure +[ACID](https://en.wikipedia.org/wiki/ACID) properties. ### Publish-Subscribe -With publish-subscribe messaging many senders can send messages to an -entity on the server, often called a *topic* (e.g. in the JMS world). +With publish-subscribe messaging many senders can send messages to an entity on +the server, often called a *topic* (e.g. in the JMS world). -There can be many *subscriptions* on a topic, a subscription is just -another word for a consumer of a topic. Each subscription receives a -*copy* of *each* message sent to the topic. This differs from the -message queue pattern where each message is only consumed by a single -consumer. +There can be many *subscriptions* on a topic, a subscription is just another +word for a consumer of a topic. Each subscription receives a *copy* of *each* +message sent to the topic. This differs from the message queue pattern where +each message is only consumed by a single consumer. -Subscriptions can optionally be *durable* which means they retain a copy -of each message sent to the topic until the subscriber consumes them - -even if the server crashes or is restarted in between. Non-durable -subscriptions only last a maximum of the lifetime of the connection that -created them. +Subscriptions can optionally be *durable* which means they retain a copy of +each message sent to the topic until the subscriber consumes them - even if the +server crashes or is restarted in between. Non-durable subscriptions only last +a maximum of the lifetime of the connection that created them. An example of publish-subscribe messaging would be a news feed. As news -articles are created by different editors around the world they are sent -to a news feed topic. There are many subscribers around the world who -are interested in receiving news items - each one creates a subscription -and the messaging system ensures that a copy of each news message is -delivered to each subscription. +articles are created by different editors around the world they are sent to a +news feed topic. There are many subscribers around the world who are interested +in receiving news items - each one creates a subscription and the messaging +system ensures that a copy of each news message is delivered to each +subscription. ## Delivery guarantees -A key feature of most messaging systems is *reliable messaging*. With -reliable messaging the server gives a guarantee that the message will be -delivered once and only once to each consumer of a queue or each durable -subscription of a topic, even in the event of system failure. This is -crucial for many businesses; e.g. you don't want your orders fulfilled -more than once or any of your orders to be lost. +A key feature of most messaging systems is *reliable messaging*. With reliable +messaging the server gives a guarantee that the message will be delivered once +and only once to each consumer of a queue or each durable subscription of a +topic, even in the event of system failure. This is crucial for many +businesses; e.g. you don't want your orders fulfilled more than once or any of +your orders to be lost. -In other cases you may not care about a once and only once delivery -guarantee and are happy to cope with duplicate deliveries or lost -messages - an example of this might be transient stock price updates - -which are quickly superseded by the next update on the same stock. The -messaging system allows you to configure which delivery guarantees you -require. +In other cases you may not care about a once and only once delivery guarantee +and are happy to cope with duplicate deliveries or lost messages - an example +of this might be transient stock price updates - which are quickly superseded +by the next update on the same stock. The messaging system allows you to +configure which delivery guarantees you require. ## Transactions -Messaging systems typically support the sending and acknowledgement of -multiple messages in a single local transaction. Apache ActiveMQ Artemis also supports +Messaging systems typically support the sending and acknowledgement of multiple +messages in a single local transaction. Apache ActiveMQ Artemis also supports the sending and acknowledgement of message as part of a large global transaction - using the Java mapping of XA: JTA. ## Durability -Messages are either durable or non durable. Durable messages will be -persisted in permanent storage and will survive server failure or -restart. Non durable messages will not survive server failure or -restart. Examples of durable messages might be orders or trades, where -they cannot be lost. An example of a non durable message might be a -stock price update which is transitory and doesn't need to survive a -restart. +Messages are either durable or non durable. Durable messages will be persisted +in permanent storage and will survive server failure or restart. Non durable +messages will not survive server failure or restart. Examples of durable +messages might be orders or trades, where they cannot be lost. An example of a +non durable message might be a stock price update which is transitory and +doesn't need to survive a restart. ## Messaging APIs and protocols -How do client applications interact with messaging systems in order to -send and consume messages? +How do client applications interact with messaging systems in order to send and +consume messages? -Several messaging systems provide their own proprietary APIs with which -the client communicates with the messaging system. +Several messaging systems provide their own proprietary APIs with which the +client communicates with the messaging system. -There are also some standard ways of operating with messaging systems -and some emerging standards in this space. +There are also some standard ways of operating with messaging systems and some +emerging standards in this space. Let's take a brief look at these: ### Java Message Service (JMS) -[JMS](https://en.wikipedia.org/wiki/Java_Message_Service) is part of -Oracle's Java EE specification. It's a Java API that encapsulates both message -queue and publish-subscribe messaging patterns. JMS is a lowest common -denominator specification - i.e. it was created to encapsulate common -functionality of the already existing messaging systems that were -available at the time of its creation. +[JMS](https://en.wikipedia.org/wiki/Java_Message_Service) is part of Oracle's +Java EE specification. It's a Java API that encapsulates both message queue and +publish-subscribe messaging patterns. JMS is a lowest common denominator +specification - i.e. it was created to encapsulate common functionality of the +already existing messaging systems that were available at the time of its +creation. -JMS is a very popular API and is implemented by most messaging systems. -JMS is only available to clients running Java. +JMS is a very popular API and is implemented by most messaging systems. JMS is +only available to clients running Java. -JMS does not define a standard wire format - it only defines a -programmatic API so JMS clients and servers from different vendors -cannot directly interoperate since each will use the vendor's own -internal wire protocol. +JMS does not define a standard wire format - it only defines a programmatic API +so JMS clients and servers from different vendors cannot directly interoperate +since each will use the vendor's own internal wire protocol. -Apache ActiveMQ Artemis provides a fully compliant JMS 1.1 and JMS 2.0 API. +Apache ActiveMQ Artemis provides a fully compliant [JMS 1.1 and JMS 2.0 client +implementation](using-jms.md). ### System specific APIs -Many systems provide their own programmatic API for which to interact -with the messaging system. The advantage of this it allows the full set -of system functionality to be exposed to the client application. API's -like JMS are not normally rich enough to expose all the extra features -that most messaging systems provide. +Many systems provide their own programmatic API for which to interact with the +messaging system. The advantage of this it allows the full set of system +functionality to be exposed to the client application. API's like JMS are not +normally rich enough to expose all the extra features that most messaging +systems provide. -Apache ActiveMQ Artemis provides its own core client API for clients to use if they -wish to have access to functionality over and above that accessible via +Apache ActiveMQ Artemis provides its own core client API for clients to use if +they wish to have access to functionality over and above that accessible via the JMS API. +Please see [Core](core.md) for using the Core API with Apache ActiveMQ Artemis. + ### RESTful API [REST](https://en.wikipedia.org/wiki/Representational_State_Transfer) approaches to messaging are showing a lot interest recently. -It seems plausible that API standards for cloud computing may converge -on a REST style set of interfaces and consequently a REST messaging -approach is a very strong contender for becoming the de-facto method for -messaging interoperability. +It seems plausible that API standards for cloud computing may converge on a +REST style set of interfaces and consequently a REST messaging approach is a +very strong contender for becoming the de-facto method for messaging +interoperability. -With a REST approach messaging resources are manipulated as resources -defined by a URI and typically using a simple set of operations on those -resources, e.g. PUT, POST, GET etc. REST approaches to messaging often -use HTTP as their underlying protocol. +With a REST approach messaging resources are manipulated as resources defined +by a URI and typically using a simple set of operations on those resources, +e.g. PUT, POST, GET etc. REST approaches to messaging often use HTTP as their +underlying protocol. -The advantage of a REST approach with HTTP is in its simplicity and the -fact the internet is already tuned to deal with HTTP optimally. +The advantage of a REST approach with HTTP is in its simplicity and the fact +the internet is already tuned to deal with HTTP optimally. -Please see [Rest Interface](rest.md) for using Apache ActiveMQ Artemis's RESTful interface. +Please see [Rest Interface](rest.md) for using Apache ActiveMQ Artemis's +RESTful interface. ### AMQP -[AMQP](https://en.wikipedia.org/wiki/AMQP) is a specification for -interoperable messaging. It also defines a wire format, so any AMQP -client can work with any messaging system that supports AMQP. AMQP -clients are available in many different programming languages. +[AMQP](https://en.wikipedia.org/wiki/AMQP) is a specification for interoperable +messaging. It also defines a wire format, so any AMQP client can work with any +messaging system that supports AMQP. AMQP clients are available in many +different programming languages. Apache ActiveMQ Artemis implements the [AMQP 1.0](https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=amqp) -specification. Any client that supports the 1.0 specification will be -able to interact with Apache ActiveMQ Artemis. +specification. Any client that supports the 1.0 specification will be able to +interact with Apache ActiveMQ Artemis. + +Please see [AMQP](amqp.md) for using AMQP with Apache ActiveMQ Artemis. ### MQTT -[MQTT](https://mqtt.org/) is a lightweight connectivity protocol. It is designed -to run in environments where device and networks are constrained. Out of the box -Apache ActiveMQ Artemis supports version MQTT 3.1.1. Any client supporting this -version of the protocol will work against Apache ActiveMQ Artemis. + +[MQTT](https://mqtt.org/) is a lightweight connectivity protocol. It is +designed to run in environments where device and networks are constrained. Out +of the box Apache ActiveMQ Artemis supports version MQTT 3.1.1. Any client +supporting this version of the protocol will work against Apache ActiveMQ +Artemis. + +Please see [MQTT](mqtt.md) for using MQTT with Apache ActiveMQ Artemis. ### STOMP @@ -244,64 +244,67 @@ theoretically any Stomp client can work with any messaging system that supports Stomp. Stomp clients are available in many different programming languages. -Please see [Stomp](protocols-interoperability.md) for using STOMP with Apache ActiveMQ Artemis. +Please see [Stomp](stomp.md) for using STOMP with Apache ActiveMQ Artemis. + +### OpenWire -### OPENWIRE +ActiveMQ 5.x defines it's own wire protocol: OpenWire. In order to support +ActiveMQ 5.x clients, Apache ActiveMQ Artemis supports OpenWire. Any ActiveMQ +5.12.x or higher can be used with Apache ActiveMQ Artemis. -ActiveMQ 5.x defines it's own wire Protocol "OPENWIRE". In order to support -ActiveMQ 5.x clients, Apache ActiveMQ Artemis supports OPENWIRE. Any ActiveMQ 5.12.x -or higher can be used with Apache ActiveMQ Artemis. +Please see [OpenWire](openwire.md) for using OpenWire with Apache ActiveMQ +Artemis. ## High Availability -High Availability (HA) means that the system should remain operational -after failure of one or more of the servers. The degree of support for -HA varies between various messaging systems. +High Availability (HA) means that the system should remain operational after +failure of one or more of the servers. The degree of support for HA varies +between various messaging systems. Apache ActiveMQ Artemis provides automatic failover where your sessions are -automatically reconnected to the backup server on event of live server -failure. +automatically reconnected to the backup server on event of live server failure. For more information on HA, please see [High Availability and Failover](ha.md). ## Clusters -Many messaging systems allow you to create groups of messaging servers -called *clusters*. Clusters allow the load of sending and consuming -messages to be spread over many servers. This allows your system to -scale horizontally by adding new servers to the cluster. +Many messaging systems allow you to create groups of messaging servers called +*clusters*. Clusters allow the load of sending and consuming messages to be +spread over many servers. This allows your system to scale horizontally by +adding new servers to the cluster. -Degrees of support for clusters varies between messaging systems, with -some systems having fairly basic clusters with the cluster members being -hardly aware of each other. +Degrees of support for clusters varies between messaging systems, with some +systems having fairly basic clusters with the cluster members being hardly +aware of each other. -Apache ActiveMQ Artemis provides very configurable state-of-the-art clustering model -where messages can be intelligently load balanced between the servers in -the cluster, according to the number of consumers on each node, and -whether they are ready for messages. +Apache ActiveMQ Artemis provides very configurable state-of-the-art clustering +model where messages can be intelligently load balanced between the servers in +the cluster, according to the number of consumers on each node, and whether +they are ready for messages. -Apache ActiveMQ Artemis also has the ability to automatically redistribute messages -between nodes of a cluster to prevent starvation on any particular node. +Apache ActiveMQ Artemis also has the ability to automatically redistribute +messages between nodes of a cluster to prevent starvation on any particular +node. For full details on clustering, please see [Clusters](clusters.md). ## Bridges and routing -Some messaging systems allow isolated clusters or single nodes to be -bridged together, typically over unreliable connections like a wide area -network (WAN), or the internet. +Some messaging systems allow isolated clusters or single nodes to be bridged +together, typically over unreliable connections like a wide area network (WAN), +or the internet. -A bridge normally consumes from a queue on one server and forwards -messages to another queue on a different server. Bridges cope with -unreliable connections, automatically reconnecting when the connections -becomes available again. +A bridge normally consumes from a queue on one server and forwards messages to +another queue on a different server. Bridges cope with unreliable connections, +automatically reconnecting when the connections becomes available again. -Apache ActiveMQ Artemis bridges can be configured with filter expressions to only -forward certain messages, and transformation can also be hooked in. +Apache ActiveMQ Artemis bridges can be configured with filter expressions to +only forward certain messages, and transformation can also be hooked in. -Apache ActiveMQ Artemis also allows routing between queues to be configured in server -side configuration. This allows complex routing networks to be set up -forwarding or copying messages from one destination to another, forming -a global network of interconnected brokers. +Apache ActiveMQ Artemis also allows routing between queues to be configured in +server side configuration. This allows complex routing networks to be set up +forwarding or copying messages from one destination to another, forming a +global network of interconnected brokers. -For more information please see [Core Bridges](core-bridges.md) and [Diverting and Splitting Message Flows](diverts.md). +For more information please see [Core Bridges](core-bridges.md) and [Diverting +and Splitting Message Flows](diverts.md). diff --git a/docs/user-manual/en/mqtt.md b/docs/user-manual/en/mqtt.md new file mode 100644 index 00000000000..dfb8da0104c --- /dev/null +++ b/docs/user-manual/en/mqtt.md @@ -0,0 +1,137 @@ +# MQTT + +MQTT is a light weight, client to server, publish / subscribe messaging +protocol. MQTT has been specifically designed to reduce transport overhead +(and thus network traffic) and code footprint on client devices. For this +reason MQTT is ideally suited to constrained devices such as sensors and +actuators and is quickly becoming the defacto standard communication protocol +for IoT. + +Apache ActiveMQ Artemis supports MQTT v3.1.1 (and also the older v3.1 code +message format). By default there are `acceptor` elements configured to accept +MQTT connections on ports `61616` and `1883`. + +See the general [Protocols and Interoperability](protocols-interoperability.md) +chapter for details on configuring an `acceptor` for MQTT. + +The best source of information on the MQTT protocol is in the [3.1.1 +specification](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html). + +Refer to the MQTT examples for a look at some of this functionality in action. + +## MQTT Quality of Service + +MQTT offers 3 quality of service levels. + +Each message (or topic subscription) can define a quality of service that is +associated with it. The quality of service level defined on a topic is the +maximum level a client is willing to accept. The quality of service level on a +message is the desired quality of service level for this message. The broker +will attempt to deliver messages to subscribers at the highest quality of +service level based on what is defined on the message and topic subscription. + +Each quality of service level offers a level of guarantee by which a message is +sent or received: + +- QoS 0: `AT MOST ONCE` + + Guarantees that a particular message is only ever received by the subscriber + a maximum of one time. This does mean that the message may never arrive. The + sender and the receiver will attempt to deliver the message, but if something + fails and the message does not reach it's destination (say due to a network + connection) the message may be lost. This QoS has the least network traffic + overhead and the least burden on the client and the broker and is often useful + for telemetry data where it doesn't matter if some of the data is lost. + +- QoS 1: `AT LEAST ONCE` + + Guarantees that a message will reach it's intended recipient one or more + times. The sender will continue to send the message until it receives an + acknowledgment from the recipient, confirming it has received the message. The + result of this QoS is that the recipient may receive the message multiple + times, and also increases the network overhead than QoS 0, (due to acks). In + addition more burden is placed on the sender as it needs to store the message + and retry should it fail to receive an ack in a reasonable time. + +- QoS 2: `EXACTLY ONCE` + + The most costly of the QoS (in terms of network traffic and burden on sender + and receiver) this QoS will ensure that the message is received by a recipient + exactly one time. This ensures that the receiver never gets any duplicate + copies of the message and will eventually get it, but at the extra cost of + network overhead and complexity required on the sender and receiver. + +## MQTT Retain Messages + +MQTT has an interesting feature in which messages can be "retained" for a +particular address. This means that once a retain message has been sent to an +address, any new subscribers to that address will receive the last sent retain +message before any others messages, this happens even if the retained message +was sent before a client has connected or subscribed. An example of where this +feature might be useful is in environments such as IoT where devices need to +quickly get the current state of a system when they are on boarded into a +system. + +## Will Messages + +A will message can be sent when a client initially connects to a broker. +Clients are able to set a "will message" as part of the connect packet. If the +client abnormally disconnects, say due to a device or network failure the +broker will proceed to publish the will message to the specified address (as +defined also in the connect packet). Other subscribers to the will topic will +receive the will message and can react accordingly. This feature can be useful +in an IoT style scenario to detect errors across a potentially large scale +deployment of devices. + +## Debug Logging + +Detailed protocol logging (e.g. packets in/out) can be activated via the +following steps: + +1. Open `/etc/logging.properties` + +2. Add `org.apache.activemq.artemis.core.protocol.mqtt` to the `loggers` list. + +3. Add this line to enable `TRACE` logging for this new logger: + `logger.org.apache.activemq.artemis.core.protocol.mqtt.level=TRACE` + +4. Ensure the `level` for the `handler` you want to log the message doesn't + block the `TRACE` logging. For example, modify the `level` of the `CONSOLE` + `handler` like so: `handler.CONSOLE.level=TRACE`. + +The MQTT specification doesn't dictate the format of the payloads which clients +publish. As far as the broker is concerned a payload is just just an array of +bytes. However, to facilitate logging the broker will encode the payloads as +UTF-8 strings and print them up to 256 characters. Payload logging is limited +to avoid filling the logs with potentially hundreds of megabytes of unhelpful +information. + + +## Wild card subscriptions + +MQTT addresses are hierarchical much like a file system, and they use a special +character (i.e. `/` by default) to separate hierarchical levels. Subscribers +are able to subscribe to specific topics or to whole branches of a hierarchy. + +To subscribe to branches of an address hierarchy a subscriber can use wild +cards. These wild cards (including the aforementioned separator) are +configurable. See the [Wildcard +Syntax](wildcard-syntax.md#customizing-the-syntax) chapter for details about +how to configure custom wild cards. + +There are 2 types of wild cards in MQTT: + +- **Multi level** (`#` by default) + + Adding this wild card to an address would match all branches of the address + hierarchy under a specified node. For example: `/uk/#` Would match + `/uk/cities`, `/uk/cities/newcastle` and also `/uk/rivers/tyne`. Subscribing to + an address `#` would result in subscribing to all topics in the broker. This + can be useful, but should be done so with care since it has significant + performance implications. + +- **Single level** (`+` by default) + + Matches a single level in the address hierarchy. For example `/uk/+/stores` + would match `/uk/newcastle/stores` but not `/uk/cities/newcastle/stores`. + diff --git a/docs/user-manual/en/network-isolation.md b/docs/user-manual/en/network-isolation.md index 78426e37dfd..d864d7b785a 100644 --- a/docs/user-manual/en/network-isolation.md +++ b/docs/user-manual/en/network-isolation.md @@ -1,15 +1,18 @@ # Network Isolation (Split Brain) -It is possible that if a replicated live or backup server becomes isolated in a network that failover will occur and you will end up -with 2 live servers serving messages in a cluster, this we call split brain. There are different configurations you can choose -from that will help mitigate this problem +It is possible that if a replicated live or backup server becomes isolated in a +network that failover will occur and you will end up with 2 live servers +serving messages in a cluster, this we call split brain. There are different +configurations you can choose from that will help mitigate this problem ## Quorum Voting -Quorum voting is used by both the live and the backup to decide what to do if a replication connection is disconnected. -Basically the server will request each live server in the cluster to vote as to whether it thinks the server it is replicating -to or from is still alive. You can also configure the time for which the quorum manager will wait for the quorum vote response. -The default time is 30 sec you can configure like so for master and also for the slave: +Quorum voting is used by both the live and the backup to decide what to do if a +replication connection is disconnected. Basically the server will request each +live server in the cluster to vote as to whether it thinks the server it is +replicating to or from is still alive. You can also configure the time for which +the quorum manager will wait for the quorum vote response. The default time is 30 +seconds you can configure like so for master and also for the slave: ```xml @@ -21,18 +24,23 @@ The default time is 30 sec you can configure like so for master and also for the ``` -This being the case the minimum number of live/backup pairs needed is 3. If less than 3 pairs -are used then the only option is to use a Network Pinger which is explained later in this chapter or choose how you want each server to -react which the following details: - +This being the case the minimum number of live/backup pairs needed is 3. If less +than 3 pairs are used then the only option is to use a Network Pinger which is +explained later in this chapter or choose how you want each server to react which +the following details: + ### Backup Voting -By default if a replica loses its replication connection to the live broker it makes a decision as to whether to start or not -with a quorum vote. This of course requires that there be at least 3 pairs of live/backup nodes in the cluster. For a 3 node -cluster it will start if it gets 2 votes back saying that its live server is no longer available, for 4 nodes this would be -3 votes and so on. When a backup loses connection to the master it will keep voting for a quorum until it either receives a vote -allowing it to start or it detects that the master is still live. for the latter it will then restart as a backup. How many votes -and how long between each vote the backup should wait is configured like so: +By default if a replica loses its replication connection to the live broker it +makes a decision as to whether to start or not with a quorum vote. This of +course requires that there be at least 3 pairs of live/backup nodes in the +cluster. For a 3 node cluster it will start if it gets 2 votes back saying that +its live server is no longer available, for 4 nodes this would be 3 votes and +so on. When a backup loses connection to the master it will keep voting for a +quorum until it either receives a vote allowing it to start or it detects that +the master is still live. for the latter it will then restart as a backup. How +many votes and how long between each vote the backup should wait is configured +like so: ```xml @@ -45,8 +53,9 @@ and how long between each vote the backup should wait is configured like so: ``` -It's also possible to statically set the quorum size that should be used for the case where the cluster size is known up front, -this is done on the Replica Policy like so: +It's also possible to statically set the quorum size that should be used for +the case where the cluster size is known up front, this is done on the Replica +Policy like so: ```xml @@ -58,16 +67,18 @@ this is done on the Replica Policy like so: ``` -In this example the quorum size is set to 2 so if you were using a single pair and the backup lost connectivity it would -never start. +In this example the quorum size is set to 2 so if you were using a single pair +and the backup lost connectivity it would never start. ### Live Voting -By default, if the live server loses its replication connection then it will just carry on and wait for a backup to reconnect -and start replicating again. In the event of a possible split brain scenario this may mean that the live stays live even though -the backup has been activated. It is possible to configure the live server to vote for a quorum if this happens, in this way -if the live server doesn't not receive a majority vote then it will shutdown. This is done by setting the _vote-on-replication-failure_ -to true. +By default, if the live server loses its replication connection then it will +just carry on and wait for a backup to reconnect and start replicating again. +In the event of a possible split brain scenario this may mean that the live +stays live even though the backup has been activated. It is possible to +configure the live server to vote for a quorum if this happens, in this way if +the live server doesn't not receive a majority vote then it will shutdown. This +is done by setting the _vote-on-replication-failure_ to true. ```xml @@ -79,22 +90,24 @@ to true. ``` -As in the backup policy it is also possible to statically configure the quorum size. +As in the backup policy it is also possible to statically configure the quorum +size. ## Pinging the network -You may configure one more addresses on the broker.xml that are part of your network topology, that will be pinged through the life cycle of the server. +You may configure one more addresses on the broker.xml that are part of your +network topology, that will be pinged through the life cycle of the server. The server will stop itself until the network is back on such case. -If you execute the create command passing a -ping argument, you will create a default xml that is ready to be used with network checks: +If you execute the create command passing a -ping argument, you will create a +default xml that is ready to be used with network checks: ``` ./artemis create /myDir/myServer --ping 10.0.0.1 ``` - This XML part will be added to your broker.xml: ```xml @@ -126,10 +139,8 @@ Use this to use an HTTP server to validate the network ``` - -Once you lose connectivity towards 10.0.0.1 on the given example -, you will see see this output at the server: - +Once you lose connectivity towards 10.0.0.1 on the given example, you will see +see this output at the server: ``` 09:49:24,562 WARN [org.apache.activemq.artemis.core.server.NetworkHealthCheck] Ping Address /10.0.0.1 wasn't reacheable @@ -178,8 +189,9 @@ Once you re establish your network connections towards the configured check list 09:53:23,556 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 1.6.0 [0.0.0.0, nodeID=04fd5dd8-b18c-11e6-9efe-6a0001921ad0] ``` -# Warning - -> Make sure you understand your network topology as this is meant to validate your network. -> Using IPs that could eventually disappear or be partially visible may defeat the purpose. -> You can use a list of multiple IPs. Any successful ping will make the server OK to continue running +> ## Warning +> +> Make sure you understand your network topology as this is meant to validate +> your network. Using IPs that could eventually disappear or be partially +> visible may defeat the purpose. You can use a list of multiple IPs. Any +> successful ping will make the server OK to continue running diff --git a/docs/user-manual/en/openwire.md b/docs/user-manual/en/openwire.md new file mode 100644 index 00000000000..31ada924ce0 --- /dev/null +++ b/docs/user-manual/en/openwire.md @@ -0,0 +1,112 @@ +# OpenWire + +Apache ActiveMQ Artemis supports the +[OpenWire](http://activemq.apache.org/openwire.html) protocol so that an Apache +ActiveMQ 5.x JMS client can talk directly to an Apache ActiveMQ Artemis server. +By default there is an `acceptor` configured to accept OpenWire connections on +port `61616`. + +See the general [Protocols and Interoperability](protocols-interoperability.md) +chapter for details on configuring an `acceptor` for OpenWire. + +Refer to the OpenWire examples for a look at this functionality in action. + +## Connection Monitoring + +OpenWire has a few parameters to control how each connection is monitored, they +are: + +- `maxInactivityDuration` + + It specifies the time (milliseconds) after which the connection is closed by + the broker if no data was received. Default value is 30000. + +- `maxInactivityDurationInitalDelay` + + It specifies the maximum delay (milliseconds) before inactivity monitoring is + started on the connection. It can be useful if a broker is under load with many + connections being created concurrently. Default value is 10000. + +- `useInactivityMonitor` + + A value of false disables the InactivityMonitor completely and connections + will never time out. By default it is enabled. On broker side you don't neet + set this. Instead you can set the connection-ttl to -1. + +- `useKeepAlive` + + Whether or not to send a KeepAliveInfo on an idle connection to prevent it + from timing out. Enabled by default. Disabling the keep alive will still make + connections time out if no data was received on the connection for the + specified amount of time. + +Note at the beginning the InactivityMonitor negotiates the appropriate +`maxInactivityDuration` and `maxInactivityDurationInitalDelay`. The shortest +duration is taken for the connection. + +Fore more details please see [ActiveMQ +InactivityMonitor](http://activemq.apache.org/activemq-inactivitymonitor.html). + +## Disable/Enable Advisories + +By default, advisory topics ([ActiveMQ +Advisory](http://activemq.apache.org/advisory-message.html)) are created in +order to send certain type of advisory messages to listening clients. As a +result, advisory addresses and queues will be displayed on the management +console, along with user deployed addresses and queues. This sometimes cause +confusion because the advisory objects are internally managed without user +being aware of them. In addition, users may not want the advisory topics at all +(they cause extra resources and performance penalty) and it is convenient to +disable them at all from the broker side. + +The protocol provides two parameters to control advisory behaviors on the +broker side. + +- `supportAdvisory` + + Whether or not the broker supports advisory messages. If the value is true, + advisory addresses/queues will be created. If the value is false, no advisory + addresses/queues are created. Default value is `true`. + +- `suppressInternalManagementObjects` + + Whether or not the advisory addresses/queues, if any, will be registered to + management service (e.g. JMX registry). If set to true, no advisory + addresses/queues will be registered. If set to false, those are registered and + will be displayed on the management console. Default value is `true`. + +The two parameters are configured on an OpenWire `acceptor`, e.g.: + +```xml +tcp://localhost:61616?protocols=OPENWIRE;supportAdvisory=true;suppressInternalManagementObjects=false +``` + +## Virtual Topic Consumer Destination Translation + +For existing OpenWire consumers of virtual topic destinations it is possible to +configure a mapping function that will translate the virtual topic consumer +destination into a FQQN address. This address then represents the consumer as a +multicast binding to an address representing the virtual topic. + +The configuration string property `virtualTopicConsumerWildcards` has two parts +seperated by a `;`. The first is the 5.x style destination filter that +identifies the destination as belonging to a virtual topic. The second +identifies the number of `paths` that identify the consumer queue such that it +can be parsed from the destination. For example, the default 5.x virtual topic +with consumer prefix of `Consumer.*.`, would require a +`virtualTopicConsumerWildcards` filter of `Consumer.*.>;2`. As a url parameter +this transforms to `Consumer.*.%3E%3B2` when the url significant characters +`>;` are escaped with their hex code points. In an `acceptor` url it would be: + +```xml +tcp://localhost:61616?protocols=OPENWIRE;virtualTopicConsumerWildcards=Consumer.*.%3E%3B2 +``` + +This will translate `Consumer.A.VirtualTopic.Orders` into a FQQN of +`VirtualTopic.Orders::Consumer.A` using the int component `2` of the +configuration to identify the consumer queue as the first two paths of the +destination. `virtualTopicConsumerWildcards` is multi valued using a `,` +separator. + +Please see Virtual Topic Mapping example contained in the OpenWire +[examples](examples.md). \ No newline at end of file diff --git a/docs/user-manual/en/paging.md b/docs/user-manual/en/paging.md index a6ce648bb2f..60e58d0cf3b 100644 --- a/docs/user-manual/en/paging.md +++ b/docs/user-manual/en/paging.md @@ -1,77 +1,70 @@ # Paging -Apache ActiveMQ Artemis transparently supports huge queues containing millions of -messages while the server is running with limited memory. +Apache ActiveMQ Artemis transparently supports huge queues containing millions +of messages while the server is running with limited memory. -In such a situation it's not possible to store all of the queues in -memory at any one time, so Apache ActiveMQ Artemis transparently *pages* messages into -and out of memory as they are needed, thus allowing massive queues with -a low memory footprint. +In such a situation it's not possible to store all of the queues in memory at +any one time, so Apache ActiveMQ Artemis transparently *pages* messages into +and out of memory as they are needed, thus allowing massive queues with a low +memory footprint. -Apache ActiveMQ Artemis will start paging messages to disk, when the size of all -messages in memory for an address exceeds a configured maximum size. +Apache ActiveMQ Artemis will start paging messages to disk, when the size of +all messages in memory for an address exceeds a configured maximum size. The default configuration from Artemis has destinations with paging. ## Page Files Messages are stored per address on the file system. Each address has an -individual folder where messages are stored in multiple files (page -files). Each file will contain messages up to a max configured size -(`page-size-bytes`). The system will navigate on the files as needed, -and it will remove the page file as soon as all the messages are -acknowledged up to that point. +individual folder where messages are stored in multiple files (page files). +Each file will contain messages up to a max configured size +(`page-size-bytes`). The system will navigate the files as needed, and it +will remove the page file as soon as all the messages are acknowledged up to +that point. Browsers will read through the page-cursor system. -Consumers with selectors will also navigate through the page-files and it will ignore messages that don't match the criteria. +Consumers with selectors will also navigate through the page-files and it will +ignore messages that don't match the criteria. + > *Warning:* -> When you have a queue, and consumers filtering the queue with a very restrictive selector you may get into a situation where you won't be able to read more data from paging until you consume messages from the queue. > -> Example: in one consumer you make a selector as 'color="red"' -> but you only have one color red 1 millions messages after blue, you won't be able to consume red until you consume blue ones. +> When you have a queue, and consumers filtering the queue with a very +> restrictive selector you may get into a situation where you won't be able to +> read more data from paging until you consume messages from the queue. +> +> Example: in one consumer you make a selector as 'color="red"' but you only +> have one color red 1 millions messages after blue, you won't be able to +> consume red until you consume blue ones. > -> This is different to browsing as we will "browse" the entire queue looking for messages and while we "depage" messages while feeding the queue. +> This is different to browsing as we will "browse" the entire queue looking +> for messages and while we "depage" messages while feeding the queue. ### Configuration -You can configure the location of the paging folder - -Global paging parameters are specified on the main configuration file -(`broker.xml`). - - - ... - /somewhere/paging-directory - ... +You can configure the location of the paging folder in `broker.xml`. - Property Name Description Default - -------------------- --------------------------------------------------------------------------------------------------------------------------- ------------- - `paging-directory` Where page files are stored. Apache ActiveMQ Artemis will create one folder for each address being paged under this configured location. data/paging - - : Paging Configuration Parameters +- `paging-directory` Where page files are stored. Apache ActiveMQ Artemis will + create one folder for each address being paged under this configured + location. Default is `data/paging`. ## Paging Mode As soon as messages delivered to an address exceed the configured size, that address alone goes into page mode. -> **Note** +> **Note:** > -> Paging is done individually per address. If you configure a -> max-size-bytes for an address, that means each matching address will -> have a maximum size that you specified. It DOES NOT mean that the -> total overall size of all matching addresses is limited to -> max-size-bytes. +> Paging is done individually per address. If you configure a max-size-bytes +> for an address, that means each matching address will have a maximum size +> that you specified. It DOES NOT mean that the total overall size of all +> matching addresses is limited to max-size-bytes. ### Configuration -Configuration is done at the address settings, done at the main -configuration file (`broker.xml`). +Configuration is done at the address settings in `broker.xml`. ```xml @@ -85,117 +78,90 @@ configuration file (`broker.xml`). This is the list of available parameters on the address settings. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Property NameDescriptionDefault
`max-size-bytes`What's the max memory the address could have before entering on page mode.-1 (disabled)
`page-size-bytes`The size of each page file used on the paging system10MiB (10 \* 1024 \* 1024 bytes)
`address-full-policy`This must be set to PAGE for paging to enable. If the value is PAGE then further messages will be paged to disk. If the value is DROP then further messages will be silently dropped. If the value is FAIL then the messages will be dropped and the client message producers will receive an exception. If the value is BLOCK then client message producers will block when they try and send further messages.PAGE
`page-max-cache-size`The system will keep up to `page-max-cache-size` page files in memory to optimize IO during paging navigation.5
+Property Name|Description|Default +---|---|--- +`max-size-bytes`|What's the max memory the address could have before entering on page mode.|-1 (disabled) +`page-size-bytes`|The size of each page file used on the paging system|10MB +`address-full-policy`|This must be set to `PAGE` for paging to enable. If the value is `PAGE` then further messages will be paged to disk. If the value is `DROP` then further messages will be silently dropped. If the value is `FAIL` then the messages will be dropped and the client message producers will receive an exception. If the value is `BLOCK` then client message producers will block when they try and send further messages.|`PAGE` +`page-max-cache-size`|The system will keep up to `page-max-cache-size` page files in memory to optimize IO during paging navigation.|5 ## Global Max Size -Beyond the max-size-bytes on the address you can also set the global-max-size on the main configuration. If you set max-size-bytes = -1 on paging the global-max-size can still be used. +Beyond the `max-size-bytes` on the address you can also set the global-max-size +on the main configuration. If you set `max-size-bytes` = `-1` on paging the +`global-max-size` can still be used. -When you have more messages than what is configured global-max-size any new produced message will make that destination to go through its paging policy. +When you have more messages than what is configured `global-max-size` any new +produced message will make that destination to go through its paging policy. -global-max-size is calculated as half of the max memory available to the Java Virtual Machine, unless specified on the broker.xml configuration. +`global-max-size` is calculated as half of the max memory available to the Java +Virtual Machine, unless specified on the `broker.xml` configuration. ## Dropping messages -Instead of paging messages when the max size is reached, an address can -also be configured to just drop messages when the address is full. +Instead of paging messages when the max size is reached, an address can also be +configured to just drop messages when the address is full. -To do this just set the `address-full-policy` to `DROP` in the address -settings +To do this just set the `address-full-policy` to `DROP` in the address settings ## Dropping messages and throwing an exception to producers -Instead of paging messages when the max size is reached, an address can -also be configured to drop messages and also throw an exception on the -client-side when the address is full. +Instead of paging messages when the max size is reached, an address can also be +configured to drop messages and also throw an exception on the client-side when +the address is full. -To do this just set the `address-full-policy` to `FAIL` in the address -settings +To do this just set the `address-full-policy` to `FAIL` in the address settings ## Blocking producers -Instead of paging messages when the max size is reached, an address can -also be configured to block producers from sending further messages when -the address is full, thus preventing the memory being exhausted on the -server. +Instead of paging messages when the max size is reached, an address can also be +configured to block producers from sending further messages when the address is +full, thus preventing the memory being exhausted on the server. -When memory is freed up on the server, producers will automatically -unblock and be able to continue sending. +When memory is freed up on the server, producers will automatically unblock and +be able to continue sending. To do this just set the `address-full-policy` to `BLOCK` in the address settings -In the default configuration, all addresses are configured to block -producers after 10 MiB of data are in the address. +In the default configuration, all addresses are configured to block producers +after 10 MiB of data are in the address. ## Caution with Addresses with Multiple Multicast Queues -When a message is routed to an address that has multiple multicast queues bound to -it, e.g. a JMS subscription in a Topic, there is only 1 copy of the -message in memory. Each queue only deals with a reference to this. -Because of this the memory is only freed up once all queues referencing -the message have delivered it. +When a message is routed to an address that has multiple multicast queues bound +to it, e.g. a JMS subscription in a Topic, there is only 1 copy of the message +in memory. Each queue only deals with a reference to this. Because of this the +memory is only freed up once all queues referencing the message have delivered +it. -If you have a single lazy subscription, the entire address will suffer -IO performance hit as all the queues will have messages being sent -through an extra storage on the paging system. +If you have a single lazy subscription, the entire address will suffer IO +performance hit as all the queues will have messages being sent through an +extra storage on the paging system. For example: -- An address has 10 multicast queues +- An address has 10 multicast queues -- One of the queues does not deliver its messages (maybe because of a - slow consumer). +- One of the queues does not deliver its messages (maybe because of a + slow consumer). -- Messages continually arrive at the address and paging is started. +- Messages continually arrive at the address and paging is started. -- The other 9 queues are empty even though messages have been sent. +- The other 9 queues are empty even though messages have been sent. -In this example all the other 9 queues will be consuming messages from -the page system. This may cause performance issues if this is an -undesirable state. +In this example all the other 9 queues will be consuming messages from the page +system. This may cause performance issues if this is an undesirable state. ## Max Disk Usage -The System will perform scans on the disk to determine if the disk is beyond a configured limit. -These are configured through 'max-disk-usage' in percentage. Once that limit is reached any -message will be blocked. (unless the protocol doesn't support flow control on which case there will be an exception thrown and the connection for those clients dropped). +The System will perform scans on the disk to determine if the disk is beyond a +configured limit. These are configured through `max-disk-usage` in percentage. +Once that limit is reached any message will be blocked. (unless the protocol +doesn't support flow control on which case there will be an exception thrown +and the connection for those clients dropped). ## Example -See the [examples](examples.md) chapter for an example which shows how to use paging with Apache ActiveMQ Artemis. +See the [Paging Example](examples.md#paging) which shows how to use paging with +Apache ActiveMQ Artemis. diff --git a/docs/user-manual/en/perf-tuning.md b/docs/user-manual/en/perf-tuning.md index 81f1f445160..3b2d37a64ad 100644 --- a/docs/user-manual/en/perf-tuning.md +++ b/docs/user-manual/en/perf-tuning.md @@ -5,269 +5,251 @@ performance. ## Tuning persistence -- To get the best performance from Apache ActiveMQ Artemis whilst - using persistent messages it is recommended that the file store - is used. Apache ActiveMQ Artemis also supports JDBC persistence, - but there is a performance cost when persisting to a database vs - local disk. -- Put the message journal on its own physical volume. If the disk is - shared with other processes e.g. transaction co-ordinator, database - or other journals which are also reading and writing from it, then - this may greatly reduce performance since the disk head may be - skipping all over the place between the different files. One of the - advantages of an append only journal is that disk head movement is - minimised - this advantage is destroyed if the disk is shared. If - you're using paging or large messages make sure they're ideally put - on separate volumes too. - -- Minimum number of journal files. Set `journal-min-files` to a number - of files that would fit your average sustainable rate. This number - represents the lower threshold of the journal file pool. - -- To set the upper threshold of the journal file pool. (`journal-min-files` being - the lower threshold). Set `journal-pool-files` to a number that represents - something near your maximum expected load. The journal will spill over - the pool should it need to, but will shrink back to the upper threshold, - when possible. This allows reuse of files, without taking up more disk - space than required. If you see new files being created on the journal - data directory too often, i.e. lots of data is being persisted, - you need to increase the journal-pool-size, this way the journal would - reuse more files instead of creating new data files, increasing performance - -- Journal file size. The journal file size should be aligned to the - capacity of a cylinder on the disk. The default value 10MiB should - be enough on most systems. - -- Use AIO journal. If using Linux, try to keep your journal type as - AIO. AIO will scale better than Java NIO. - -- Tune `journal-buffer-timeout`. The timeout can be increased to - increase throughput at the expense of latency. - -- If you're running AIO you might be able to get some better - performance by increasing `journal-max-io`. DO NOT change this - parameter if you are running NIO. - -- If you are 100% sure you don't need power failure durability guarantees, - disable `journal-data-sync` and use `NIO` or `MAPPED` journal: - you'll benefit a huge performance boost on writes - with process failure durability guarantees. +- To get the best performance from Apache ActiveMQ Artemis whilst using + persistent messages it is recommended that the file store is used. Apache + ActiveMQ Artemis also supports JDBC persistence, but there is a performance + cost when persisting to a database vs local disk. + +- Put the message journal on its own physical volume. If the disk is shared + with other processes e.g. transaction co-ordinator, database or other + journals which are also reading and writing from it, then this may greatly + reduce performance since the disk head may be skipping all over the place + between the different files. One of the advantages of an append only journal is + that disk head movement is minimised - this advantage is destroyed if the disk + is shared. If you're using paging or large messages make sure they're ideally + put on separate volumes too. + +- Minimum number of journal files. Set `journal-min-files` to a number of files + that would fit your average sustainable rate. This number represents the + lower threshold of the journal file pool. + +- To set the upper threshold of the journal file pool. (`journal-min-files` being + the lower threshold). Set `journal-pool-files` to a number that represents + something near your maximum expected load. The journal will spill over the + pool should it need to, but will shrink back to the upper threshold, when + possible. This allows reuse of files, without taking up more disk space than + required. If you see new files being created on the journal data directory too + often, i.e. lots of data is being persisted, you need to increase the + journal-pool-size, this way the journal would reuse more files instead of + creating new data files, increasing performance + +- Journal file size. The journal file size should be aligned to the capacity of + a cylinder on the disk. The default value 10MiB should be enough on most + systems. + +- Use `ASYNCIO` journal. If using Linux, try to keep your journal type as + `ASYNCIO`. `ASYNCIO` will scale better than Java NIO. + +- Tune `journal-buffer-timeout`. The timeout can be increased to increase + throughput at the expense of latency. + +- If you're running `ASYNCIO` you might be able to get some better performance by + increasing `journal-max-io`. DO NOT change this parameter if you are running + NIO. + +- If you are 100% sure you don't need power failure durability guarantees, + disable `journal-data-sync` and use `NIO` or `MAPPED` journal: you'll benefit + a huge performance boost on writes with process failure durability guarantees. ## Tuning JMS -There are a few areas where some tweaks can be done if you are using the -JMS API - -- Disable message id. Use the `setDisableMessageID()` method on the - `MessageProducer` class to disable message ids if you don't need - them. This decreases the size of the message and also avoids the - overhead of creating a unique ID. - -- Disable message timestamp. Use the `setDisableMessageTimeStamp()` - method on the `MessageProducer` class to disable message timestamps - if you don't need them. - -- Avoid `ObjectMessage`. `ObjectMessage` is convenient but it comes at - a cost. The body of a `ObjectMessage` uses Java serialization to - serialize it to bytes. The Java serialized form of even small - objects is very verbose so takes up a lot of space on the wire, also - Java serialization is slow compared to custom marshalling - techniques. Only use `ObjectMessage` if you really can't use one of - the other message types, i.e. if you really don't know the type of - the payload until run-time. - -- Avoid `AUTO_ACKNOWLEDGE`. `AUTO_ACKNOWLEDGE` mode requires an - acknowledgement to be sent from the server for each message received - on the client, this means more traffic on the network. If you can, - use `DUPS_OK_ACKNOWLEDGE` or use `CLIENT_ACKNOWLEDGE` or a - transacted session and batch up many acknowledgements with one - acknowledge/commit. - -- Avoid durable messages. By default JMS messages are durable. If you - don't really need durable messages then set them to be non-durable. - Durable messages incur a lot more overhead in persisting them to - storage. - -- Batch many sends or acknowledgements in a single transaction. - Apache ActiveMQ Artemis will only require a network round trip on the commit, not - on every send or acknowledgement. +There are a few areas where some tweaks can be done if you are using the JMS +API + +- Disable message id. Use the `setDisableMessageID()` method on the + `MessageProducer` class to disable message ids if you don't need them. This + decreases the size of the message and also avoids the overhead of creating a + unique ID. + +- Disable message timestamp. Use the `setDisableMessageTimeStamp()` method on + the `MessageProducer` class to disable message timestamps if you don't need + them. + +- Avoid `ObjectMessage`. `ObjectMessage` is convenient but it comes at a cost. + The body of a `ObjectMessage` uses Java serialization to serialize it to + bytes. The Java serialized form of even small objects is very verbose so takes + up a lot of space on the wire, also Java serialization is slow compared to + custom marshalling techniques. Only use `ObjectMessage` if you really can't use + one of the other message types, i.e. if you really don't know the type of the + payload until run-time. + +- Avoid `AUTO_ACKNOWLEDGE`. `AUTO_ACKNOWLEDGE` mode requires an acknowledgement + to be sent from the server for each message received on the client, this + means more traffic on the network. If you can, use `DUPS_OK_ACKNOWLEDGE` or use + `CLIENT_ACKNOWLEDGE` or a transacted session and batch up many acknowledgements + with one acknowledge/commit. + +- Avoid durable messages. By default JMS messages are durable. If you don't + really need durable messages then set them to be non-durable. Durable + messages incur a lot more overhead in persisting them to storage. + +- Batch many sends or acknowledgements in a single transaction. Apache + ActiveMQ Artemis will only require a network round trip on the commit, not on + every send or acknowledgement. ## Other Tunings -There are various other places in Apache ActiveMQ Artemis where we can perform some -tuning: - -- Use Asynchronous Send Acknowledgements. If you need to send durable - messages non transactionally and you need a guarantee that they have - reached the server by the time the call to send() returns, don't set - durable messages to be sent blocking, instead use asynchronous send - acknowledgements to get your acknowledgements of send back in a - separate stream, see [Guarantees of sends and commits](send-guarantees.md) - for more information on this. - -- Use pre-acknowledge mode. With pre-acknowledge mode, messages are - acknowledged `before` they are sent to the client. This reduces the - amount of acknowledgement traffic on the wire. For more information - on this, see [Extra Acknowledge Modes](pre-acknowledge.md). - -- Disable security. You may get a small performance boost by disabling - security by setting the `security-enabled` parameter to `false` in - `broker.xml`. - -- Disable persistence. If you don't need message persistence, turn it - off altogether by setting `persistence-enabled` to false in - `broker.xml`. - -- Sync transactions lazily. Setting `journal-sync-transactional` to - `false` in `broker.xml` can give you better - transactional persistent performance at the expense of some - possibility of loss of transactions on failure. See [Guarantees of sends and commits](send-guarantees.md) - for more information. - -- Sync non transactional lazily. Setting - `journal-sync-non-transactional` to `false` in - `broker.xml` can give you better non-transactional - persistent performance at the expense of some possibility of loss of - durable messages on failure. See [Guarantees of sends and commits](send-guarantees.md) - for more information. - -- Send messages non blocking. Setting `block-on-durable-send` and - `block-on-non-durable-send` to `false` in the jms config (if - you're using JMS and JNDI) or directly on the ServerLocator. This - means you don't have to wait a whole network round trip for every - message sent. See [Guarantees of sends and commits](send-guarantees.md) - for more information. - -- If you have very fast consumers, you can increase - consumer-window-size. This effectively disables consumer flow - control. - -- Use the core API not JMS. Using the JMS API you will have slightly - lower performance than using the core API, since all JMS operations - need to be translated into core operations before the server can - handle them. If using the core API try to use methods that take - `SimpleString` as much as possible. `SimpleString`, unlike - java.lang.String does not require copying before it is written to - the wire, so if you re-use `SimpleString` instances between calls - then you can avoid some unnecessary copying. - -- If using frameworks like Spring, configure destinations permanently broker side - and enable `destinationCache` on the client side. - See the [Setting The Destination Cache](using-jms.md) - for more information on this. +There are various other places in Apache ActiveMQ Artemis where we can perform +some tuning: + +- Use Asynchronous Send Acknowledgements. If you need to send durable messages + non transactionally and you need a guarantee that they have reached the + server by the time the call to send() returns, don't set durable messages to be + sent blocking, instead use asynchronous send acknowledgements to get your + acknowledgements of send back in a separate stream, see [Guarantees of sends + and commits](send-guarantees.md) for more information on this. + +- Use pre-acknowledge mode. With pre-acknowledge mode, messages are + acknowledged `before` they are sent to the client. This reduces the amount of + acknowledgement traffic on the wire. For more information on this, see [Extra + Acknowledge Modes](pre-acknowledge.md). + +- Disable security. You may get a small performance boost by disabling security + by setting the `security-enabled` parameter to `false` in `broker.xml`. + +- Disable persistence. If you don't need message persistence, turn it off + altogether by setting `persistence-enabled` to false in `broker.xml`. + +- Sync transactions lazily. Setting `journal-sync-transactional` to `false` in + `broker.xml` can give you better transactional persistent performance at the + expense of some possibility of loss of transactions on failure. See + [Guarantees of sends and commits](send-guarantees.md) for more information. + +- Sync non transactional lazily. Setting `journal-sync-non-transactional` to + `false` in `broker.xml` can give you better non-transactional persistent + performance at the expense of some possibility of loss of durable messages on + failure. See [Guarantees of sends and commits](send-guarantees.md) for more + information. + +- Send messages non blocking. Setting `block-on-durable-send` and + `block-on-non-durable-send` to `false` in the jms config (if you're using JMS + and JNDI) or directly on the ServerLocator. This means you don't have to wait a + whole network round trip for every message sent. See [Guarantees of sends and + commits](send-guarantees.md) for more information. + +- If you have very fast consumers, you can increase consumer-window-size. This + effectively disables consumer flow control. + +- Use the core API not JMS. Using the JMS API you will have slightly lower + performance than using the core API, since all JMS operations need to be + translated into core operations before the server can handle them. If using the + core API try to use methods that take `SimpleString` as much as possible. + `SimpleString`, unlike java.lang.String does not require copying before it is + written to the wire, so if you re-use `SimpleString` instances between calls + then you can avoid some unnecessary copying. + +- If using frameworks like Spring, configure destinations permanently broker + side and enable `destinationCache` on the client side. See the [Setting The + Destination Cache](using-jms.md) for more information on this. ## Tuning Transport Settings -- TCP buffer sizes. If you have a fast network and fast machines you - may get a performance boost by increasing the TCP send and receive - buffer sizes. See the [Configuring the Transport](configuring-transports.md) - for more information on this. - - > **Note** - > - > Note that some operating systems like later versions of Linux - > include TCP auto-tuning and setting TCP buffer sizes manually can - > prevent auto-tune from working and actually give you worse - > performance! - -- Increase limit on file handles on the server. If you expect a lot of - concurrent connections on your servers, or if clients are rapidly - opening and closing connections, you should make sure the user - running the server has permission to create sufficient file handles. - - This varies from operating system to operating system. On Linux - systems you can increase the number of allowable open file handles - in the file `/etc/security/limits.conf` e.g. add the lines - - serveruser soft nofile 20000 - serveruser hard nofile 20000 - - This would allow up to 20000 file handles to be open by the user - `serveruser`. - -- Use `batch-delay` and set `direct-deliver` to false for the best - throughput for very small messages. Apache ActiveMQ Artemis comes with a - preconfigured connector/acceptor pair (`netty-throughput`) in - `broker.xml` and JMS connection factory - (`ThroughputConnectionFactory`) in `activemq-jms.xml`which can be - used to give the very best throughput, especially for small - messages. See the [Configuring the Transport](configuring-transports.md) - for more information on this. +- TCP buffer sizes. If you have a fast network and fast machines you may get a + performance boost by increasing the TCP send and receive buffer sizes. See + the [Configuring the Transport](configuring-transports.md) for more information + on this. + + > **Note:** + > + > Note that some operating systems like later versions of Linux include TCP + > auto-tuning and setting TCP buffer sizes manually can prevent auto-tune + > from working and actually give you worse performance! + +- Increase limit on file handles on the server. If you expect a lot of + concurrent connections on your servers, or if clients are rapidly opening and + closing connections, you should make sure the user running the server has + permission to create sufficient file handles. + + This varies from operating system to operating system. On Linux systems you + can increase the number of allowable open file handles in the file + `/etc/security/limits.conf` e.g. add the lines + + ``` + serveruser soft nofile 20000 + serveruser hard nofile 20000 + ``` + + This would allow up to 20000 file handles to be open by the user + `serveruser`. + +- Use `batch-delay` and set `direct-deliver` to false for the best throughput + for very small messages. Apache ActiveMQ Artemis comes with a preconfigured + connector/acceptor pair (`netty-throughput`) in `broker.xml` and JMS connection + factory (`ThroughputConnectionFactory`) in `activemq-jms.xml`which can be used + to give the very best throughput, especially for small messages. See the + [Configuring the Transport](configuring-transports.md) for more information on + this. ## Tuning the VM -We highly recommend you use the latest Java JVM for the best -performance. We test internally using the Sun JVM, so some of these -tunings won't apply to JDKs from other providers (e.g. IBM or JRockit) - -- Garbage collection. For smooth server operation we recommend using a - parallel garbage collection algorithm, e.g. using the JVM argument - `-XX:+UseParallelOldGC` on Sun JDKs. - -- Memory settings. Give as much memory as you can to the server. - Apache ActiveMQ Artemis can run in low memory by using paging (described in [Paging](paging.md)) but - if it can run with all queues in RAM this will improve performance. - The amount of memory you require will depend on the size and number - of your queues and the size and number of your messages. Use the JVM - arguments `-Xms` and `-Xmx` to set server available RAM. We - recommend setting them to the same high value. - - When under periods of high load, it is likely that Artemis will be generating - and destroying lots of objects. This can result in a build up of stale objects. - To reduce the chance of running out of memory and causing a full GC - (which may introduce pauses and unintentional behaviour), it is recommended that the - max heap size (`-Xmx`) for the JVM is set at least to 5 x the `global-max-size` of the broker. - As an example, in a situation where the broker is under high load and running - with a `global-max-size` of 1GB, it is recommended the the max heap size is set to 5GB. - -- Aggressive options. Different JVMs provide different sets of JVM - tuning parameters, for the Sun Hotspot JVM the full list of options - is available - [here](http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html). - We recommend at least using `-XX:+AggressiveOpts`. - You may get some mileage with the other tuning parameters depending - on your OS platform and application usage patterns. +We highly recommend you use the latest Java JVM for the best performance. We +test internally using the Sun JVM, so some of these tunings won't apply to JDKs +from other providers (e.g. IBM or JRockit) + +- Garbage collection. For smooth server operation we recommend using a parallel + garbage collection algorithm, e.g. using the JVM argument + `-XX:+UseParallelOldGC` on Sun JDKs. + +- Memory settings. Give as much memory as you can to the server. Apache + ActiveMQ Artemis can run in low memory by using paging (described in + [Paging](paging.md)) but if it can run with all queues in RAM this will improve + performance. The amount of memory you require will depend on the size and + number of your queues and the size and number of your messages. Use the JVM + arguments `-Xms` and `-Xmx` to set server available RAM. We recommend setting + them to the same high value. + + When under periods of high load, it is likely that Artemis will be generating + and destroying lots of objects. This can result in a build up of stale objects. + To reduce the chance of running out of memory and causing a full GC (which may + introduce pauses and unintentional behaviour), it is recommended that the max + heap size (`-Xmx`) for the JVM is set at least to 5 x the `global-max-size` of + the broker. As an example, in a situation where the broker is under high load + and running with a `global-max-size` of 1GB, it is recommended the the max heap + size is set to 5GB. + +- Aggressive options. Different JVMs provide different sets of JVM tuning + parameters, for the Sun Hotspot JVM the full list of options is available + [here](http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html). + We recommend at least using `-XX:+AggressiveOpts`. You may get some mileage + with the other tuning parameters depending on your OS platform and application + usage patterns. ## Avoiding Anti-Patterns -- Re-use connections / sessions / consumers / producers. Probably the - most common messaging anti-pattern we see is users who create a new - connection/session/producer for every message they send or every - message they consume. This is a poor use of resources. These objects - take time to create and may involve several network round trips. - Always re-use them. - - > **Note** - > - > Some popular libraries such as the Spring JMS Template are known - > to use these anti-patterns. If you're using Spring JMS Template - > and you're getting poor performance you know why. Don't blame - > Apache ActiveMQ Artemis! The Spring JMS Template can only safely be used in an - > app server which caches JMS sessions (e.g. using JCA), and only - > then for sending messages. It cannot be safely be used for - > synchronously consuming messages, even in an app server. - -- Avoid fat messages. Verbose formats such as XML take up a lot of - space on the wire and performance will suffer as result. Avoid XML - in message bodies if you can. - -- Don't create temporary queues for each request. This common - anti-pattern involves the temporary queue request-response pattern. - With the temporary queue request-response pattern a message is sent - to a target and a reply-to header is set with the address of a local - temporary queue. When the recipient receives the message they - process it then send back a response to the address specified in the - reply-to. A common mistake made with this pattern is to create a new - temporary queue on each message sent. This will drastically reduce - performance. Instead the temporary queue should be re-used for many - requests. - -- Don't use Message-Driven Beans for the sake of it. As soon as you - start using MDBs you are greatly increasing the codepath for each - message received compared to a straightforward message consumer, - since a lot of extra application server code is executed. Ask - yourself do you really need MDBs? Can you accomplish the same task - using just a normal message consumer? +- Re-use connections / sessions / consumers / producers. Probably the most + common messaging anti-pattern we see is users who create a new + connection/session/producer for every message they send or every message they + consume. This is a poor use of resources. These objects take time to create and + may involve several network round trips. Always re-use them. + + > **Note:** + > + > Some popular libraries such as the Spring JMS Template are known to use + > these anti-patterns. If you're using Spring JMS Template and you're getting + > poor performance you know why. Don't blame Apache ActiveMQ Artemis! The + > Spring JMS Template can only safely be used in an app server which caches + > JMS sessions (e.g. using JCA), and only then for sending messages. It + > cannot be safely be used for synchronously consuming messages, even in an + > app server. + +- Avoid fat messages. Verbose formats such as XML take up a lot of space on the + wire and performance will suffer as result. Avoid XML in message bodies if + you can. + +- Don't create temporary queues for each request. This common anti-pattern + involves the temporary queue request-response pattern. With the temporary + queue request-response pattern a message is sent to a target and a reply-to + header is set with the address of a local temporary queue. When the recipient + receives the message they process it then send back a response to the address + specified in the reply-to. A common mistake made with this pattern is to create + a new temporary queue on each message sent. This will drastically reduce + performance. Instead the temporary queue should be re-used for many requests. + +- Don't use Message-Driven Beans for the sake of it. As soon as you start using + MDBs you are greatly increasing the codepath for each message received + compared to a straightforward message consumer, since a lot of extra + application server code is executed. Ask yourself do you really need MDBs? Can + you accomplish the same task using just a normal message consumer? ## Troubleshooting @@ -275,17 +257,30 @@ tunings won't apply to JDKs from other providers (e.g. IBM or JRockit) In certain situations UDP used on discovery may not work. Typical situations are: -1. The nodes are behind a firewall. If your nodes are on different machines then it is possible that the firewall is blocking the multicasts. you can test this by disabling the firewall for each node or adding the appropriate rules. -2. You are using a home network or are behind a gateway. Typically home networks will redirect any UDP traffic to the Internet Service Provider which is then either dropped by the ISP or just lost. To fix this you will need to add a route to the firewall/gateway that will redirect any multicast traffic back on to the local network instead. -3. All the nodes are in one machine. If this is the case then it is a similar problem to point 2 and the same solution should fix it. Alternatively you could add a multicast route to the loopback interface. On linux the command would be: -```sh -# you should run this as root -route add -net 224.0.0.0 netmask 240.0.0.0 dev lo -``` - This will redirect any traffic directed to the 224.0.0.0 to the loopback interface. This will also work if you have no network at all. - - * on Mac OS X, the command is slightly different: -```sh -sudo route add 224.0.0.0 127.0.0.1 -netmask 240.0.0.0 -``` +1. The nodes are behind a firewall. If your nodes are on different machines + then it is possible that the firewall is blocking the multicasts. you can + test this by disabling the firewall for each node or adding the appropriate + rules. +2. You are using a home network or are behind a gateway. Typically home + networks will redirect any UDP traffic to the Internet Service Provider + which is then either dropped by the ISP or just lost. To fix this you will need + to add a route to the firewall/gateway that will redirect any multicast traffic + back on to the local network instead. +3. All the nodes are in one machine. If this is the case then it is a similar + problem to point 2 and the same solution should fix it. Alternatively you + could add a multicast route to the loopback interface. On linux the command + would be: + + ```sh + # you should run this as root + route add -net 224.0.0.0 netmask 240.0.0.0 dev lo + ``` + + This will redirect any traffic directed to the 224.0.0.0 to the loopback + interface. This will also work if you have no network at all. On Mac OS X, the + command is slightly different: + + ```sh + sudo route add 224.0.0.0 127.0.0.1 -netmask 240.0.0.0 + ``` diff --git a/docs/user-manual/en/persistence.md b/docs/user-manual/en/persistence.md index 00159e437ed..b0b253bd5f4 100644 --- a/docs/user-manual/en/persistence.md +++ b/docs/user-manual/en/persistence.md @@ -59,7 +59,7 @@ completion when AIO informs us that the data has been persisted. Using AIO will typically provide even better performance than using Java NIO. -The AIO journal is only available when running Linux kernel 2.6 or +This journal option is only available when running Linux kernel 2.6 or later and after having installed libaio (if it's not already installed). For instructions on how to install libaio please see Installing AIO section. @@ -69,53 +69,53 @@ systems: ext2, ext3, ext4, jfs, xfs and NFSV4. For more information on libaio please see [lib AIO](libaio.md). libaio is part of the kernel project. - + ### [Memory mapped](https://en.wikipedia.org/wiki/Memory-mapped_file) The third implementation uses a file-backed [READ_WRITE](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/FileChannel.MapMode.html#READ_WRITE) memory mapping against the OS page cache to interface with the file system. - + This provides extremely good performance (especially under strictly process failure durability requirements), almost zero copy (actually *is* the kernel page cache) and zero garbage (from the Java HEAP perspective) operations and runs on any platform where there's a Java 4+ runtime. - + Under power failure durability requirements it will perform at least on par with the NIO journal with the only exception of Linux OS with kernel less or equals 2.6, in which the [*msync*](https://docs.oracle.com/javase/8/docs/api/java/nio/MappedByteBuffer.html#force%28%29)) implementation necessary to ensure durable writes was different (and slower) from the [*fsync*](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/FileChannel.html#force%28boolean%29) used is case of NIO journal. - + It benefits by the configuration of OS [huge pages](https://en.wikipedia.org/wiki/Page_%28computer_memory%29), -in particular when is used a big number of journal files and sizing them as multiple of the OS page size in bytes. +in particular when is used a big number of journal files and sizing them as multiple of the OS page size in bytes. ### Standard Files The standard Apache ActiveMQ Artemis core server uses two instances of the journal: -- Bindings journal. +- Bindings journal. - This journal is used to store bindings related data. That includes - the set of queues that are deployed on the server and their - attributes. It also stores data such as id sequence counters. + This journal is used to store bindings related data. That includes + the set of queues that are deployed on the server and their + attributes. It also stores data such as id sequence counters. - The bindings journal is always a NIO journal as it is typically low - throughput compared to the message journal. + The bindings journal is always a NIO journal as it is typically low + throughput compared to the message journal. - The files on this journal are prefixed as `activemq-bindings`. Each - file has a `bindings` extension. File size is `1048576`, and it is - located at the bindings folder. + The files on this journal are prefixed as `activemq-bindings`. Each + file has a `bindings` extension. File size is `1048576`, and it is + located at the bindings folder. -- Message journal. +- Message journal. - This journal instance stores all message related data, including the - message themselves and also duplicate-id caches. + This journal instance stores all message related data, including the + message themselves and also duplicate-id caches. - By default Apache ActiveMQ Artemis will try and use an AIO journal. If AIO is not - available, e.g. the platform is not Linux with the correct kernel - version or AIO has not been installed then it will automatically - fall back to using Java NIO which is available on any Java platform. + By default Apache ActiveMQ Artemis will try and use an AIO journal. If AIO is not + available, e.g. the platform is not Linux with the correct kernel + version or AIO has not been installed then it will automatically + fall back to using Java NIO which is available on any Java platform. - The files on this journal are prefixed as `activemq-data`. Each file - has a `amq` extension. File size is by the default `10485760` - (configurable), and it is located at the journal folder. + The files on this journal are prefixed as `activemq-data`. Each file + has a `amq` extension. File size is by the default `10485760` + (configurable), and it is located at the journal folder. For large messages, Apache ActiveMQ Artemis persists them outside the message journal. This is discussed in [Large Messages](large-messages.md). @@ -132,17 +132,17 @@ the broker for Zero Persistence section. The bindings journal is configured using the following attributes in `broker.xml` -- `bindings-directory` +- `bindings-directory` - This is the directory in which the bindings journal lives. The - default value is `data/bindings`. + This is the directory in which the bindings journal lives. The + default value is `data/bindings`. -- `create-bindings-dir` +- `create-bindings-dir` - If this is set to `true` then the bindings directory will be - automatically created at the location specified in - `bindings-directory` if it does not already exist. The default value - is `true` + If this is set to `true` then the bindings directory will be + automatically created at the location specified in + `bindings-directory` if it does not already exist. The default value + is `true` #### Configuring the jms journal @@ -153,159 +153,158 @@ The jms config shares its configuration with the bindings journal. The message journal is configured using the following attributes in `broker.xml` -- `journal-directory` +- `journal-directory` - This is the directory in which the message journal lives. The - default value is `data/journal`. + This is the directory in which the message journal lives. The + default value is `data/journal`. - For the best performance, we recommend the journal is located on its - own physical volume in order to minimise disk head movement. If the - journal is on a volume which is shared with other processes which - might be writing other files (e.g. bindings journal, database, or - transaction coordinator) then the disk head may well be moving - rapidly between these files as it writes them, thus drastically - reducing performance. + For the best performance, we recommend the journal is located on its + own physical volume in order to minimise disk head movement. If the + journal is on a volume which is shared with other processes which + might be writing other files (e.g. bindings journal, database, or + transaction coordinator) then the disk head may well be moving + rapidly between these files as it writes them, thus drastically + reducing performance. - When the message journal is stored on a SAN we recommend each - journal instance that is stored on the SAN is given its own LUN - (logical unit). + When the message journal is stored on a SAN we recommend each + journal instance that is stored on the SAN is given its own LUN + (logical unit). -- `create-journal-dir` +- `create-journal-dir` - If this is set to `true` then the journal directory will be - automatically created at the location specified in - `journal-directory` if it does not already exist. The default value - is `true` + If this is set to `true` then the journal directory will be + automatically created at the location specified in + `journal-directory` if it does not already exist. The default value + is `true` -- `journal-type` +- `journal-type` - Valid values are `NIO`, `ASYNCIO` or `MAPPED`. + Valid values are `NIO`, `ASYNCIO` or `MAPPED`. - Choosing `NIO` chooses the Java NIO journal. Choosing `ASYNCIO` chooses - the Linux asynchronous IO journal. If you choose `ASYNCIO` but are not - running Linux or you do not have libaio installed then Apache ActiveMQ Artemis will - detect this and automatically fall back to using `NIO`. - Choosing `MAPPED` chooses the Java Memory Mapped journal. + Choosing `NIO` chooses the Java NIO journal. Choosing `ASYNCIO` chooses + the Linux asynchronous IO journal. If you choose `ASYNCIO` but are not + running Linux or you do not have libaio installed then Apache ActiveMQ Artemis will + detect this and automatically fall back to using `NIO`. + Choosing `MAPPED` chooses the Java Memory Mapped journal. -- `journal-sync-transactional` +- `journal-sync-transactional` - If this is set to true then Apache ActiveMQ Artemis will make sure all transaction - data is flushed to disk on transaction boundaries (commit, prepare - and rollback). The default value is `true`. + If this is set to true then Apache ActiveMQ Artemis will make sure all transaction + data is flushed to disk on transaction boundaries (commit, prepare + and rollback). The default value is `true`. -- `journal-sync-non-transactional` +- `journal-sync-non-transactional` - If this is set to true then Apache ActiveMQ Artemis will make sure non - transactional message data (sends and acknowledgements) are flushed - to disk each time. The default value for this is `true`. + If this is set to true then Apache ActiveMQ Artemis will make sure non + transactional message data (sends and acknowledgements) are flushed + to disk each time. The default value for this is `true`. -- `journal-file-size` +- `journal-file-size` - The size of each journal file in bytes. The default value for this - is `10485760` bytes (10MiB). + The size of each journal file in bytes. The default value for this + is `10485760` bytes (10MiB). -- `journal-min-files` +- `journal-min-files` - The minimum number of files the journal will maintain. When Apache ActiveMQ Artemis - starts and there is no initial message data, Apache ActiveMQ Artemis will - pre-create `journal-min-files` number of files. + The minimum number of files the journal will maintain. When Apache ActiveMQ Artemis + starts and there is no initial message data, Apache ActiveMQ Artemis will + pre-create `journal-min-files` number of files. - Creating journal files and filling them with padding is a fairly - expensive operation and we want to minimise doing this at run-time - as files get filled. By pre-creating files, as one is filled the - journal can immediately resume with the next one without pausing to - create it. + Creating journal files and filling them with padding is a fairly + expensive operation and we want to minimise doing this at run-time + as files get filled. By pre-creating files, as one is filled the + journal can immediately resume with the next one without pausing to + create it. - Depending on how much data you expect your queues to contain at - steady state you should tune this number of files to match that - total amount of data. + Depending on how much data you expect your queues to contain at + steady state you should tune this number of files to match that + total amount of data. -- `journal-pool-files` +- `journal-pool-files` - The system will create as many files as needed however when reclaiming files - it will shrink back to the `journal-pool-files`. + The system will create as many files as needed however when reclaiming files + it will shrink back to the `journal-pool-files`. - The default to this parameter is -1, meaning it will never delete files on the journal once created. + The default to this parameter is -1, meaning it will never delete files on the journal once created. - Notice that the system can't grow infinitely as you are still required to use paging for destinations that can - grow indefinitely. + Notice that the system can't grow infinitely as you are still required to use paging for destinations that can + grow indefinitely. - Notice: in case you get too many files you can use [compacting](tools.md). + Notice: in case you get too many files you can use [compacting](data-tools.md). -- `journal-max-io` +- `journal-max-io` - Write requests are queued up before being submitted to the system - for execution. This parameter controls the maximum number of write - requests that can be in the IO queue at any one time. If the queue - becomes full then writes will block until space is freed up. + Write requests are queued up before being submitted to the system + for execution. This parameter controls the maximum number of write + requests that can be in the IO queue at any one time. If the queue + becomes full then writes will block until space is freed up. - When using NIO, this value should always be equal to `1` + When using NIO, this value should always be equal to `1` - When using AIO, the default should be `500`. + When using ASYNCIO, the default should be `500`. - The system maintains different defaults for this parameter depending - on whether it's NIO or AIO (default for NIO is 1, default for AIO is - 500) + The system maintains different defaults for this parameter depending + on whether it's NIO or ASYNCIO (default for NIO is 1, default for ASYNCIO is + 500) - There is a limit and the total max AIO can't be higher than what is - configured at the OS level (/proc/sys/fs/aio-max-nr) usually at - 65536. + There is a limit and the total max ASYNCIO can't be higher than what is + configured at the OS level (/proc/sys/fs/aio-max-nr) usually at + 65536. -- `journal-buffer-timeout` +- `journal-buffer-timeout` - Instead of flushing on every write that requires a flush, we - maintain an internal buffer, and flush the entire buffer either when - it is full, or when a timeout expires, whichever is sooner. This is - used for both NIO and AIO and allows the system to scale better with - many concurrent writes that require flushing. + Instead of flushing on every write that requires a flush, we + maintain an internal buffer, and flush the entire buffer either when + it is full, or when a timeout expires, whichever is sooner. This is + used for both NIO and ASYNCIO and allows the system to scale better with + many concurrent writes that require flushing. - This parameter controls the timeout at which the buffer will be - flushed if it hasn't filled already. AIO can typically cope with a - higher flush rate than NIO, so the system maintains different - defaults for both NIO and AIO (default for NIO is 3333333 - nanoseconds - 300 times per second, default for AIO is 500000 - nanoseconds - ie. 2000 times per second). + This parameter controls the timeout at which the buffer will be + flushed if it hasn't filled already. ASYNCIO can typically cope with a + higher flush rate than NIO, so the system maintains different + defaults for both NIO and ASYNCIO (default for NIO is 3333333 + nanoseconds - 300 times per second, default for ASYNCIO is 500000 + nanoseconds - ie. 2000 times per second). - > **Note** - > - > By increasing the timeout, you may be able to increase system - > throughput at the expense of latency, the default parameters are - > chosen to give a reasonable balance between throughput and - > latency. + > **Note:** + > + > By increasing the timeout, you may be able to increase system + > throughput at the expense of latency, the default parameters are + > chosen to give a reasonable balance between throughput and + > latency. -- `journal-buffer-size` +- `journal-buffer-size` - The size of the timed buffer on AIO. The default value is `490KiB`. + The size of the timed buffer on ASYNCIO. The default value is `490KiB`. -- `journal-compact-min-files` +- `journal-compact-min-files` - The minimal number of files before we can consider compacting the - journal. The compacting algorithm won't start until you have at - least `journal-compact-min-files` + The minimal number of files before we can consider compacting the + journal. The compacting algorithm won't start until you have at + least `journal-compact-min-files` - Setting this to 0 will disable the feature to compact completely. - This could be dangerous though as the journal could grow indefinitely. - Use it wisely! + Setting this to 0 will disable the feature to compact completely. + This could be dangerous though as the journal could grow indefinitely. + Use it wisely! + The default for this parameter is `10` - The default for this parameter is `10` +- `journal-compact-percentage` -- `journal-compact-percentage` + The threshold to start compacting. When less than this percentage is + considered live data, we start compacting. Note also that compacting + won't kick in until you have at least `journal-compact-min-files` + data files on the journal - The threshold to start compacting. When less than this percentage is - considered live data, we start compacting. Note also that compacting - won't kick in until you have at least `journal-compact-min-files` - data files on the journal - - The default for this parameter is `30` - -- `journal-datasync` (default: true) - - This will disable the use of fdatasync on journal writes. - When enabled it ensures full power failure durability, otherwise - process failure durability on journal writes (OS guaranteed). - This is particular effective for `NIO` and `MAPPED` journals, which rely on - *fsync*/*msync* to force write changes to disk. + The default for this parameter is `30` + +- `journal-datasync` (default: true) + + This will disable the use of fdatasync on journal writes. + When enabled it ensures full power failure durability, otherwise + process failure durability on journal writes (OS guaranteed). + This is particular effective for `NIO` and `MAPPED` journals, which rely on + *fsync*/*msync* to force write changes to disk. #### Note on disabling `journal-datasync` @@ -362,9 +361,9 @@ The message journal is configured using the following attributes in The Java NIO journal gives great performance, but If you are running Apache ActiveMQ Artemis using Linux Kernel 2.6 or later, we highly recommend you use -the `AIO` journal for the very best persistence performance. +the `ASYNCIO` journal for the very best persistence performance. -It's not possible to use the AIO journal under other operating systems +It's not possible to use the ASYNCIO journal under other operating systems or earlier versions of the Linux kernel. If you are running Linux kernel 2.6 or later and don't already have @@ -372,11 +371,15 @@ If you are running Linux kernel 2.6 or later and don't already have Using yum, (e.g. on Fedora or Red Hat Enterprise Linux): - yum install libaio +```sh +yum install libaio +``` Using aptitude, (e.g. on Ubuntu or Debian system): - apt-get install libaio +```sh +apt-get install libaio +``` ## JDBC Persistence @@ -423,50 +426,50 @@ To configure Apache ActiveMQ Artemis to use a database for persisting messages a ``` -- `jdbc-connection-url` +- `jdbc-connection-url` - The full JDBC connection URL for your database server. The connection url should include all configuration parameters and database name. Note: When configuring the server using the XML configuration files please ensure to escape any illegal chars; "&" for example, is typical in JDBC connection url and should be escaped to "&". + The full JDBC connection URL for your database server. The connection url should include all configuration parameters and database name. **Note:** When configuring the server using the XML configuration files please ensure to escape any illegal chars; "&" for example, is typical in JDBC connection url and should be escaped to "&". -- `bindings-table-name` +- `bindings-table-name` - The name of the table in which bindings data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. + The name of the table in which bindings data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. -- `message-table-name` +- `message-table-name` - The name of the table in which bindings data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. + The name of the table in which bindings data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. -- `large-message-table-name` +- `large-message-table-name` - The name of the table in which messages and related data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. - -- `page-store-table-name` + The name of the table in which messages and related data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. + +- `page-store-table-name` - The name of the table to house the page store directory information. Note that each address will have it's own page table which will use this name appended with a unique id of up to 20 characters. + The name of the table to house the page store directory information. Note that each address will have it's own page table which will use this name appended with a unique id of up to 20 characters. -- `node-manager-store-table-name` +- `node-manager-store-table-name` - The name of the table in which the HA Shared Store locks (ie live and backup) and HA related data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. - Each Shared Store live/backup pairs must use the same table name and isn't supported to share the same table between multiple (and unrelated) live/backup pairs. + The name of the table in which the HA Shared Store locks (ie live and backup) and HA related data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. + Each Shared Store live/backup pairs must use the same table name and isn't supported to share the same table between multiple (and unrelated) live/backup pairs. -- `jdbc-driver-class-name` +- `jdbc-driver-class-name` - The fully qualified class name of the desired database Driver. + The fully qualified class name of the desired database Driver. -- `jdbc-network-timeout` +- `jdbc-network-timeout` - The JDBC network connection timeout in milliseconds. The default value - is 20000 milliseconds (ie 20 seconds). - When using a shared store it is recommended to set it less then or equal to `jdbc-lock-expiration`. + The JDBC network connection timeout in milliseconds. The default value + is 20000 milliseconds (ie 20 seconds). + When using a shared store it is recommended to set it less then or equal to `jdbc-lock-expiration`. -- `jdbc-lock-renew-period` +- `jdbc-lock-renew-period` - The period in milliseconds of the keep alive service of a JDBC lock. The default value - is 2000 milliseconds (ie 2 seconds). - -- `jdbc-lock-expiration` + The period in milliseconds of the keep alive service of a JDBC lock. The default value + is 2000 milliseconds (ie 2 seconds). + +- `jdbc-lock-expiration` - The time in milliseconds a JDBC lock is considered valid without keeping it alive. The default value - is 20000 milliseconds (ie 20 seconds). + The time in milliseconds a JDBC lock is considered valid without keeping it alive. The default value + is 20000 milliseconds (ie 20 seconds). - `jdbc-journal-sync-period` diff --git a/docs/user-manual/en/pre-acknowledge.md b/docs/user-manual/en/pre-acknowledge.md index d328d8b880c..fc7942ad2b5 100644 --- a/docs/user-manual/en/pre-acknowledge.md +++ b/docs/user-manual/en/pre-acknowledge.md @@ -2,11 +2,11 @@ JMS specifies 3 acknowledgement modes: -- `AUTO_ACKNOWLEDGE` +- `AUTO_ACKNOWLEDGE` -- `CLIENT_ACKNOWLEDGE` +- `CLIENT_ACKNOWLEDGE` -- `DUPS_OK_ACKNOWLEDGE` +- `DUPS_OK_ACKNOWLEDGE` Apache ActiveMQ Artemis supports two additional modes: `PRE_ACKNOWLEDGE` and `INDIVIDUAL_ACKNOWLEDGE` @@ -32,7 +32,7 @@ update messages. With these messages it might be reasonable to lose a message in event of crash, since the next price update message will arrive soon, overriding the previous price. -> **Note** +> **Note:** > > Please note, that if you use pre-acknowledge mode, then you will lose > transactional semantics for messages being consumed, since clearly @@ -67,7 +67,7 @@ acknowledge mode with `ActiveMQJMSConstants.INDIVIDUAL_ACKNOWLEDGE`. Individual ACK inherits all the semantics from Client Acknowledge, with the exception the message is individually acked. -> **Note** +> **Note:** > > Please note, that to avoid confusion on MDB processing, Individual > ACKNOWLEDGE is not supported through MDBs (or the inbound resource @@ -76,5 +76,5 @@ the exception the message is individually acked. ## Example -See the [examples](examples.md) chapter for an example which shows how to -use pre-acknowledgement mode with JMS. +See the [Pre-acknowledge Example](examples.md#pre-acknowledge) which shows how +to use pre-acknowledgement mode with JMS. diff --git a/docs/user-manual/en/preface.md b/docs/user-manual/en/preface.md index 6dccb7e8d8c..cbcc7d98e04 100644 --- a/docs/user-manual/en/preface.md +++ b/docs/user-manual/en/preface.md @@ -2,43 +2,43 @@ What is Apache ActiveMQ Artemis? -- Apache ActiveMQ Artemis is an open source project to build a multi-protocol, - embeddable, very high performance, clustered, asynchronous messaging - system. +- Apache ActiveMQ Artemis is an open source project to build a multi-protocol, + embeddable, very high performance, clustered, asynchronous messaging + system. -- Apache ActiveMQ Artemis is an example of Message Oriented Middleware (MoM). For a - description of MoMs and other messaging concepts please see the [Messaging Concepts](messaging-concepts.md). +- Apache ActiveMQ Artemis is an example of Message Oriented Middleware (MoM). For a + description of MoMs and other messaging concepts please see the [Messaging Concepts](messaging-concepts.md). Why use Apache ActiveMQ Artemis? Here are just a few of the reasons: -- 100% open source software. Apache ActiveMQ Artemis is licensed using the Apache - Software License v 2.0 to minimise barriers to adoption. +- 100% open source software. Apache ActiveMQ Artemis is licensed using the Apache + Software License v 2.0 to minimise barriers to adoption. -- Apache ActiveMQ Artemis is designed with usability in mind. +- Apache ActiveMQ Artemis is designed with usability in mind. -- Written in Java. Runs on any platform with a Java 8+ runtime, that's - everything from Windows desktops to IBM mainframes. +- Written in Java. Runs on any platform with a Java 8+ runtime, that's + everything from Windows desktops to IBM mainframes. -- Amazing performance. Our ground-breaking high performance journal - provides persistent messaging performance at rates normally seen for - non-persistent messaging, our non-persistent messaging performance - rocks the boat too. +- Amazing performance. Our ground-breaking high performance journal + provides persistent messaging performance at rates normally seen for + non-persistent messaging, our non-persistent messaging performance + rocks the boat too. -- Full feature set. All the features you'd expect in any serious - messaging system, and others you won't find anywhere else. +- Full feature set. All the features you'd expect in any serious + messaging system, and others you won't find anywhere else. -- Elegant, clean-cut design with minimal third party dependencies. Run - ActiveMQ Artemis stand-alone, run it in integrated in your favourite Java EE - application server, or run it embedded inside your own product. It's - up to you. +- Elegant, clean-cut design with minimal third party dependencies. Run + ActiveMQ Artemis stand-alone, run it in integrated in your favourite Java EE + application server, or run it embedded inside your own product. It's + up to you. -- Seamless high availability. We provide a HA solution with automatic - client failover so you can guarantee zero message loss or - duplication in event of server failure. +- Seamless high availability. We provide a HA solution with automatic + client failover so you can guarantee zero message loss or + duplication in event of server failure. -- Hugely flexible clustering. Create clusters of servers that know how - to load balance messages. Link geographically distributed clusters - over unreliable connections to form a global network. Configure - routing of messages in a highly flexible way. +- Hugely flexible clustering. Create clusters of servers that know how + to load balance messages. Link geographically distributed clusters + over unreliable connections to form a global network. Configure + routing of messages in a highly flexible way. diff --git a/docs/user-manual/en/project-info.md b/docs/user-manual/en/project-info.md index 3dc4b72eeec..a952c985ca5 100644 --- a/docs/user-manual/en/project-info.md +++ b/docs/user-manual/en/project-info.md @@ -9,20 +9,19 @@ page: ## Project Information -- If you have any user questions please use our [user - forum](http://activemq.2283324.n4.nabble.com/ActiveMQ-User-f2341805.html) +- If you have any user questions please use our [user + forum](http://activemq.2283324.n4.nabble.com/ActiveMQ-User-f2341805.html) -- If you have development related questions, please use our [developer - forum](http://activemq.2283324.n4.nabble.com/ActiveMQ-Dev-f2368404.html) +- If you have development related questions, please use our [developer + forum](http://activemq.2283324.n4.nabble.com/ActiveMQ-Dev-f2368404.html) -- Pop in and chat to us in our [IRC - channel](irc://irc.freenode.net:6667/apache-activemq) +- Pop in and chat to us in our [IRC + channel](irc://irc.freenode.net:6667/apache-activemq) -- Apache ActiveMQ Artemis Git repository is - -- All release tags are available from - +- Apache ActiveMQ Artemis Git repository is +- All release tags are available from + And many thanks to all our contributors, both old and new who helped create Apache ActiveMQ Artemis. diff --git a/docs/user-manual/en/protocols-interoperability.md b/docs/user-manual/en/protocols-interoperability.md index 3a6a26aeb11..0fe16f76e2e 100644 --- a/docs/user-manual/en/protocols-interoperability.md +++ b/docs/user-manual/en/protocols-interoperability.md @@ -1,646 +1,69 @@ # Protocols and Interoperability -## Protocols +Apache ActiveMQ Artemis has a powerful & flexible core which provides a foundation upon which other protocols can be +implemented. Each protocol implementation translates the ideas of its specific protocol onto this core. -ActiveMQ Artemis has a plugable protocol architecture. Protocol plugins come in the form of ActiveMQ Artemis protocol -modules. Each protocol module should be added to the brokers class path and are loaded by the broker at boot time. -ActiveMQ Artemis ships with 5 protocol modules out of the box. The 5 modules offer support for the following protocols: +The broker ships with a client implementation which interacts directly with this core. It uses what's called the ["core" +API](core.md), and it communicates over the network using the "core" protocol. -* AMQP -* OpenWire -* MQTT -* STOMP -* HornetQ +## Supported Protocols & APIs -In addition to the protocols above ActiveMQ Artemis also offers support for it's own highly performant native protocol - "Core". +The broker has a pluggable protocol architecture. Protocol plugins come in the form of protocol modules. Each protocol +module is included on the broker's class path and loaded by the broker at boot time. The broker ships with 5 protocol +modules out of the box. The 5 modules offer support for the following protocols: -## Configuring protocols +- [AMQP](amqp.md) +- [OpenWire](openwire.md) +- [MQTT](mqtt.md) +- [STOMP](stomp.md) +- HornetQ + +#### APIs and Other Interfaces + +Although JMS is a standardized API, it does not define a network protocol. The [ActiveMQ Artemis JMS 2.0 client](using-jms.md) +is implemented on top of the core protocol. We also provide a [client-side JNDI implementation](using-jms.md#jndi). + +The broker also ships with a [REST messaging interface](rest.md) (not to be confused with the REST management API +provided via our integration with Jolokia). + +## Configuring Acceptors In order to make use of a particular protocol, a transport must be configured with the desired protocol enabled. There is a whole section on configuring transports that can be found [here](configuring-transports.md). The default configuration shipped with the ActiveMQ Artemis distribution comes with a number of acceptors already -defined, one for each of the above protocols plus a generic acceptor that supports all protocols. To enable a -protocol on a particular acceptor simply add a url parameter "protocol=AMQP,STOMP" to the acceptor url. Where the value -of the parameter is a comma separated list of protocol names. If the protocol parameter is omitted from the url all -protocols are enabled. +defined, one for each of the above protocols plus a generic acceptor that supports all protocols. To enable +protocols on a particular acceptor simply add the `protocols` url parameter to the acceptor url where the value is one +or more protocols (separated by commas). If the `protocols` parameter is omitted from the url **all** protocols are +enabled. +- The following example enables only MQTT on port 1883 ```xml - tcp://localhost:1883?protocols=MQTT +``` - +- The following example enables MQTT and AMQP on port 1883 +```xml - tcp://localhost:1883?protocols=MQTT,AMQP + tcp://localhost:5672?protocols=MQTT,AMQP +``` - +- The following example enables **all** protocols on `61616`: +```xml tcp://localhost:61616 ``` -## AMQP - -Apache ActiveMQ Artemis supports the [AMQP -1.0](https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=amqp) -specification. To enable AMQP you must configure a Netty Acceptor to -receive AMQP clients, like so: - -```xml -tcp://localhost:5672?protocols=AMQP -``` - - -Apache ActiveMQ Artemis will then accept AMQP 1.0 clients on port 5672 which is the -default AMQP port. - -There are 2 AMQP examples available see proton-j and proton-ruby which -use the qpid Java and Ruby clients respectively. - -### AMQP and security - -The Apache ActiveMQ Artemis Server accepts AMQP SASL Authentication and will use this -to map onto the underlying session created for the connection so you can -use the normal Apache ActiveMQ Artemis security configuration. - -### AMQP Links - -An AMQP Link is a uni directional transport for messages between a -source and a target, i.e. a client and the Apache ActiveMQ Artemis Broker. A link will -have an endpoint of which there are 2 kinds, a Sender and A Receiver. At -the Broker a Sender will have its messages converted into an Apache ActiveMQ Artemis -Message and forwarded to its destination or target. A Receiver will map -onto an Apache ActiveMQ Artemis Server Consumer and convert Apache ActiveMQ Artemis messages back into -AMQP messages before being delivered. - -### AMQP and destinations - -If an AMQP Link is dynamic then a temporary queue will be created and -either the remote source or remote target address will be set to the -name of the temporary queue. If the Link is not dynamic then the the -address of the remote target or source will used for the queue. If this -does not exist then an exception will be sent - -> **Note** -> -> For the next version we will add a flag to aut create durable queue -> but for now you will have to add them via the configuration - -### AMQP and Multicast Queues (Topics) - -Although amqp has no notion of topics it is still possible to treat amqp consumers or receivers as subscriptions rather -than just consumers on a queue. By default any receiving link that attaches to an address that has only multicast enabled -will be treated as a subscription and a subscription queue will be created. If the Terminus Durability is either UNSETTLED_STATE -or CONFIGURATION then the queue will be made durable, similar to a JMS durable subscription and given a name made up from -the container id and the link name, something like `my-container-id:my-link-name`. if the Terminus Durability is configured -as NONE then a volatile multicast queue will be created. - -Artemis also supports the qpid-jms client and will respect its use of topics regardless of the prefix used for the address. - -### AMQP and Coordinations - Handling Transactions - -An AMQP links target can also be a Coordinator, the Coordinator is used -to handle transactions. If a coordinator is used the the underlying -HormetQ Server session will be transacted and will be either rolled back -or committed via the coordinator. - -> **Note** -> -> AMQP allows the use of multiple transactions per session, -> `amqp:multi-txns-per-ssn`, however in this version Apache ActiveMQ Artemis will only -> support single transactions per session - -### AMQP scheduling message delivery - -An AMQP message can provide scheduling information that controls the time in the future when the -message will be delivered at the earliest. This information is provided by adding a message annotation -to the sent message. - -There are two different message annotations that can be used to schedule a message for later delivery: - -* `x-opt-delivery-time` -The specified value must be a positive long corresponding to the time the message should be made available -for delivery (in milliseconds). - -* `x-opt-delivery-delay` -The specified value must be a positive long corresponding to the amount of milliseconds after the broker -receives the given message before it should be made available for delivery. - -if both annotations are present in the same message then the broker will prefer the more specific `x-opt-delivery-time` value. - -## OpenWire - -Apache ActiveMQ Artemis now supports the -[OpenWire](http://activemq.apache.org/openwire.html) protocol so that an -Apache ActiveMQ 5.x JMS client can talk directly to an Apache ActiveMQ Artemis server. To enable -OpenWire support you must configure a Netty Acceptor, like so: - -```xml -tcp://localhost:61616?protocols=OPENWIRE -``` - -The Apache ActiveMQ Artemis server will then listens on port 61616 for incoming -openwire commands. Please note the "protocols" is not mandatory here. -The openwire configuration conforms to Apache ActiveMQ Artemis's "Single Port" feature. -Please refer to [Configuring Single Port](configuring-transports.md#single-port-support) for details. - -Please refer to the openwire example for more coding details. - -Currently we support Apache ActiveMQ Artemis clients that using standard JMS APIs. In -the future we will get more supports for some advanced, Apache ActiveMQ Artemis -specific features into Apache ActiveMQ Artemis. - -### Connection Monitoring - -OpenWire has a few parameters to control how each connection is monitored, they are: - -* maxInactivityDuration: -It specifies the time (milliseconds) after which the connection is closed by the broker if no data was received. -Default value is 30000. - -* maxInactivityDurationInitalDelay: -It specifies the maximum delay (milliseconds) before inactivity monitoring is started on the connection. -It can be useful if a broker is under load with many connections being created concurrently. -Default value is 10000. - -* useInactivityMonitor: -A value of false disables the InactivityMonitor completely and connections will never time out. -By default it is enabled. On broker side you don't neet set this. Instead you can set the -connection-ttl to -1. - -* useKeepAlive: -Whether or not to send a KeepAliveInfo on an idle connection to prevent it from timing out. -Enabled by default. Disabling the keep alive will still make connections time out if no data -was received on the connection for the specified amount of time. - -Note at the beginning the InactivityMonitor negotiates the appropriate maxInactivityDuration and -maxInactivityDurationInitalDelay. The shortest duration is taken for the connection. - -More details please see [ActiveMQ InactivityMonitor](http://activemq.apache.org/activemq-inactivitymonitor.html). - -### Disable/Enable Advisories - -By default, advisory topics ([ActiveMQ Advisory](http://activemq.apache.org/advisory-message.html)) -are created in order to send certain type of advisory messages to listening clients. As a result, -advisory addresses and queues will be displayed on the management console, along with user deployed -addresses and queues. This sometimes cause confusion because the advisory objects are internally -managed without user being aware of them. In addition, users may not want the advisory topics at all -(they cause extra resources and performance penalty) and it is convenient to disable them at all -from the broker side. - -The protocol provides two parameters to control advisory behaviors on the broker side. - -* supportAdvisory -Whether or not the broker supports advisory messages. If the value is true, advisory addresses/ -queues will be created. If the value is false, no advisory addresses/queues are created. Default -value is true. - -* suppressInternalManagementObjects -Whether or not the advisory addresses/queues, if any, will be registered to management service -(e.g. JMX registry). If set to true, no advisory addresses/queues will be registered. If set to -false, those are registered and will be displayed on the management console. Default value is -true. - -The two parameters are configured on openwire acceptors, via URLs or API. For example: - - tcp://127.0.0.1:61616?protocols=CORE,AMQP,OPENWIRE;supportAdvisory=true;suppressInternalManagementObjects=false - -### Virtual Topic Consumer Destination Translation - -For existing OpenWire consumers of virtual topic destinations it is possible to configure a mapping function -that will translate the virtual topic consumer destination into a FQQN address. This address then represents -the consumer as a multicast binding to an address representing the virtual topic. - -The configuration string property ```virtualTopicConsumerWildcards``` has two parts seperated by a ```;```. -The first is the 5.x style destination filter that identifies the destination as belonging to a virtual topic. -The second identifies the number of ```paths``` that identify the consumer queue such that it can be parsed from the -destination. -For example, the default 5.x virtual topic with consumer prefix of ```Consumer.*.```, would require a -```virtualTopicConsumerWildcards``` filter of ```Consumer.*.>;2```. As a url parameter this transforms to ```Consumer.*.%3E%3B2``` when -the url significant characters ```>;``` are escaped with their hex code points. -In an acceptor url it would be: - -```xml -tcp://127.0.0.1:61616?protocols=OPENWIRE;virtualTopicConsumerWildcards=Consumer.*.%3E%3B2 -``` - -This will translate ```Consumer.A.VirtualTopic.Orders``` into a FQQN of ```VirtualTopic.Orders::Consumer.A``` using the -int component ```2``` of the configuration to identify the consumer queue as the first two paths of the destination. -```virtualTopicConsumerWildcards``` is multi valued using a ```,``` separator. - -Please see Virtual Topic Mapping example contained in the OpenWire [examples](examples.md). - -## MQTT - -MQTT is a light weight, client to server, publish / subscribe messaging protocol. MQTT has been specifically -designed to reduce transport overhead (and thus network traffic) and code footprint on client devices. -For this reason MQTT is ideally suited to constrained devices such as sensors and actuators and is quickly -becoming the defacto standard communication protocol for IoT. - -Apache ActiveMQ Artemis supports MQTT v3.1.1 (and also the older v3.1 code message format). To enable MQTT, -simply add an appropriate acceptor with the MQTT protocol enabled. For example: - - tcp://localhost:1883?protocols=MQTT - -By default the configuration shipped with Apache ActiveMQ Artemis has the above acceptor already defined, MQTT is -also active by default on the generic acceptor defined on port 61616 (where all protocols are enabled), in the out -of the box configuration. - -The best source of information on the MQTT protocol is in the specification. The MQTT v3.1.1 specification can -be downloaded from the OASIS website here: https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html - -Some note worthy features of MQTT are explained below: - -### MQTT Quality of Service - -MQTT offers 3 quality of service levels. - -Each message (or topic subscription) can define a quality of service that is associated with it. The quality of service -level defined on a topic is the maximum level a client is willing to accept. The quality of service level on a -message is the desired quality of service level for this message. The broker will attempt to deliver messages to -subscribers at the highest quality of service level based on what is defined on the message and topic subscription. - -Each quality of service level offers a level of guarantee by which a message is sent or received: - -* QoS 0: AT MOST ONCE: Guarantees that a particular message is only ever received by the subscriber a maximum of one time. -This does mean that the message may never arrive. The sender and the receiver will attempt to deliver the message, -but if something fails and the message does not reach it's destination (say due to a network connection) the message -may be lost. This QoS has the least network traffic overhead and the least burden on the client and the broker and is often -useful for telemetry data where it doesn't matter if some of the data is lost. - -* QoS 1: AT LEAST ONCE: Guarantees that a message will reach it's intended recipient one or more times. The sender will -continue to send the message until it receives an acknowledgment from the recipient, confirming it has received the message. -The result of this QoS is that the recipient may receive the message multiple times, and also increases the network -overhead than QoS 0, (due to acks). In addition more burden is placed on the sender as it needs to store the message -and retry should it fail to receive an ack in a reasonable time. - -* QoS 2: EXACTLY ONCE: The most costly of the QoS (in terms of network traffic and burden on sender and receiver) this -QoS will ensure that the message is received by a recipient exactly one time. This ensures that the receiver never gets -any duplicate copies of the message and will eventually get it, but at the extra cost of network overhead and complexity -required on the sender and receiver. - -### MQTT Retain Messages - -MQTT has an interesting feature in which messages can be "retained" for a particular address. This means that once a -retain message has been sent to an address, any new subscribers to that address will receive the last sent retain -message before any others messages, this happens even if the retained message was sent before a client has connected -or subscribed. An example of where this feature might be useful is in environments such as IoT where devices need to -quickly get the current state of a system when they are on boarded into a system. - -### Will Messages - -A will message can be sent when a client initially connects to a broker. Clients are able to set a "will -message" as part of the connect packet. If the client abnormally disconnects, say due to a device or network failure -the broker will proceed to publish the will message to the specified address (as defined also in the connect packet). -Other subscribers to the will topic will receive the will message and can react accordingly. This feature can be useful - in an IoT style scenario to detect errors across a potentially large scale deployment of devices. - -### Debug Logging - -Detailed protocol logging (e.g. packets in/out) can be activated via the following steps: - -1) Open `/etc/logging.properties` -2) Add `org.apache.activemq.artemis.core.protocol.mqtt` to the `loggers` list. -3) Add this line to enable `TRACE` logging for this new logger: `logger.org.apache.activemq.artemis.core.protocol.mqtt.level=TRACE` -4) Ensure the `level` for the `handler` you want to log the message doesn't block the `TRACE` logging. For example, - modify the `level` of the `CONSOLE` `handler` like so: `handler.CONSOLE.level=TRACE` - -The MQTT specification doesn't dictate the format of the payloads which clients publish. As far as the broker is -concerned a payload is just just an array of bytes. However, to facilitate logging the broker will encode the payloads -as UTF-8 strings and print them up to 256 characters. Payload logging is limited to avoid filling the logs with potentially -hundreds of megabytes of unhelpful information. - - -### Wild card subscriptions - -MQTT addresses are hierarchical much like a file system, and use "/" character to separate hierarchical levels. -Subscribers are able to subscribe to specific topics or to whole branches of a hierarchy. - -To subscribe to branches of an address hierarchy a subscriber can use wild cards. - -There are 2 types of wild card in MQTT: - - * "#" Multi level wild card. Adding this wild card to an address would match all branches of the address hierarchy - under a specified node. For example: /uk/# Would match /uk/cities, /uk/cities/newcastle and also /uk/rivers/tyne. - Subscribing to an address "#" would result in subscribing to all topics in the broker. This can be useful, but should - be done so with care since it has significant performance implications. - - * "+" Single level wild card. Matches a single level in the address hierarchy. For example /uk/+/stores would - match /uk/newcastle/stores but not /uk/cities/newcastle/stores. - - -## Stomp - -[Stomp](https://stomp.github.io/) is a text-orientated wire protocol -that allows Stomp clients to communicate with Stomp Brokers. Apache ActiveMQ Artemis -now supports Stomp 1.0, 1.1 and 1.2. - -Stomp clients are available for several languages and platforms making -it a good choice for interoperability. - -## Native Stomp support - -Apache ActiveMQ Artemis provides native support for Stomp. To be able to send and -receive Stomp messages, you must configure a `NettyAcceptor` with a -`protocols` parameter set to have `stomp`: - -```xml -tcp://localhost:61613?protocols=STOMP -``` - -With this configuration, Apache ActiveMQ Artemis will accept Stomp connections on the -port `61613` (which is the default port of the Stomp brokers). - -See the `stomp` example which shows how to configure an Apache ActiveMQ Artemis server -with Stomp. - -### Limitations - -Message acknowledgements are not transactional. The ACK frame can not be -part of a transaction (it will be ignored if its `transaction` header is -set). - -### Stomp 1.1/1.2 Notes - -#### Virtual Hosting - -Apache ActiveMQ Artemis currently doesn't support virtual hosting, which means the -'host' header in CONNECT fram will be ignored. - -### Mapping Stomp destinations to addresses and queues - -Stomp clients deals with *destinations* when sending messages and -subscribing. Destination names are simply strings which are mapped to -some form of destination on the server - how the server translates these -is left to the server implementation. - -In Apache ActiveMQ Artemis, these destinations are mapped to *addresses* and *queues* -depending on the operation being done and the desired semantics (e.g. anycast or -multicast). - -#### Sending - -When a Stomp client sends a message (using a `SEND` frame), the protocol manager looks -at the message to determine where to route it and potentially how to create the address -and/or queue to which it is being sent. The protocol manager uses either of the following -bits of information from the frame to determine the routing type: - -1. The value of the `destination-type` header. Valid values are `ANYCAST` and -`MULTICAST` (case sensitive). - -2. The "prefix" on the `destination` header. See [additional info](address-model.md) on -prefixes. - -If no indication of routing type is supplied then anycast semantics are used. - -The `destination` header maps to an address of the same name. If the `destination` header -used a prefix then the prefix is stripped. - -#### Receiving - -When a client receives a message from the broker the message will have the `destination-type` -header set to either `MULTICAST` or `ANYCAST` as determined when the message was originally -sent/routed. - -#### Subscribing - -When a Stomp client subscribes to a destination (using a `SUBSCRIBE` frame), the protocol -manager looks at the frame to determine what subscription semantics to use and potentially how -to create the address and/or queue for the subscription. The protocol manager uses either of -the following bits of information from the frame to determine the routing type: - -1. The value of the `subscription-type` header. Valid values are `ANYCAST` and -`MULTICAST` (case sensitive). - -2. The "prefix" on the `destination` header. See [additional info](address-model.md) on -prefixes. - -If no indication of routing type is supplied then anycast semantics are used. - -The `destination` header maps to an address of the same name if multicast is used or to a queue -of the same name if anycast is used. If the `destination` header used a prefix then the prefix -is stripped. - -### STOMP heart-beating and connection-ttl - -Well behaved STOMP clients will always send a DISCONNECT frame before -closing their connections. In this case the server will clear up any -server side resources such as sessions and consumers synchronously. -However if STOMP clients exit without sending a DISCONNECT frame or if -they crash the server will have no way of knowing immediately whether -the client is still alive or not. STOMP connections therefore default to -a connection-ttl value of 1 minute (see chapter on -[connection-ttl](connection-ttl.md) for more information. This value can -be overridden using the `connection-ttl-override` property or if you -need a specific connectionTtl for your stomp connections without -affecting the broker-wide `connection-ttl-override` setting, you can -configure your stomp acceptor with the "connectionTtl" property, which -is used to set the ttl for connections that are created from that acceptor. -For example: - -```xml -tcp://localhost:61613?protocols=STOMP;connectionTtl=20000 -``` - -The above configuration will make sure that any Stomp connection that is -created from that acceptor and does not include a `heart-beat` header -or disables client-to-server heart-beats by specifying a `0` value will -have its connection-ttl set to 20 seconds. The `connectionTtl` set on an -acceptor will take precedence over `connection-ttl-override`. The default -`connectionTtl` is 60,000 milliseconds. - -Since Stomp 1.0 does not support heart-beating then all connections from -Stomp 1.0 clients will have a connection TTL imposed upon them by the broker -based on the aforementioned configuration options. Likewise, any Stomp 1.1 -or 1.2 clients that don't specify a `heart-beat` header or disable client-to-server -heart-beating (e.g. by sending `0,X` in the `heart-beat` header) will have -a connection TTL imposed upon them by the broker. - -For Stomp 1.1 and 1.2 clients which send a non-zero client-to-server `heart-beat` -header value then their connection TTL will be set accordingly. However, the broker -will not strictly set the connection TTL to the same value as the specified in the -`heart-beat` since even small network delays could then cause spurious disconnects. -Instead, the client-to-server value in the `heart-beat` will be multiplied by the -`heartBeatConnectionTtlModifer` specified on the acceptor. The -`heartBeatConnectionTtlModifer` is a decimal value that defaults to `2.0` so for -example, if a client sends a `heart-beat` header of `1000,0` the the connection TTL -will be set to `2000` so that the data or ping frames sent every 1000 milliseconds will -have a sufficient cushion so as not to be considered late and trigger a disconnect. -This is also in accordance with the Stomp 1.1 and 1.2 specifications which both state, -"because of timing inaccuracies, the receiver SHOULD be tolerant and take into account -an error margin." - -The minimum and maximum connection TTL allowed can also be specified on the -acceptor via the `connectionTtlMin` and `connectionTtlMax` properties respectively. -The default `connectionTtlMin` is 1000 and the default `connectionTtlMax` is Java's -`Long.MAX_VALUE` meaning there essentially is no max connection TTL by default. -Keep in mind that the `heartBeatConnectionTtlModifer` is relevant here. For -example, if a client sends a `heart-beat` header of `20000,0` and the acceptor -is using a `connectionTtlMax` of `30000` and a default `heartBeatConnectionTtlModifer` -of `2.0` then the connection TTL would be `40000` (i.e. `20000` * `2.0`) which would -exceed the `connectionTtlMax`. In this case the server would respond to the client -with a `heart-beat` header of `0,15000` (i.e. `30000` / `2.0`). As described -previously, this is to make sure there is a sufficient cushion for the client -heart-beats in accordance with the Stomp 1.1 and 1.2 specifications. The same kind -of calculation is done for `connectionTtlMin`. - -The minimum server-to-client heart-beat value is 500ms. - -> **Note** -> -> Please note that the STOMP protocol version 1.0 does not contain any -> heart-beat frame. It is therefore the user's responsibility to make -> sure data is sent within connection-ttl or the server will assume the -> client is dead and clean up server side resources. With `Stomp 1.1` -> users can use heart-beats to maintain the life cycle of stomp -> connections. - -### Selector/Filter expressions - -Stomp subscribers can specify an expression used to select or filter -what the subscriber receives using the `selector` header. The filter -expression syntax follows the *core filter syntax* described in the -[Filter Expressions](filter-expressions.md) documentation. - -### Stomp and JMS interoperability - -#### Sending and consuming Stomp message from JMS or Apache ActiveMQ Artemis Core API - -Stomp is mainly a text-orientated protocol. To make it simpler to -interoperate with JMS and Apache ActiveMQ Artemis Core API, our Stomp implementation -checks for presence of the `content-length` header to decide how to map -a Stomp 1.0 message to a JMS Message or a Core message. - -If the Stomp 1.0 message does *not* have a `content-length` header, it will -be mapped to a JMS *TextMessage* or a Core message with a *single -nullable SimpleString in the body buffer*. - -Alternatively, if the Stomp 1.0 message *has* a `content-length` header, it -will be mapped to a JMS *BytesMessage* or a Core message with a *byte[] -in the body buffer*. - -The same logic applies when mapping a JMS message or a Core message to -Stomp. A Stomp 1.0 client can check the presence of the `content-length` -header to determine the type of the message body (String or bytes). - -#### Message IDs for Stomp messages - -When receiving Stomp messages via a JMS consumer or a QueueBrowser, the -messages have no properties like JMSMessageID by default. However this -may bring some inconvenience to clients who wants an ID for their -purpose. Apache ActiveMQ Artemis Stomp provides a parameter to enable message ID on -each incoming Stomp message. If you want each Stomp message to have a -unique ID, just set the `stompEnableMessageId` to true. For example: - - tcp://localhost:61613?protocols=STOMP;stompEnableMessageId=true - -When the server starts with the above setting, each stomp message sent -through this acceptor will have an extra property added. The property -key is ` - amq-message-id` and the value is a String representation of a -long type internal message id prefixed with "`STOMP`", like: - - amq-message-id : STOMP12345 - -If `stomp-enable-message-id` is not specified in the configuration, -default is `false`. - -### Durable Subscriptions - -The `SUBSCRIBE` and `UNSUBSCRIBE` frames can be augmented with special headers to create -and destroy durable subscriptions respectively. - -To create a durable subscription the `client-id` header must be set on the `CONNECT` frame -and the `durable-subscription-name` must be set on the `SUBSCRIBE` frame. The combination -of these two headers will form the identity of the durable subscription. - -To delete a durable subscription the `client-id` header must be set on the `CONNECT` frame -and the `durable-subscription-name` must be set on the `UNSUBSCRIBE` frame. The values for -these headers should match what was set on the `SUBSCRIBE` frame to delete the corresponding -durable subscription. - -It is possible to pre-configure durable subscriptions since the Stomp implementation creates -the queue used for the durable subscription in a deterministic way (i.e. using the format of -`client-id`.`subscription-name`). For example, if you wanted to configure a durable -subscription on the address `myAddress` with a client-id of `myclientid` and a subscription -name of `mysubscription` then configure the durable subscription: - -```xml - - ... - -
- - - -
-
- ... -
-``` - -### Handling of Large Messages with Stomp - -Stomp clients may send very large frame bodies which can exceed the -size of Apache ActiveMQ Artemis server's internal buffer, causing unexpected errors. To -prevent this situation from happening, Apache ActiveMQ Artemis provides a stomp -configuration attribute `stompMinLargeMessageSize`. This attribute -can be configured inside a stomp acceptor, as a parameter. For example: - -```xml -tcp://localhost:61613?protocols=STOMP;stompMinLargeMessageSize=10240 -``` - -The type of this attribute is integer. When this attributed is -configured, Apache ActiveMQ Artemis server will check the size of the body of each -Stomp frame arrived from connections established with this acceptor. If -the size of the body is equal or greater than the value of -`stompMinLargeMessageSize`, the message will be persisted as a large -message. When a large message is delievered to a stomp consumer, the -broker will automatically handle the conversion from a large -message to a normal message, before sending it to the client. - -If a large message is compressed, the server will uncompressed it before -sending it to stomp clients. The default value of -`stompMinLargeMessageSize` is the same as the default value of -[min-large-message-size](large-messages.md#configuring-parameters). - -### Stomp Over Web Sockets - -Apache ActiveMQ Artemis also support Stomp over [Web -Sockets](https://html.spec.whatwg.org/multipage/web-sockets.html). Modern web browser which -support Web Sockets can send and receive Stomp messages from Apache ActiveMQ Artemis. - -Stomp over Web Sockets is supported via the normal Stomp acceptor: - -```xml -tcp://localhost:61614?protocols=STOMP -``` - -With this configuration, Apache ActiveMQ Artemis will accept Stomp connections over Web -Sockets on the port `61614`. Web browser can -then connect to `ws://:61614` using a Web Socket to send -and receive Stomp messages. - -A companion JavaScript library to ease client-side development is -available from [GitHub](https://github.com/jmesnil/stomp-websocket) -(please see its [documentation](http://jmesnil.net/stomp-websocket/doc/) -for a complete description). - -The payload length of websocket frames can vary between client implementations. By default -Apache ActiveMQ Artemis will accept frames with a payload length of 65,536. If the client -needs to send payloads longer than this in a single frame this length can be adjusted by -using the `stompMaxFramePayloadLength` URL parameter on the acceptor. - -The `stomp-websockets` example shows how to configure Apache ActiveMQ Artemis server to -have web browsers and Java applications exchanges messages on a JMS -topic. - -## REST - -Please see [Rest Interface](rest.md) +Here are the supported protocols and their corresponding value used in the `protocols` url parameter. +Protocol|`protocols` value +---|--- +Core (Artemis & HornetQ native)|`CORE` +OpenWire (5.x native)|`OPENWIRE` +AMQP|`AMQP` +MQTT|`MQTT` +STOMP|`STOMP` \ No newline at end of file diff --git a/docs/user-manual/en/resource-limits.md b/docs/user-manual/en/resource-limits.md index 7939dc7293f..45e4d84a8aa 100644 --- a/docs/user-manual/en/resource-limits.md +++ b/docs/user-manual/en/resource-limits.md @@ -20,10 +20,10 @@ Here is an example of the XML used to set resource limits: ``` Unlike the `match` from `address-setting`, this `match` does not use -any wild-card syntax. It's a simple 1:1 mapping of the limits to a user. +any wild-card syntax. It's a simple 1:1 mapping of the limits to a **user**. -`max-connections` defines how many connections the matched user can make +- `max-connections` defines how many connections the matched user can make to the broker. The default is -1 which means there is no limit. -`max-queues` defines how many queues the matched user can create. The default +- `max-queues` defines how many queues the matched user can create. The default is -1 which means there is no limit. \ No newline at end of file diff --git a/docs/user-manual/en/rest.md b/docs/user-manual/en/rest.md index 91d4cd283b0..7915eef0df5 100644 --- a/docs/user-manual/en/rest.md +++ b/docs/user-manual/en/rest.md @@ -10,15 +10,17 @@ and receiving simple HTTP messages that contain the content you want to push aro here's a simple example of posting an order to an order processing queue express as an HTTP message: - POST /queue/orders/create HTTP/1.1 - Host: example.com - Content-Type: application/xml - - - Bill - iPhone 4 - $199.99 - +``` +POST /queue/orders/create HTTP/1.1 +Host: example.com +Content-Type: application/xml + + + Bill + iPhone 4 + $199.99 + +``` As you can see, we're just posting some arbitrary XML document to a URL. When the XML is received on the server is it processed within Apache ActiveMQ Artemis @@ -31,29 +33,29 @@ discuss the entire interface in detail later. Why would you want to use Apache ActiveMQ Artemis's REST interface? What are the goals of the REST interface? -- Easily usable by machine-based (code) clients. +- Easily usable by machine-based (code) clients. -- Zero client footprint. We want Apache ActiveMQ Artemis to be usable by any - client/programming language that has an adequate HTTP client - library. You shouldn't have to download, install, and configure a - special library to interact with Apache ActiveMQ Artemis. +- Zero client footprint. We want Apache ActiveMQ Artemis to be usable by any + client/programming language that has an adequate HTTP client + library. You shouldn't have to download, install, and configure a + special library to interact with Apache ActiveMQ Artemis. -- Lightweight interoperability. The HTTP protocol is strong enough to - be our message exchange protocol. Since interactions are RESTful the - HTTP uniform interface provides all the interoperability you need to - communicate between different languages, platforms, and even - messaging implementations that choose to implement the same RESTful - interface as Apache ActiveMQ Artemis (i.e. the [REST-\*](http://www.jboss.org/reststar) - effort.) +- Lightweight interoperability. The HTTP protocol is strong enough to + be our message exchange protocol. Since interactions are RESTful the + HTTP uniform interface provides all the interoperability you need to + communicate between different languages, platforms, and even + messaging implementations that choose to implement the same RESTful + interface as Apache ActiveMQ Artemis (i.e. the [REST-\*](http://www.jboss.org/reststar) + effort.) -- No envelope (e.g. SOAP) or feed (e.g. Atom) format requirements. You - shouldn't have to learn, use, or parse a specific XML document - format in order to send and receive messages through Apache ActiveMQ Artemis's REST - interface. +- No envelope (e.g. SOAP) or feed (e.g. Atom) format requirements. You + shouldn't have to learn, use, or parse a specific XML document + format in order to send and receive messages through Apache ActiveMQ Artemis's REST + interface. -- Leverage the reliability, scalability, and clustering features of - Apache ActiveMQ Artemis on the back end without sacrificing the simplicity of a - REST interface. +- Leverage the reliability, scalability, and clustering features of + Apache ActiveMQ Artemis on the back end without sacrificing the simplicity of a + REST interface. ## Installation and Configuration @@ -63,68 +65,68 @@ Apache ActiveMQ Artemis's REST interface is installed as a Web archive (WAR). It This section should be used when you want to use the Apache ActiveMQ Artemis REST interface in an environment that already has Apache ActiveMQ Artemis installed and running. You must create a Web archive (.WAR) file with the following web.xml settings: - - - - org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap - - - - - - org.apache.activemq.artemis.rest.integration.RestMessagingBootstrapListener - - - - - Rest-Messaging - - org.jboss.resteasy.plugins.server.servlet.FilterDispatcher - - - - - Rest-Messaging - /* - - +```xml + + + org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap + + + + org.apache.activemq.artemis.rest.integration.RestMessagingBootstrapListener + + + + Rest-Messaging + org.jboss.resteasy.plugins.server.servlet.FilterDispatcher + + + + Rest-Messaging + /* + + +``` Within your WEB-INF/lib directory you must have the artemis-rest.jar file. If RESTEasy is not installed within your environment, you must add the RESTEasy jar files within the lib directory as well. Here's a sample Maven pom.xml that can build a WAR with the Apache ActiveMQ Artemis REST library. - - - 4.0.0 - org.somebody - artemis-rest - war - My App - 1.0-SNAPSHOT - - - - org.apache.activemq.rest - artemis-rest - $VERSION - - - * - * - - - - - +```xml + + + 4.0.0 + org.somebody + artemis-rest + war + My App + 1.0-SNAPSHOT + + + + org.apache.activemq.rest + artemis-rest + $VERSION + + + * + * + + + + + +``` The project structure should look this like: - |-- pom.xml - `-- src - `-- main - `-- webapp - `-- WEB-INF - `-- web.xml +``` +|-- pom.xml +`-- src + `-- main + `-- webapp + `-- WEB-INF + `-- web.xml +``` It is worth noting that when deploying a WAR in a Java EE application server like Wildfly the URL for the resulting application will include the name of the WAR by default. For example, if you've constructed a WAR as described above named "activemq-rest.war" then clients will access it at, e.g. http://localhost:8080/activemq-rest/[queues|topics]. We'll see more about this later. @@ -133,70 +135,68 @@ It is worth noting that when deploying a WAR in a Java EE application server lik You can bootstrap Apache ActiveMQ Artemis within your WAR as well. To do this, you must have the Apache ActiveMQ Artemis core and JMS jars along with Netty, RESTEasy, and the Apache ActiveMQ Artemis REST jar within your WEB-INF/lib. You must also have a broker.xml config file within WEB-INF/classes. The examples that come with the Apache ActiveMQ Artemis REST distribution show how to do this. You must also add an additional listener to your web.xml file. Here's an example: - - - - org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap - - - - - - org.apache.activemq.artemis.rest.integration.ActiveMQBootstrapListener - - - - - - org.apache.activemq.artemis.rest.integration.RestMessagingBootstrapListener - - - - - Rest-Messaging - - org.jboss.resteasy.plugins.server.servlet.FilterDispatcher - - - - - Rest-Messaging - /* - - +```xml + + + org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap + + + + org.apache.activemq.artemis.rest.integration.ActiveMQBootstrapListener + + + + org.apache.activemq.artemis.rest.integration.RestMessagingBootstrapListener + + + + Rest-Messaging + org.jboss.resteasy.plugins.server.servlet.FilterDispatcher + + + + Rest-Messaging + /* + + +``` Here's a Maven pom.xml file for creating a WAR for this environment. Make sure your Apache ActiveMQ Artemis configuration file(s) are within the src/main/resources directory so that they are stuffed within the WAR's WEB-INF/classes directory! - - - 4.0.0 - org.somebody - artemis-rest - war - My App - 1.0-SNAPSHOT - - - - org.apache.activemq.rest - artemis-rest - $VERSION - - - +```xml + + + 4.0.0 + org.somebody + artemis-rest + war + My App + 1.0-SNAPSHOT + + + + org.apache.activemq.rest + artemis-rest + $VERSION + + + +``` The project structure should look this like: - |-- pom.xml - `-- src - `-- main - `-- resources - `-- broker.xml - `-- webapp - `-- WEB-INF - `-- web.xml +``` +|-- pom.xml +`-- src + `-- main + `-- resources + `-- broker.xml + `-- webapp + `-- WEB-INF + `-- web.xml +``` ### REST Configuration @@ -207,72 +207,74 @@ WEB-INF/classes directory. You must set the web.xml context-param file. Below is the format of the XML configuration file and the default values for each. - - 0 - false - false - true - topic-push-store - queue-push-store - 0 - 10 - 1 - 300 - -1 - vm://0 - +```xml + + 0 + false + false + true + topic-push-store + queue-push-store + 0 + 10 + 1 + 300 + -1 + vm://0 + +``` Let's give an explanation of each config option. -- `server-in-vm-id`. The Apache ActiveMQ Artemis REST implementation was formerly hard-coded - to use the in-vm transport to communicate with the embedded Apache ActiveMQ Artemis instance. - This is the id of the embedded instance. It is "0" by default. Note: this is deprecated in - favor of `url` which can be used to connect to an arbitrary instance of Apache ActiveMQ - Artemis (including one over the network). +- `server-in-vm-id`. The Apache ActiveMQ Artemis REST implementation was formerly hard-coded + to use the in-vm transport to communicate with the embedded Apache ActiveMQ Artemis instance. + This is the id of the embedded instance. It is "0" by default. **Note:** this is deprecated in + favor of `url` which can be used to connect to an arbitrary instance of Apache ActiveMQ + Artemis (including one over the network). -- `use-link-headers`. By default, all links (URLs) are published using - custom headers. You can instead have the Apache ActiveMQ Artemis REST - implementation publish links using the [Link Header - specification](https://tools.ietf.org/html/draft-nottingham-http-link-header-10) - instead if you desire. +- `use-link-headers`. By default, all links (URLs) are published using + custom headers. You can instead have the Apache ActiveMQ Artemis REST + implementation publish links using the [Link Header + specification](https://tools.ietf.org/html/draft-nottingham-http-link-header-10) + instead if you desire. -- `default-durable-send`. Whether a posted message should be persisted - by default if the user does not specify a durable query parameter. +- `default-durable-send`. Whether a posted message should be persisted + by default if the user does not specify a durable query parameter. -- `dups-ok`. If this is true, no duplicate detection protocol will be - enforced for message posting. +- `dups-ok`. If this is true, no duplicate detection protocol will be + enforced for message posting. -- `topic-push-store-dir`. This must be a relative or absolute file - system path. This is a directory where push registrations for topics - are stored. See [Pushing Messages](#message-push). +- `topic-push-store-dir`. This must be a relative or absolute file + system path. This is a directory where push registrations for topics + are stored. See [Pushing Messages](#message-push). -- `queue-push-store-dir`. This must be a relative or absolute file - system path. This is a directory where push registrations for queues - are stored. See [Pushing Messages](#message-push). +- `queue-push-store-dir`. This must be a relative or absolute file + system path. This is a directory where push registrations for queues + are stored. See [Pushing Messages](#message-push). -- `producer-session-pool-size`. The REST implementation pools Apache ActiveMQ Artemis - sessions for sending messages. This is the size of the pool. That - number of sessions will be created at startup time. +- `producer-session-pool-size`. The REST implementation pools Apache ActiveMQ Artemis + sessions for sending messages. This is the size of the pool. That + number of sessions will be created at startup time. -- `producer-time-to-live`. Default time to live for posted messages. - Default is no ttl. +- `producer-time-to-live`. Default time to live for posted messages. + Default is no ttl. -- `session-timeout-task-interval`. Pull consumers and pull - subscriptions can time out. This is the interval the thread that - checks for timed-out sessions will run at. A value of 1 means it - will run every 1 second. +- `session-timeout-task-interval`. Pull consumers and pull + subscriptions can time out. This is the interval the thread that + checks for timed-out sessions will run at. A value of 1 means it + will run every 1 second. -- `consumer-session-timeout-seconds`. Timeout in seconds for pull - consumers/subscriptions that remain idle for that amount of time. +- `consumer-session-timeout-seconds`. Timeout in seconds for pull + consumers/subscriptions that remain idle for that amount of time. -- `consumer-window-size`. For consumers, this config option is the - same as the Apache ActiveMQ Artemis one of the same name. It will be used by - sessions created by the Apache ActiveMQ Artemis REST implementation. - This is deprecated in favor of `url` as it can be specified as a URL - parameter. +- `consumer-window-size`. For consumers, this config option is the + same as the Apache ActiveMQ Artemis one of the same name. It will be used by + sessions created by the Apache ActiveMQ Artemis REST implementation. + This is deprecated in favor of `url` as it can be specified as a URL + parameter. -- `url`. The URL the Apache ActiveMQ Artemis REST implementation should use - to connect to the Apache ActiveMQ Artemis instance. Default to "vm://0". +- `url`. The URL the Apache ActiveMQ Artemis REST implementation should use + to connect to the Apache ActiveMQ Artemis instance. Default to "vm://0". ## Apache ActiveMQ Artemis REST Interface Basics @@ -288,8 +290,10 @@ in published XML representations. Let's look at how this works. To interact with a queue or topic you do a HEAD or GET request on the following relative URI pattern: - /queues/{name} - /topics/{name} +``` +/queues/{name} +/topics/{name} +``` The base of the URI is the base URL of the WAR you deployed the Apache ActiveMQ Artemis REST server within as defined in the [Installation and @@ -298,22 +302,24 @@ string within the above URI pattern with the name of the queue or topic you are interested in interacting with. Next, perform your HEAD or GET request on this URI. Here's what a request/response would look like. - HEAD /queues/bar HTTP/1.1 - Host: example.com - - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-create-with-id: http://example.com/queues/bar/create/{id} - msg-pull-consumers: http://example.com/queues/bar/pull-consumers - msg-push-consumers: http://example.com/queues/bar/push-consumers +``` +HEAD /queues/bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/queues/bar/create +msg-create-with-id: http://example.com/queues/bar/create/{id} +msg-pull-consumers: http://example.com/queues/bar/pull-consumers +msg-push-consumers: http://example.com/queues/bar/push-consumers +``` -> **Note** +> **Note:** > > You can use the "curl" utility to test this easily. Simply execute a > command like this: > -> curl --head http://example.com/queues/bar +> curl --head http://example.com/queues/bar The HEAD or GET response contains a number of custom response headers that are URLs to additional REST resources that allow you to interact @@ -329,40 +335,40 @@ changes as the Apache ActiveMQ Artemis REST interface evolves over time. Below is a list of response headers you should expect when interacting with a Queue resource. -- `msg-create`. This is a URL you POST messages to. The semantics of - this link are described in [Posting Messages](#posting-messages). +- `msg-create`. This is a URL you POST messages to. The semantics of + this link are described in [Posting Messages](#posting-messages). -- `msg-create-with-id`. This is a URL *template* you can use to POST - messages. The semantics of this link are described in [Posting - Messages](#posting-messages). +- `msg-create-with-id`. This is a URL *template* you can use to POST + messages. The semantics of this link are described in [Posting + Messages](#posting-messages). -- `msg-pull-consumers`. This is a URL for creating consumers that will - pull from a queue. The semantics of this link are described in - [Consuming Messages via Pull](#consuming-messages-via-pull). +- `msg-pull-consumers`. This is a URL for creating consumers that will + pull from a queue. The semantics of this link are described in + [Consuming Messages via Pull](#consuming-messages-via-pull). -- `msg-push-consumers`. This is a URL for registering other URLs you - want the Apache ActiveMQ Artemis REST server to push messages to. The semantics of - this link are described in [Pushing Messages](#pushing-messages). +- `msg-push-consumers`. This is a URL for registering other URLs you + want the Apache ActiveMQ Artemis REST server to push messages to. The semantics of + this link are described in [Pushing Messages](#pushing-messages). ### Topic Resource Response Headers Below is a list of response headers you should expect when interacting with a Topic resource. -- `msg-create`. This is a URL you POST messages to. The semantics of - this link are described in [Posting Messages](#posting-messages). +- `msg-create`. This is a URL you POST messages to. The semantics of + this link are described in [Posting Messages](#posting-messages). -- `msg-create-with-id`. This is a URL *template* you can use to POST - messages. The semantics of this link are described in [Posting - Messages](#posting-messages). +- `msg-create-with-id`. This is a URL *template* you can use to POST + messages. The semantics of this link are described in [Posting + Messages](#posting-messages). -- `msg-pull-subscriptions`. This is a URL for creating subscribers - that will pull from a topic. The semantics of this link are - described in [Consuming Messages via Pull](#consuming-messages-via-pull). +- `msg-pull-subscriptions`. This is a URL for creating subscribers + that will pull from a topic. The semantics of this link are + described in [Consuming Messages via Pull](#consuming-messages-via-pull). -- `msg-push-subscriptions`. This is a URL for registering other URLs - you want the Apache ActiveMQ Artemis REST server to push messages to. The semantics - of this link are described in [Pushing Messages](#pushing-messages). +- `msg-push-subscriptions`. This is a URL for registering other URLs + you want the Apache ActiveMQ Artemis REST server to push messages to. The semantics + of this link are described in [Pushing Messages](#pushing-messages). ## Posting Messages @@ -375,7 +381,7 @@ a simple HTTP message to the URL published by the `msg-create` header. The HTTP message contains whatever content you want to publish to the Apache ActiveMQ Artemis destination. Here's an example scenario: -> **Note** +> **Note:** > > You can also post messages to the URL template found in > `msg-create-with-id`, but this is a more advanced use-case involving @@ -384,31 +390,35 @@ Apache ActiveMQ Artemis destination. Here's an example scenario: 1. Obtain the starting `msg-create` header from the queue or topic resource. - HEAD /queues/bar HTTP/1.1 - Host: example.com + ``` + HEAD /queues/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-create-with-id: http://example.com/queues/bar/create/{id} + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/queues/bar/create + msg-create-with-id: http://example.com/queues/bar/create/{id} + ``` 2. Do a POST to the URL contained in the `msg-create` header. - POST /queues/bar/create - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/create + Host: example.com + Content-Type: application/xml - - Bill - iPhone4
- $199.99 - + + Bill + iPhone4
+ $199.99 + - --- Response --- - HTTP/1.1 201 Created - msg-create-next: http://example.com/queues/bar/create + --- Response --- + HTTP/1.1 201 Created + msg-create-next: http://example.com/queues/bar/create + ``` - > **Note** + > **Note:** > > You can use the "curl" utility to test this easily. Simply execute > a command like this: @@ -422,19 +432,21 @@ Apache ActiveMQ Artemis destination. Here's an example scenario: 3. POST your next message to the queue using the URL returned in the `msg-create-next` header. - POST /queues/bar/create - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/create + Host: example.com + Content-Type: application/xml - - Monica - iPad - $499.99 - + + Monica + iPad + $499.99 + - --- Response -- - HTTP/1.1 201 Created - msg-create-next: http://example.com/queues/bar/create + --- Response -- + HTTP/1.1 201 Created + msg-create-next: http://example.com/queues/bar/create + ``` Continue using the new `msg-create-next` header returned with each response. @@ -467,29 +479,33 @@ discussed earlier. Here's an example: 1. Obtain the starting `msg-create` header from the queue or topic resource. - HEAD /queues/bar HTTP/1.1 - Host: example.com + ``` + HEAD /queues/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-create-with-id: http://example.com/queues/bar/create/{id} + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/queues/bar/create + msg-create-with-id: http://example.com/queues/bar/create/{id} + ``` 2. Do a POST to the URL contained in the `msg-create` header. - POST /queues/bar/create - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/create + Host: example.com + Content-Type: application/xml - - Bill - iPhone4 - $199.99 - + + Bill + iPhone4 + $199.99 + - --- Response --- - HTTP/1.1 307 Redirect - Location: http://example.com/queues/bar/create/13582001787372 + --- Response --- + HTTP/1.1 307 Redirect + Location: http://example.com/queues/bar/create/13582001787372 + ``` A successful response will return a 307 response code. This is standard HTTP protocol. It is telling you that you must re-POST to @@ -498,19 +514,21 @@ discussed earlier. Here's an example: 3. re-POST your message to the URL provided within the `Location` header. - POST /queues/bar/create/13582001787372 - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/create/13582001787372 + Host: example.com + Content-Type: application/xml - - Bill - iPhone4 - $199.99 - + + Bill + iPhone4 + $199.99 + - --- Response -- - HTTP/1.1 201 Created - msg-create-next: http://example.com/queues/bar/create/13582001787373 + --- Response -- + HTTP/1.1 201 Created + msg-create-next: http://example.com/queues/bar/create/13582001787373 + ``` You should receive a 201 Created response. If there is a network failure, just re-POST to the Location header. For new messages, use @@ -518,19 +536,21 @@ discussed earlier. Here's an example: 4. POST any new message to the returned `msg-create-next` header. - POST /queues/bar/create/13582001787373 - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/create/13582001787373 + Host: example.com + Content-Type: application/xml - - Monica - iPad - $499.99 - + + Monica + iPad + $499.99 + - --- Response -- - HTTP/1.1 201 Created - msg-create-next: http://example.com/queues/bar/create/13582001787374 + --- Response -- + HTTP/1.1 201 Created + msg-create-next: http://example.com/queues/bar/create/13582001787374 + ``` If there ever is a network problem, just repost to the URL provided in the `msg-create-next` header. @@ -546,8 +566,10 @@ posted to the system. If you happen to use the same ID more than once you'll see a message like this on the server: - WARN [org.apache.activemq.artemis.core.server] (Thread-3 (Apache ActiveMQ Artemis-remoting-threads-ActiveMQServerImpl::serverUUID=8d6be6f8-5e8b-11e2-80db-51bbde66f473-26319292-267207)) AMQ112098: Duplicate message detected - message will not be routed. Message information: - ServerMessage[messageID=20,priority=4, bodySize=1500,expiration=0, durable=true, address=bar,properties=TypedProperties[{http_content$type=application/x-www-form-urlencoded, http_content$length=3, postedAsHttpMessage=true, _AMQ_DUPL_ID=42}]]@12835058 +``` +WARN [org.apache.activemq.artemis.core.server] (Thread-3 (Apache ActiveMQ Artemis-remoting-threads-ActiveMQServerImpl::serverUUID=8d6be6f8-5e8b-11e2-80db-51bbde66f473-26319292-267207)) AMQ112098: Duplicate message detected - message will not be routed. Message information: +ServerMessage[messageID=20,priority=4, bodySize=1500,expiration=0, durable=true, address=bar,properties=TypedProperties[{http_content$type=application/x-www-form-urlencoded, http_content$length=3, postedAsHttpMessage=true, _AMQ_DUPL_ID=42}]]@12835058 +``` An alternative to this approach is to use the `msg-create-with-id` header. This is not an invokable URL, but a URL template. The idea is @@ -555,7 +577,9 @@ that the client provides the `DUPLICATE_DETECTION_ID` and creates its own `create-next` URL. The `msg-create-with-id` header looks like this (you've see it in previous examples, but we haven't used it): - msg-create-with-id: http://example.com/queues/bar/create/{id} +``` +msg-create-with-id: http://example.com/queues/bar/create/{id} +``` You see that it is a regular URL appended with a `{id}`. This `{id}` is a pattern matching substring. A client would generate its @@ -580,15 +604,17 @@ called `durable` to true when you post your messages to the URLs returned in the `msg-create`, `msg-create-with-id`, or `msg-create-next` headers. here's an example of that. - POST /queues/bar/create?durable=true - Host: example.com - Content-Type: application/xml - - - Bill - iPhone4 - $199.99 - +``` +POST /queues/bar/create?durable=true +Host: example.com +Content-Type: application/xml + + + Bill + iPhone4 + $199.99 + +``` ### TTL, Expiration and Priority @@ -600,15 +626,17 @@ time in milliseconds you want the message active. The `priority` is another query parameter with an integer value between 0 and 9 expressing the priority of the message. i.e.: - POST /queues/bar/create?expiration=30000&priority=3 - Host: example.com - Content-Type: application/xml - - - Bill - iPhone4 - $199.99 - +``` +POST /queues/bar/create?expiration=30000&priority=3 +Host: example.com +Content-Type: application/xml + + + Bill + iPhone4 + $199.99 + +``` ## Consuming Messages via Pull @@ -635,31 +663,31 @@ topic. If you want to use the acknowledgement protocol and/or create a durable subscription (topics only), then you must use the form parameters (`application/x-www-form-urlencoded`) described below. -- `autoAck`. A value of `true` or `false` can be given. This defaults - to `true` if you do not pass this parameter. +- `autoAck`. A value of `true` or `false` can be given. This defaults + to `true` if you do not pass this parameter. -- `durable`. A value of `true` or `false` can be given. This defaults - to `false` if you do not pass this parameter. Only available on - topics. This specifies whether you want a durable subscription or - not. A durable subscription persists through server restart. +- `durable`. A value of `true` or `false` can be given. This defaults + to `false` if you do not pass this parameter. Only available on + topics. This specifies whether you want a durable subscription or + not. A durable subscription persists through server restart. -- `name`. This is the name of the durable subscription. If you do not - provide this parameter, the name will be automatically generated by - the server. Only usable on topics. +- `name`. This is the name of the durable subscription. If you do not + provide this parameter, the name will be automatically generated by + the server. Only usable on topics. -- `selector`. This is an optional JMS selector string. The Apache ActiveMQ Artemis - REST interface adds HTTP headers to the JMS message for REST - produced messages. HTTP headers are prefixed with "http\_" and every - '-' character is converted to a '\$'. +- `selector`. This is an optional JMS selector string. The Apache ActiveMQ Artemis + REST interface adds HTTP headers to the JMS message for REST + produced messages. HTTP headers are prefixed with "http\_" and every + '-' character is converted to a '\$'. -- `idle-timeout`. For a topic subscription, idle time in milliseconds - in which the consumer connections will be closed if idle. +- `idle-timeout`. For a topic subscription, idle time in milliseconds + in which the consumer connections will be closed if idle. -- `delete-when-idle`. Boolean value, If true, a topic subscription - will be deleted (even if it is durable) when the idle timeout is - reached. +- `delete-when-idle`. Boolean value, If true, a topic subscription + will be deleted (even if it is durable) when the idle timeout is + reached. -> **Note** +> **Note:** > > If you have multiple pull-consumers active at the same time on the > same destination be aware that unless the `consumer-window-size` is 0 @@ -672,19 +700,19 @@ This section focuses on the auto-acknowledge protocol for consuming messages via a pull. Here's a list of the response headers and URLs you'll be interested in. -- `msg-pull-consumers`. The URL of a factory resource for creating - queue consumer resources. You will pull from these created - resources. +- `msg-pull-consumers`. The URL of a factory resource for creating + queue consumer resources. You will pull from these created + resources. -- `msg-pull-subscriptions`. The URL of a factory resource for creating - topic subscription resources. You will pull from the created - resources. +- `msg-pull-subscriptions`. The URL of a factory resource for creating + topic subscription resources. You will pull from the created + resources. -- `msg-consume-next`. The URL you will pull the next message from. - This is returned with every response. +- `msg-consume-next`. The URL you will pull the next message from. + This is returned with every response. -- `msg-consumer`. This is a URL pointing back to the consumer or - subscription resource created for the client. +- `msg-consumer`. This is a URL pointing back to the consumer or + subscription resource created for the client. #### Creating an Auto-Ack Consumer or Subscription @@ -693,25 +721,29 @@ Here is an example of creating an auto-acknowledged queue pull consumer. 1. Find the pull-consumers URL by doing a HEAD or GET request to the base queue resource. - HEAD /queues/bar HTTP/1.1 - Host: example.com + ``` + HEAD /queues/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-pull-consumers: http://example.com/queues/bar/pull-consumers - msg-push-consumers: http://example.com/queues/bar/push-consumers + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/queues/bar/create + msg-pull-consumers: http://example.com/queues/bar/pull-consumers + msg-push-consumers: http://example.com/queues/bar/push-consumers + ``` 2. Next do an empty POST to the URL returned in the `msg-pull-consumers` header. - POST /queues/bar/pull-consumers HTTP/1.1 - Host: example.com + ``` + POST /queues/bar/pull-consumers HTTP/1.1 + Host: example.com - --- response --- - HTTP/1.1 201 Created - Location: http://example.com/queues/bar/pull-consumers/auto-ack/333 - msg-consume-next: http://example.com/queues/bar/pull-consumers/auto-ack/333/consume-next-1 + --- response --- + HTTP/1.1 201 Created + Location: http://example.com/queues/bar/pull-consumers/auto-ack/333 + msg-consume-next: http://example.com/queues/bar/pull-consumers/auto-ack/333/consume-next-1 + ``` The `Location` header points to the JMS consumer resource that was created on the server. It is good to remember this URL, although, as @@ -725,29 +757,33 @@ pull subscription. 1. Find the `pull-subscriptions` URL by doing a HEAD or GET request to the base topic resource - HEAD /topics/bar HTTP/1.1 - Host: example.com + ``` + HEAD /topics/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/topics/foo/create - msg-pull-subscriptions: http://example.com/topics/foo/pull-subscriptions - msg-push-subscriptions: http://example.com/topics/foo/push-subscriptions + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/topics/foo/create + msg-pull-subscriptions: http://example.com/topics/foo/pull-subscriptions + msg-push-subscriptions: http://example.com/topics/foo/push-subscriptions + ``` 2. Next do a POST to the URL returned in the `msg-pull-subscriptions` header passing in a `true` value for the `durable` form parameter. - POST /topics/foo/pull-subscriptions HTTP/1.1 - Host: example.com - Content-Type: application/x-www-form-urlencoded + ``` + POST /topics/foo/pull-subscriptions HTTP/1.1 + Host: example.com + Content-Type: application/x-www-form-urlencoded - durable=true + durable=true - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/topics/foo/pull-subscriptions/auto-ack/222 - msg-consume-next: - http://example.com/topics/foo/pull-subscriptions/auto-ack/222/consume-next-1 + --- Response --- + HTTP/1.1 201 Created + Location: http://example.com/topics/foo/pull-subscriptions/auto-ack/222 + msg-consume-next: + http://example.com/topics/foo/pull-subscriptions/auto-ack/222/consume-next-1 + ``` The `Location` header points to the JMS subscription resource that was created on the server. It is good to remember this URL, @@ -779,16 +815,18 @@ resource. 1. Do a POST on the msg-consume-next URL that was returned with the consumer or subscription resource discussed earlier. - POST /queues/bar/pull-consumers/consume-next-1 - Host: example.com + ``` + POST /queues/bar/pull-consumers/consume-next-1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - Content-Type: application/xml - msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-2 - msg-consumer: http://example.com/queues/bar/pull-consumers/333 + --- Response --- + HTTP/1.1 200 Ok + Content-Type: application/xml + msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-2 + msg-consumer: http://example.com/queues/bar/pull-consumers/333 - ... + ... + ``` The POST returns the message consumed from the queue. It also returns a new msg-consume-next link. Use this new link to get the @@ -801,13 +839,15 @@ resource. returns a new msg-consume-next link. Use this new link to get the next message. - POST /queues/bar/pull-consumers/consume-next-2 - Host: example.com + ``` + POST /queues/bar/pull-consumers/consume-next-2 + Host: example.com - --- Response --- - Http/1.1 503 Service Unavailable - Retry-After: 5 - msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-2 + --- Response --- + Http/1.1 503 Service Unavailable + Retry-After: 5 + msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-2 + ``` In this case, there are no messages in the queue, so we get a 503 response back. As per the HTTP 1.1 spec, a 503 response may return a @@ -821,15 +861,17 @@ resource. 3. POST to the URL within the last `msg-consume-next` to get the next message. - POST /queues/bar/pull-consumers/consume-next-2 - Host: example.com + ``` + POST /queues/bar/pull-consumers/consume-next-2 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - Content-Type: application/xml - msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-3 + --- Response --- + HTTP/1.1 200 Ok + Content-Type: application/xml + msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-3 - ... + ... + ``` #### Recovering From Network Failures @@ -874,21 +916,21 @@ you have received the message and that the server can internally ack the message. Here is a list of the response headers you will be interested in. -- `msg-pull-consumers`. The URL of a factory resource for creating - queue consumer resources. You will pull from these created resources +- `msg-pull-consumers`. The URL of a factory resource for creating + queue consumer resources. You will pull from these created resources -- `msg-pull-subscriptions`. The URL of a factory resource for creating - topic subscription resources. You will pull from the created - resources. +- `msg-pull-subscriptions`. The URL of a factory resource for creating + topic subscription resources. You will pull from the created + resources. -- `msg-acknowledge-next`. URL used to obtain the next message in the - queue or topic subscription. It does not acknowledge the message - though. +- `msg-acknowledge-next`. URL used to obtain the next message in the + queue or topic subscription. It does not acknowledge the message + though. -- `msg-acknowledgement`. URL used to acknowledge a message. +- `msg-acknowledgement`. URL used to acknowledge a message. -- `msg-consumer`. This is a URL pointing back to the consumer or - subscription resource created for the client. +- `msg-consumer`. This is a URL pointing back to the consumer or + subscription resource created for the client. #### Creating manually-acknowledged consumers or subscriptions @@ -897,28 +939,32 @@ Here is an example of creating an auto-acknowledged queue pull consumer. 1. Find the pull-consumers URL by doing a HEAD or GET request to the base queue resource. - HEAD /queues/bar HTTP/1.1 - Host: example.com + ``` + HEAD /queues/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-pull-consumers: http://example.com/queues/bar/pull-consumers - msg-push-consumers: http://example.com/queues/bar/push-consumers + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/queues/bar/create + msg-pull-consumers: http://example.com/queues/bar/pull-consumers + msg-push-consumers: http://example.com/queues/bar/push-consumers + ``` 2. Next do a POST to the URL returned in the `msg-pull-consumers` header passing in a `false` value to the `autoAck` form parameter . - POST /queues/bar/pull-consumers HTTP/1.1 - Host: example.com - Content-Type: application/x-www-form-urlencoded + ``` + POST /queues/bar/pull-consumers HTTP/1.1 + Host: example.com + Content-Type: application/x-www-form-urlencoded - autoAck=false + autoAck=false - --- response --- - HTTP/1.1 201 Created - Location: http://example.com/queues/bar/pull-consumers/acknowledged/333 - msg-acknowledge-next: http://example.com/queues/bar/pull-consumers/acknowledged/333/acknowledge-next-1 + --- response --- + HTTP/1.1 201 Created + Location: http://example.com/queues/bar/pull-consumers/acknowledged/333 + msg-acknowledge-next: http://example.com/queues/bar/pull-consumers/acknowledged/333/acknowledge-next-1 + ``` The `Location` header points to the JMS consumer resource that was created on the server. It is good to remember this URL, although, as @@ -932,30 +978,34 @@ topic pull subscription. 1. Find the `pull-subscriptions` URL by doing a HEAD or GET request to the base topic resource - HEAD /topics/bar HTTP/1.1 - Host: example.com + ``` + HEAD /topics/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/topics/foo/create - msg-pull-subscriptions: http://example.com/topics/foo/pull-subscriptions - msg-push-subscriptions: http://example.com/topics/foo/push-subscriptions + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/topics/foo/create + msg-pull-subscriptions: http://example.com/topics/foo/pull-subscriptions + msg-push-subscriptions: http://example.com/topics/foo/push-subscriptions + ``` 2. Next do a POST to the URL returned in the `msg-pull-subscriptions` header passing in a `true` value for the `durable` form parameter and a `false` value to the `autoAck` form parameter. - POST /topics/foo/pull-subscriptions HTTP/1.1 - Host: example.com - Content-Type: application/x-www-form-urlencoded + ``` + POST /topics/foo/pull-subscriptions HTTP/1.1 + Host: example.com + Content-Type: application/x-www-form-urlencoded - durable=true&autoAck=false + durable=true&autoAck=false - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/topics/foo/pull-subscriptions/acknowledged/222 - msg-acknowledge-next: - http://example.com/topics/foo/pull-subscriptions/acknowledged/222/consume-next-1 + --- Response --- + HTTP/1.1 201 Created + Location: http://example.com/topics/foo/pull-subscriptions/acknowledged/222 + msg-acknowledge-next: + http://example.com/topics/foo/pull-subscriptions/acknowledged/222/consume-next-1 + ``` The `Location` header points to the JMS subscription resource that was created on the server. It is good to remember this URL, @@ -982,17 +1032,19 @@ resource. 1. Do a POST on the msg-acknowledge-next URL that was returned with the consumer or subscription resource discussed earlier. - POST /queues/bar/pull-consumers/consume-next-1 - Host: example.com + ``` + POST /queues/bar/pull-consumers/consume-next-1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - Content-Type: application/xml - msg-acknowledgement: - http://example.com/queues/bar/pull-consumers/333/acknowledgement/2 - msg-consumer: http://example.com/queues/bar/pull-consumers/333 + --- Response --- + HTTP/1.1 200 Ok + Content-Type: application/xml + msg-acknowledgement: + http://example.com/queues/bar/pull-consumers/333/acknowledgement/2 + msg-consumer: http://example.com/queues/bar/pull-consumers/333 - ... + ... + ``` The POST returns the message consumed from the queue. It also returns a`msg-acknowledgemen`t link. You will use this new link to @@ -1007,16 +1059,18 @@ resource. whether you want to acknowledge or unacknowledge the message on the server. - POST /queues/bar/pull-consumers/acknowledgement/2 - Host: example.com - Content-Type: application/x-www-form-urlencoded + ``` + POST /queues/bar/pull-consumers/acknowledgement/2 + Host: example.com + Content-Type: application/x-www-form-urlencoded - acknowledge=true + acknowledge=true - --- Response --- - Http/1.1 204 Ok - msg-acknowledge-next: - http://example.com/queues/bar/pull-consumers/333/acknowledge-next-2 + --- Response --- + Http/1.1 204 Ok + msg-acknowledge-next: + http://example.com/queues/bar/pull-consumers/333/acknowledge-next-2 + ``` Whether you acknowledge or unacknowledge the message, the response will contain a new msg-acknowledge-next header that you must use to @@ -1075,16 +1129,18 @@ response from the server. The value of this header is the time in seconds the client is willing to block for. You would send this request header with your pull requests. Here's an example: - POST /queues/bar/pull-consumers/consume-next-2 - Host: example.com - Accept-Wait: 30 +``` +POST /queues/bar/pull-consumers/consume-next-2 +Host: example.com +Accept-Wait: 30 - --- Response --- - HTTP/1.1 200 Ok - Content-Type: application/xml - msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-3 +--- Response --- +HTTP/1.1 200 Ok +Content-Type: application/xml +msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-3 - ... +... +``` In this example, we're posting to a msg-consume-next URL and telling the server that we would be willing to block for 30 seconds. @@ -1117,17 +1173,19 @@ provide a URL to ship the forwarded message to. Finally, you have to provide authentication information if the final endpoint requires authentication. Here's a simple example: - - false - 1 - ]]> - - - 5 - 1000 - true - +```xml + + false + 1 + ]]> + + + 5 + 1000 + true + +``` The `durable` element specifies whether the registration should be saved to disk so that if there is a server restart, the push subscription will @@ -1162,36 +1220,42 @@ it is not provided it defaults to POST. The `rel` attribute is very important and the value of it triggers different behavior. Here's the values a rel attribute can have: -- `destination`. The href URL is assumed to be a queue or topic - resource of another Apache ActiveMQ Artemis REST server. The push registration will - initially do a HEAD request to this URL to obtain a - msg-create-with-id header. It will use this header to push new - messages to the Apache ActiveMQ Artemis REST endpoint reliably. Here's an example: - - - - - -- `template`. In this case, the server is expecting the link element's - href attribute to be a URL expression. The URL expression must have - one and only one URL parameter within it. The server will use a - unique value to create the endpoint URL. Here's an example: - - - - - - In this example, the {id} sub-string is the one and only one URL - parameter. - -- `user defined`. If the rel attributes is not destination or template - (or is empty or missing), then the server will send an HTTP message - to the href URL using the HTTP method defined in the method - attribute. Here's an example: - - - - +- `destination`. The href URL is assumed to be a queue or topic + resource of another Apache ActiveMQ Artemis REST server. The push registration will + initially do a HEAD request to this URL to obtain a + msg-create-with-id header. It will use this header to push new + messages to the Apache ActiveMQ Artemis REST endpoint reliably. Here's an example: + + ```xml + + + + ``` + +- `template`. In this case, the server is expecting the link element's + href attribute to be a URL expression. The URL expression must have + one and only one URL parameter within it. The server will use a + unique value to create the endpoint URL. Here's an example: + + ``` + + + + ``` + + In this example, the {id} sub-string is the one and only one URL + parameter. + +- `user defined`. If the rel attributes is not destination or template + (or is empty or missing), then the server will send an HTTP message + to the href URL using the HTTP method defined in the method + attribute. Here's an example: + + ``` + + + + ``` ### The Topic Push Subscription XML @@ -1200,14 +1264,16 @@ push-topic-registration. (Also remember the `selector` element is optional). The rest of the document is the same. Here's an example of a template registration: - - true - 1 - ]]> - - - +```xml + + true + 1 + ]]> + + + +``` ### Creating a Push Subscription at Runtime @@ -1218,29 +1284,33 @@ Here's an example of creating a push registration for a queue: 1. First do a HEAD request to the queue resource: - HEAD /queues/bar HTTP/1.1 - Host: example.com + ``` + HEAD /queues/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-pull-consumers: http://example.com/queues/bar/pull-consumers - msg-push-consumers: http://example.com/queues/bar/push-consumers + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/queues/bar/create + msg-pull-consumers: http://example.com/queues/bar/pull-consumers + msg-push-consumers: http://example.com/queues/bar/push-consumers + ``` 2. Next POST your subscription XML to the URL returned from msg-push-consumers header - POST /queues/bar/push-consumers - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/push-consumers + Host: example.com + Content-Type: application/xml - - - + + + - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/queues/bar/push-consumers/1-333-1212 + --- Response --- + HTTP/1.1 201 Created + Location: http://example.com/queues/bar/push-consumers/1-333-1212 + ``` The Location header contains the URL for the created resource. If you want to unregister this, then do a HTTP DELETE on this URL. @@ -1249,29 +1319,33 @@ Here's an example of creating a push registration for a topic: 1. First do a HEAD request to the topic resource: - HEAD /topics/bar HTTP/1.1 - Host: example.com + ``` + HEAD /topics/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/topics/bar/create - msg-pull-subscriptions: http://example.com/topics/bar/pull-subscriptions - msg-push-subscriptions: http://example.com/topics/bar/push-subscriptions + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/topics/bar/create + msg-pull-subscriptions: http://example.com/topics/bar/pull-subscriptions + msg-push-subscriptions: http://example.com/topics/bar/push-subscriptions + ``` 2. Next POST your subscription XML to the URL returned from msg-push-subscriptions header - POST /topics/bar/push-subscriptions - Host: example.com - Content-Type: application/xml + ``` + POST /topics/bar/push-subscriptions + Host: example.com + Content-Type: application/xml - - - + + + - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/topics/bar/push-subscriptions/1-333-1212 + --- Response --- + HTTP/1.1 201 Created + Location: http://example.com/topics/bar/push-subscriptions/1-333-1212 + ``` The Location header contains the URL for the created resource. If you want to unregister this, then do a HTTP DELETE on this URL. @@ -1291,38 +1365,44 @@ Here's an example of a hand-created queue registration. This file must go in the directory specified by the queue-push-store-dir config variable defined in Chapter 2: - - bar - true - - +```xml + + bar + true + + +``` Here's an example of a hand-created topic registration. This file must go in the directory specified by the topic-push-store-dir config variable defined in Chapter 2: - - my-subscription-1true - - foo - +```xml + + my-subscription-1true + + foo + +``` ### Pushing to Authenticated Servers Push subscriptions only support BASIC and DIGEST authentication out of the box. Here is an example of adding BASIC authentication: - - true - - - - guest - geheim - - - +```xml + + true + + + + guest + geheim + + + +``` For DIGEST, just replace basic-auth with digest-auth. @@ -1331,11 +1411,13 @@ transmitted with each request. Use the header element with the name attribute representing the name of the header. Here's what custom headers might look like: - - true - -
jfdiwe3321
- +```xml + + true + +
jfdiwe3321
+
+``` ## Creating Destinations @@ -1344,32 +1426,36 @@ Currently you cannot create a temporary queue or topic. To create a queue you do a POST to the relative URL /queues with an XML representation of the queue. For example: - POST /queues - Host: example.com - Content-Type: application/activemq.xml +``` +POST /queues +Host: example.com +Content-Type: application/activemq.xml - - true - + + true + - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/queues/testQueue +--- Response --- +HTTP/1.1 201 Created +Location: http://example.com/queues/testQueue +``` Notice that the Content-Type is application/activemq.xml. Here's what creating a topic would look like: - POST /topics - Host: example.com - Content-Type: application/activemq.xml +``` +POST /topics +Host: example.com +Content-Type: application/activemq.xml - - + + - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/topics/testTopic +--- Response --- +HTTP/1.1 201 Created +Location: http://example.com/topics/testTopic +``` ## Securing the Apache ActiveMQ Artemis REST Interface @@ -1389,16 +1475,16 @@ web.xml for every path of every queue and topic you have deployed. Here is a list of URI patterns: Post | Description - --- | --- - /queues | secure the POST operation to secure queue creation - /queues/{queue-name}/create/ | secure this URL pattern for producing messages. - /queues/{queue-name}/pull-consumers/ | secure this URL pattern for pushing messages. - /queues/{queue-name}/push-consumers/ | secure the POST operation to secure topic creation - /topics | secure the POST operation to secure topic creation - /topics/{topic-name} | secure the GET HEAD operation to getting information about the topic. - /topics/{topic-name}/create/ | secure this URL pattern for producing messages - /topics/{topic-name}/pull-subscriptions/ | secure this URL pattern for pulling messages - /topics/{topic-name}/push-subscriptions/ | secure this URL pattern for pushing messages +--- | --- +/queues | secure the POST operation to secure queue creation +/queues/{queue-name}/create/ | secure this URL pattern for producing messages. +/queues/{queue-name}/pull-consumers/ | secure this URL pattern for pushing messages. +/queues/{queue-name}/push-consumers/ | secure the POST operation to secure topic creation +/topics | secure the POST operation to secure topic creation +/topics/{topic-name} | secure the GET HEAD operation to getting information about the topic. +/topics/{topic-name}/create/ | secure this URL pattern for producing messages +/topics/{topic-name}/pull-subscriptions/ | secure this URL pattern for pulling messages +/topics/{topic-name}/push-subscriptions/ | secure this URL pattern for pushing messages ## Mixing JMS and REST @@ -1419,8 +1505,10 @@ server will use RESTEasy content handlers (MessageBodyReader/Writers) to transform the Java object to the type desired. Here's an example of a JMS producer setting the content type of the message. - ObjectMessage message = session.createObjectMessage(); - message.setStringProperty(org.apache.activemq.rest.HttpHeaderProperty.CONTENT_TYPE, "application/xml"); +```java +ObjectMessage message = session.createObjectMessage(); +message.setStringProperty(org.apache.activemq.rest.HttpHeaderProperty.CONTENT_TYPE, "application/xml"); +``` If the JMS producer does not set the content-type, then this information must be obtained from the REST consumer. If it is a pull consumer, then @@ -1436,8 +1524,7 @@ Apache ActiveMQ Artemis REST has a simple helper class for you to transform the body to a Java object. Here's some example code: ```java -public void onMessage(Message message) -{ +public void onMessage(Message message) { MyType obj = org.apache.activemq.rest.Jms.getEntity(message, MyType.class); } ``` diff --git a/docs/user-manual/en/scheduled-messages.md b/docs/user-manual/en/scheduled-messages.md index 6dff58a0256..9d1f07f31bc 100644 --- a/docs/user-manual/en/scheduled-messages.md +++ b/docs/user-manual/en/scheduled-messages.md @@ -30,5 +30,5 @@ same property on the core message before sending. ## Example -See the [examples](examples.md) chapter for an example which shows how scheduled messages can be used with +See the [Scheduled Message Example](examples.md#scheduled-message) which shows how scheduled messages can be used with JMS. diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md index 2f707014936..dfd1a41b2b0 100644 --- a/docs/user-manual/en/security.md +++ b/docs/user-manual/en/security.md @@ -1,606 +1,755 @@ # Security -This chapter describes how security works with Apache ActiveMQ Artemis and how you can -configure it. To disable security completely simply set the -`security-enabled` property to false in the `broker.xml` -file. - -For performance reasons security is cached and invalidated every so -long. To change this period set the property -`security-invalidation-interval`, which is in milliseconds. The default -is `10000` ms. - -To assist in security auditing the `populate-validated-user` option exists. If this is `true` then -the server will add the name of the validated user to the message using the key `_AMQ_VALIDATED_USER`. -For JMS and Stomp clients this is mapped to the key `JMSXUserID`. For users authenticated based on -their SSL certificate this name is the name to which their certificate's DN maps. If `security-enabled` -is `false` and `populate-validated-user` is `true` then the server will simply use whatever user name -(if any) the client provides. This option is `false` by default. +This chapter describes how security works with Apache ActiveMQ Artemis and how +you can configure it. To disable security completely simply set the +`security-enabled` property to false in the `broker.xml` file. + +For performance reasons security is cached and invalidated every so long. To +change this period set the property `security-invalidation-interval`, which is +in milliseconds. The default is `10000` ms. + +## Tracking the Validated User + +To assist in security auditing the `populate-validated-user` option exists. If +this is `true` then the server will add the name of the validated user to the +message using the key `_AMQ_VALIDATED_USER`. For JMS and Stomp clients this is +mapped to the key `JMSXUserID`. For users authenticated based on their SSL +certificate this name is the name to which their certificate's DN maps. If +`security-enabled` is `false` and `populate-validated-user` is `true` then the +server will simply use whatever user name (if any) the client provides. This +option is `false` by default. ## Role based security for addresses -Apache ActiveMQ Artemis contains a flexible role-based security model for applying -security to queues, based on their addresses. +Apache ActiveMQ Artemis contains a flexible role-based security model for +applying security to queues, based on their addresses. -As explained in [Using Core](using-core.md), Apache ActiveMQ Artemis core consists mainly of sets of queues bound -to addresses. A message is sent to an address and the server looks up -the set of queues that are bound to that address, the server then routes -the message to those set of queues. +As explained in [Using Core](core.md), Apache ActiveMQ Artemis core consists +mainly of sets of queues bound to addresses. A message is sent to an address +and the server looks up the set of queues that are bound to that address, the +server then routes the message to those set of queues. -Apache ActiveMQ Artemis allows sets of permissions to be defined against the queues -based on their address. An exact match on the address can be used or a -wildcard match can be used using the wildcard characters '`#`' and -'`*`'. +Apache ActiveMQ Artemis allows sets of permissions to be defined against the +queues based on their address. An exact match on the address can be used or a +[wildcard match](wildcard-syntax.md) can be used. -Eight different permissions can be given to the set of queues which -match the address. Those permissions are: +Eight different permissions can be given to the set of queues which match the +address. Those permissions are: -- `createAddress`. This permission allows the user to create an - address fitting the `match`. +- `createAddress`. This permission allows the user to create an address fitting + the `match`. -- `deleteAddress`. This permission allows the user to delete an - address fitting the `match`. +- `deleteAddress`. This permission allows the user to delete an address fitting + the `match`. -- `createDurableQueue`. This permission allows the user to create a - durable queue under matching addresses. +- `createDurableQueue`. This permission allows the user to create a durable + queue under matching addresses. -- `deleteDurableQueue`. This permission allows the user to delete a - durable queue under matching addresses. +- `deleteDurableQueue`. This permission allows the user to delete a durable + queue under matching addresses. -- `createNonDurableQueue`. This permission allows the user to create a - non-durable queue under matching addresses. +- `createNonDurableQueue`. This permission allows the user to create a + non-durable queue under matching addresses. -- `deleteNonDurableQueue`. This permission allows the user to delete a - non-durable queue under matching addresses. +- `deleteNonDurableQueue`. This permission allows the user to delete a + non-durable queue under matching addresses. -- `send`. This permission allows the user to send a message to - matching addresses. +- `send`. This permission allows the user to send a message to matching + addresses. -- `consume`. This permission allows the user to consume a message from - a queue bound to matching addresses. +- `consume`. This permission allows the user to consume a message from a queue + bound to matching addresses. -- `browse`. This permission allows the user to browse a queue bound to - the matching address. +- `browse`. This permission allows the user to browse a queue bound to the + matching address. -- `manage`. This permission allows the user to invoke management - operations by sending management messages to the management address. +- `manage`. This permission allows the user to invoke management operations by + sending management messages to the management address. For each permission, a list of roles who are granted that permission is -specified. If the user has any of those roles, he/she will be granted -that permission for that set of addresses. - -Let's take a simple example, here's a security block from -`broker.xml` file: - - - - - - - - - - -The '`#`' character signifies "any sequence of words". Words are -delimited by the '`.`' character. For a full description of the wildcard -syntax please see [Understanding the Wildcard Syntax](wildcard-syntax.md). -The above security block applies to any address -that starts with the string "globalqueues.europe.": - -Only users who have the `admin` role can create or delete durable queues -bound to an address that starts with the string "globalqueues.europe." - -Any users with the roles `admin`, `guest`, or `europe-users` can create -or delete temporary queues bound to an address that starts with the -string "globalqueues.europe." - -Any users with the roles `admin` or `europe-users` can send messages to -these addresses or consume messages from queues bound to an address that -starts with the string "globalqueues.europe." - -The mapping between a user and what roles they have is handled by the -security manager. Apache ActiveMQ Artemis ships with a user manager that reads user +specified. If the user has any of those roles, he/she will be granted that +permission for that set of addresses. + +Let's take a simple example, here's a security block from `broker.xml` file: + +```xml + + + + + + + + +``` + +Using the default [wildcard syntax](wildcard-syntax.md) the `#` character +signifies "any sequence of words". Words are delimited by the `.` character. +Therefore, the above security block applies to any address that starts with the +string "globalqueues.europe.". + +Only users who have the `admin` role can create or delete durable queues bound +to an address that starts with the string "globalqueues.europe." + +Any users with the roles `admin`, `guest`, or `europe-users` can create or +delete temporary queues bound to an address that starts with the string +"globalqueues.europe." + +Any users with the roles `admin` or `europe-users` can send messages to these +addresses or consume messages from queues bound to an address that starts with +the string "globalqueues.europe." + +The mapping between a user and what roles they have is handled by the security +manager. Apache ActiveMQ Artemis ships with a user manager that reads user credentials from a file on disk, and can also plug into JAAS or JBoss Application Server security. -For more information on configuring the security manager, please see 'Changing the Security Manager'. +For more information on configuring the security manager, please see 'Changing +the Security Manager'. -There can be zero or more `security-setting` elements in each xml file. -Where more than one match applies to a set of addresses the *more -specific* match takes precedence. +There can be zero or more `security-setting` elements in each xml file. Where +more than one match applies to a set of addresses the *more specific* match +takes precedence. -Let's look at an example of that, here's another `security-setting` -block: +Let's look at an example of that, here's another `security-setting` block: - - - - +```xml + + + + +``` -In this `security-setting` block the match -'globalqueues.europe.orders.\#' is more specific than the previous match -'globalqueues.europe.\#'. So any addresses which match -'globalqueues.europe.orders.\#' will take their security settings *only* -from the latter security-setting block. +In this `security-setting` block the match 'globalqueues.europe.orders.\#' is +more specific than the previous match 'globalqueues.europe.\#'. So any +addresses which match 'globalqueues.europe.orders.\#' will take their security +settings *only* from the latter security-setting block. -Note that settings are not inherited from the former block. All the -settings will be taken from the more specific matching block, so for the -address 'globalqueues.europe.orders.plastics' the only permissions that -exist are `send` and `consume` for the role europe-users. The -permissions `createDurableQueue`, `deleteDurableQueue`, -`createNonDurableQueue`, `deleteNonDurableQueue` are not inherited from -the other security-setting block. +Note that settings are not inherited from the former block. All the settings +will be taken from the more specific matching block, so for the address +'globalqueues.europe.orders.plastics' the only permissions that exist are +`send` and `consume` for the role europe-users. The permissions +`createDurableQueue`, `deleteDurableQueue`, `createNonDurableQueue`, +`deleteNonDurableQueue` are not inherited from the other security-setting +block. -By not inheriting permissions, it allows you to effectively deny -permissions in more specific security-setting blocks by simply not -specifying them. Otherwise it would not be possible to deny permissions -in sub-groups of addresses. +By not inheriting permissions, it allows you to effectively deny permissions in +more specific security-setting blocks by simply not specifying them. Otherwise +it would not be possible to deny permissions in sub-groups of addresses. ## Security Setting Plugin -Aside from configuring sets of permissions via XML these permissions can alternatively be -configured via a plugin which implements `org.apache.activemq.artemis.core.server.SecuritySettingPlugin` e.g.: - - - - - - - - - - - +Aside from configuring sets of permissions via XML these permissions can +alternatively be configured via a plugin which implements +`org.apache.activemq.artemis.core.server.SecuritySettingPlugin` e.g.: + +```xml + + + + + + + + + + +``` -Most of this configuration is specific to the plugin implementation. However, there are two configuration details that -will be specified for every implementation: +Most of this configuration is specific to the plugin implementation. However, +there are two configuration details that will be specified for every +implementation: -- `class-name`. This attribute of `security-setting-plugin` indicates the name of the class which implements - `org.apache.activemq.artemis.core.server.SecuritySettingPlugin`. +- `class-name`. This attribute of `security-setting-plugin` indicates the name + of the class which implements + `org.apache.activemq.artemis.core.server.SecuritySettingPlugin`. -- `setting`. Each of these elements represents a name/value pair that will be passed to the implementation for configuration - purposes. +- `setting`. Each of these elements represents a name/value pair that will be + passed to the implementation for configuration purposes. -See the JavaDoc on `org.apache.activemq.artemis.core.server.SecuritySettingPlugin` for further details about the interface -and what each method is expected to do. +See the JavaDoc on +`org.apache.activemq.artemis.core.server.SecuritySettingPlugin` for further +details about the interface and what each method is expected to do. ### Available plugins #### LegacyLDAPSecuritySettingPlugin -This plugin will read the security information that was previously handled by [`LDAPAuthorizationMap`](http://activemq.apache.org/security.html) -and the [`cachedLDAPAuthorizationMap`](http://activemq.apache.org/cached-ldap-authorization-module.html) in Apache ActiveMQ 5.x -and turn it into Artemis security settings where possible. The security implementations of ActiveMQ 5.x and Artemis don't -match perfectly so some translation must occur to achieve near equivalent functionality. +This plugin will read the security information that was previously handled by +[`LDAPAuthorizationMap`](http://activemq.apache.org/security.html) and the +[`cachedLDAPAuthorizationMap`](http://activemq.apache.org/cached-ldap-authorization-module.html) +in Apache ActiveMQ 5.x and turn it into Artemis security settings where +possible. The security implementations of ActiveMQ 5.x and Artemis don't match +perfectly so some translation must occur to achieve near equivalent +functionality. Here is an example of the plugin's configuration: - - - - - - - - - -- `class-name`. The implementation is `org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin`. - -- `initialContextFactory`. The initial context factory used to connect to LDAP. It must always be set to - `com.sun.jndi.ldap.LdapCtxFactory` (i.e. the default value). - -- `connectionURL`. Specifies the location of the directory server using an ldap URL, `ldap://Host:Port`. You can - optionally qualify this URL, by adding a forward slash, `/`, followed by the DN of a particular node in the directory - tree. For example, `ldap://ldapserver:10389/ou=system`. The default is `ldap://localhost:1024`. - -- `connectionUsername`. The DN of the user that opens the connection to the directory server. For example, `uid=admin,ou=system`. - Directory servers generally require clients to present username/password credentials in order to open a connection. - -- `connectionPassword`. The password that matches the DN from `connectionUsername`. In the directory server, in the - DIT, the password is normally stored as a `userPassword` attribute in the corresponding directory entry. - -- `connectionProtocol`. Currently the only supported value is a blank string. In future, this option will allow you to - select the Secure Socket Layer (SSL) for the connection to the directory server. Note: this option must be set - explicitly to an empty string, because it has no default value. - -- `authentication`. Specifies the authentication method used when binding to the LDAP server. Can take either of the - values, `simple` (username and password, the default value) or `none` (anonymous). Note: Simple Authentication and - Security Layer (SASL) authentication is currently not supported. - -- `destinationBase`. Specifies the DN of the node whose children provide the permissions for all destinations. In this - case the DN is a literal value (that is, no string substitution is performed on the property value). For example, a - typical value of this property is `ou=destinations,o=ActiveMQ,ou=system` (i.e. the default value). - -- `filter`. Specifies an LDAP search filter, which is used when looking up the permissions for any kind of destination. - The search filter attempts to match one of the children or descendants of the queue or topic node. The default value - is `(cn=*)`. - -- `roleAttribute`. Specifies an attribute of the node matched by `filter`, whose value is the DN of a role. Default - value is `uniqueMember`. - -- `adminPermissionValue`. Specifies a value that matches the `admin` permission. The default value is `admin`. - -- `readPermissionValue`. Specifies a value that matches the `read` permission. The default value is `read`. - -- `writePermissionValue`. Specifies a value that matches the `write` permission. The default value is `write`. - -- `enableListener`. Whether or not to enable a listener that will automatically receive updates made in the LDAP server - and update the broker's authorization configuration in real-time. The default value is `true`. - -The name of the queue or topic defined in LDAP will serve as the "match" for the security-setting, the permission value -will be mapped from the ActiveMQ 5.x type to the Artemis type, and the role will be mapped as-is. - -ActiveMQ 5.x only has 3 permission types - `read`, `write`, and `admin`. These permission types are described on their -[website](http://activemq.apache.org/security.html). However, as described previously, ActiveMQ Artemis has 9 permission -types - `createAddress`, `deleteAddress`, `createDurableQueue`, `deleteDurableQueue`, `createNonDurableQueue`, -`deleteNonDurableQueue`, `send`, `consume`, `browse`, and `manage`. Here's how the old types are mapped to the new types: - -- `read` - `consume`, `browse` -- `write` - `send` -- `admin` - `createAddress`, `deleteAddress`, `createDurableQueue`, `deleteDurableQueue`, `createNonDurableQueue`, - `deleteNonDurableQueue` - -As mentioned, there are a few places where a translation was performed to achieve some equivalence.: - -- This mapping doesn't include the Artemis `manage` permission type since there is no type analogous for that in ActiveMQ - 5.x. +```xml + + + + + + + + +``` -- The `admin` permission in ActiveMQ 5.x relates to whether or not the broker will auto-create a destination if - it doesn't exist and the user sends a message to it. Artemis automatically allows the automatic creation of a - destination if the user has permission to send message to it. Therefore, the plugin will map the `admin` permission - to the 4 aforementioned permissions in Artemis. +- `class-name`. The implementation is + `org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin`. + +- `initialContextFactory`. The initial context factory used to connect to LDAP. + It must always be set to `com.sun.jndi.ldap.LdapCtxFactory` (i.e. the default + value). + +- `connectionURL`. Specifies the location of the directory server using an ldap + URL, `ldap://Host:Port`. You can optionally qualify this URL, by adding a + forward slash, `/`, followed by the DN of a particular node in the directory + tree. For example, `ldap://ldapserver:10389/ou=system`. The default is + `ldap://localhost:1024`. + +- `connectionUsername`. The DN of the user that opens the connection to the + directory server. For example, `uid=admin,ou=system`. Directory servers + generally require clients to present username/password credentials in order to + open a connection. + +- `connectionPassword`. The password that matches the DN from + `connectionUsername`. In the directory server, in the DIT, the password is + normally stored as a `userPassword` attribute in the corresponding directory + entry. + +- `connectionProtocol`. Currently the only supported value is a blank string. + In future, this option will allow you to select the Secure Socket Layer (SSL) +for the connection to the directory server. **Note:** this option must be set +explicitly to an empty string, because it has no default value. + +- `authentication`. Specifies the authentication method used when binding to + the LDAP server. Can take either of the values, `simple` (username and +password, the default value) or `none` (anonymous). **Note:** Simple Authentication +and Security Layer (SASL) authentication is currently not supported. + +- `destinationBase`. Specifies the DN of the node whose children provide the + permissions for all destinations. In this case the DN is a literal value + (that is, no string substitution is performed on the property value). For + example, a typical value of this property is + `ou=destinations,o=ActiveMQ,ou=system` (i.e. the default value). + +- `filter`. Specifies an LDAP search filter, which is used when looking up the + permissions for any kind of destination. The search filter attempts to match + one of the children or descendants of the queue or topic node. The default + value is `(cn=*)`. + +- `roleAttribute`. Specifies an attribute of the node matched by `filter`, + whose value is the DN of a role. Default value is `uniqueMember`. + +- `adminPermissionValue`. Specifies a value that matches the `admin` + permission. The default value is `admin`. + +- `readPermissionValue`. Specifies a value that matches the `read` permission. + The default value is `read`. + +- `writePermissionValue`. Specifies a value that matches the `write` + permission. The default value is `write`. + +- `enableListener`. Whether or not to enable a listener that will automatically + receive updates made in the LDAP server and update the broker's authorization + configuration in real-time. The default value is `true`. + +The name of the queue or topic defined in LDAP will serve as the "match" for +the security-setting, the permission value will be mapped from the ActiveMQ 5.x +type to the Artemis type, and the role will be mapped as-is. + +ActiveMQ 5.x only has 3 permission types - `read`, `write`, and `admin`. These +permission types are described on their +[website](http://activemq.apache.org/security.html). However, as described +previously, ActiveMQ Artemis has 9 permission types - `createAddress`, +`deleteAddress`, `createDurableQueue`, `deleteDurableQueue`, +`createNonDurableQueue`, `deleteNonDurableQueue`, `send`, `consume`, `browse`, +and `manage`. Here's how the old types are mapped to the new types: + +- `read` - `consume`, `browse` +- `write` - `send` +- `admin` - `createAddress`, `deleteAddress`, `createDurableQueue`, + `deleteDurableQueue`, `createNonDurableQueue`, `deleteNonDurableQueue` + +As mentioned, there are a few places where a translation was performed to +achieve some equivalence.: + +- This mapping doesn't include the Artemis `manage` permission type since there + is no type analogous for that in ActiveMQ 5.x. + +- The `admin` permission in ActiveMQ 5.x relates to whether or not the broker + will auto-create a destination if it doesn't exist and the user sends a + message to it. Artemis automatically allows the automatic creation of a + destination if the user has permission to send message to it. Therefore, the + plugin will map the `admin` permission to the 4 aforementioned permissions in + Artemis. ## Secure Sockets Layer (SSL) Transport -When messaging clients are connected to servers, or servers are connected to other servers (e.g. via bridges) over an -untrusted network then Apache ActiveMQ Artemis allows that traffic to be encrypted using the Secure Sockets Layer (SSL) -transport. +When messaging clients are connected to servers, or servers are connected to +other servers (e.g. via bridges) over an untrusted network then Apache ActiveMQ +Artemis allows that traffic to be encrypted using the Secure Sockets Layer +(SSL) transport. -For more information on configuring the SSL transport, please see [Configuring the Transport](configuring-transports.md). +For more information on configuring the SSL transport, please see [Configuring +the Transport](configuring-transports.md). ## User credentials Apache ActiveMQ Artemis ships with two security manager implementations: -- The legacy, deprecated `ActiveMQSecurityManager` that reads user credentials, i.e. user names, passwords and role -information from properties files on the classpath called `artemis-users.properties` and `artemis-roles.properties`. +- The legacy, deprecated `ActiveMQSecurityManager` that reads user credentials, + i.e. user names, passwords and role information from properties files on the + classpath called `artemis-users.properties` and `artemis-roles.properties`. -- The flexible, pluggable `ActiveMQJAASSecurityManager` which supports any standard JAAS login module. Artemis ships -with several login modules which will be discussed further down. This is the default security manager. +- The flexible, pluggable `ActiveMQJAASSecurityManager` which supports any + standard JAAS login module. Artemis ships with several login modules which + will be discussed further down. This is the default security manager. ### JAAS Security Manager -When using the Java Authentication and Authorization Service (JAAS) much of the configuration depends on which login -module is used. However, there are a few commonalities for every case. -The first place to look is in `bootstrap.xml`. Here is an example using the `PropertiesLogin` JAAS login -module which reads user, password, and role information from properties files: +When using the Java Authentication and Authorization Service (JAAS) much of the +configuration depends on which login module is used. However, there are a few +commonalities for every case. The first place to look is in `bootstrap.xml`. +Here is an example using the `PropertiesLogin` JAAS login module which reads +user, password, and role information from properties files: - +```xml + +``` -No matter what login module you're using, you'll need to specify it here in `bootstrap.xml`. The `domain` attribute -here refers to the relevant login module entry in `login.config`. For example: +No matter what login module you're using, you'll need to specify it here in +`bootstrap.xml`. The `domain` attribute here refers to the relevant login +module entry in `login.config`. For example: - PropertiesLogin { - org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule required - debug=true - org.apache.activemq.jaas.properties.user="artemis-users.properties" - org.apache.activemq.jaas.properties.role="artemis-roles.properties"; - }; +``` +PropertiesLogin { + org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule required + debug=true + org.apache.activemq.jaas.properties.user="artemis-users.properties" + org.apache.activemq.jaas.properties.role="artemis-roles.properties"; +}; +``` -The `login.config` file is a standard JAAS configuration file. You can read more about this file on -[Oracle's website](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/LoginConfigFile.html). +The `login.config` file is a standard JAAS configuration file. You can read +more about this file on [Oracle's +website](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/LoginConfigFile.html). In short, the file defines: -- an alias for an entry (e.g. `PropertiesLogin`) +- an alias for an entry (e.g. `PropertiesLogin`) -- the implementation class for the login module (e.g. `org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule`) +- the implementation class for the login module (e.g. + `org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule`) -- a flag which indicates whether the success of the login module is `required`, `requisite`, `sufficient`, or `optional` -(see more details on these flags in the [JavaDoc](https://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/Configuration.html) +- a flag which indicates whether the success of the login module is `required`, + `requisite`, `sufficient`, or `optional` (see more details on these flags in + the + [JavaDoc](https://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/Configuration.html) -- a list of configuration options specific to the login module implementation +- a list of configuration options specific to the login module implementation -By default, the location and name of `login.config` is specified on the Artemis command-line which is set by -`etc/artemis.profile` on linux and `etc\artemis.profile.cmd` on Windows. +By default, the location and name of `login.config` is specified on the Artemis +command-line which is set by `etc/artemis.profile` on linux and +`etc\artemis.profile.cmd` on Windows. #### Dual Authentication -The JAAS Security Manager also supports another configuration parameter - `certificate-domain`. This is useful when you -want to authenticate clients connecting with SSL connections based on their SSL certificates (e.g. using the `CertificateLoginModule` -discussed below) but you still want to authenticate clients connecting with non-SSL connections with, e.g., username and -password. Here's an example of what would go in `bootstrap.xml`: +The JAAS Security Manager also supports another configuration parameter - +`certificate-domain`. This is useful when you want to authenticate clients +connecting with SSL connections based on their SSL certificates (e.g. using the +`CertificateLoginModule` discussed below) but you still want to authenticate +clients connecting with non-SSL connections with, e.g., username and password. +Here's an example of what would go in `bootstrap.xml`: - +```xml + +``` And here's the corresponding `login.config`: - PropertiesLogin { - org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule required - debug=false - org.apache.activemq.jaas.properties.user="artemis-users.properties" - org.apache.activemq.jaas.properties.role="artemis-roles.properties"; - }; - - CertLogin { - org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule required - debug=true - org.apache.activemq.jaas.textfiledn.user="cert-users.properties" - org.apache.activemq.jaas.textfiledn.role="cert-roles.properties"; - }; +``` +PropertiesLogin { + org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule required + debug=false + org.apache.activemq.jaas.properties.user="artemis-users.properties" + org.apache.activemq.jaas.properties.role="artemis-roles.properties"; +}; + +CertLogin { + org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule required + debug=true + org.apache.activemq.jaas.textfiledn.user="cert-users.properties" + org.apache.activemq.jaas.textfiledn.role="cert-roles.properties"; +}; +``` -When the broker is configured this way then any client connecting with SSL and a client certificate will be authenticated -using `CertLogin` and any client connecting without SSL will be authenticated using `PropertiesLogin`. +When the broker is configured this way then any client connecting with SSL and +a client certificate will be authenticated using `CertLogin` and any client +connecting without SSL will be authenticated using `PropertiesLogin`. ### JAAS Login Modules #### GuestLoginModule -Allows users without credentials (and, depending on how it is configured, possibly also users with invalid credentials) -to access the broker. Normally, the guest login module is chained with another login module, such as a properties login -module. It is implemented by `org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule`. -- `org.apache.activemq.jaas.guest.user` - the user name to assign; default is "guest" +Allows users without credentials (and, depending on how it is configured, +possibly also users with invalid credentials) to access the broker. Normally, +the guest login module is chained with another login module, such as a +properties login module. It is implemented by +`org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule`. -- `org.apache.activemq.jaas.guest.role` - the role name to assign; default is "guests" +- `org.apache.activemq.jaas.guest.user` - the user name to assign; default is "guest" -- `credentialsInvalidate` - boolean flag; if `true`, reject login requests that include a password (i.e. guest login -succeeds only when the user does not provide a password); default is `false` +- `org.apache.activemq.jaas.guest.role` - the role name to assign; default is "guests" -- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it -should be set to `false`, or omitted; default is `false` +- `credentialsInvalidate` - boolean flag; if `true`, reject login requests that + include a password (i.e. guest login succeeds only when the user does not + provide a password); default is `false` + +- `debug` - boolean flag; if `true`, enable debugging; this is used only for + testing or debugging; normally, it should be set to `false`, or omitted; + default is `false` There are two basic use cases for the guest login module, as follows: -- Guests with no credentials or invalid credentials. +- Guests with no credentials or invalid credentials. -- Guests with no credentials only. +- Guests with no credentials only. -The following snippet shows how to configure a JAAS login entry for the use case where users with no credentials or -invalid credentials are logged in as guests. In this example, the guest login module is used in combination with the +The following snippet shows how to configure a JAAS login entry for the use +case where users with no credentials or invalid credentials are logged in as +guests. In this example, the guest login module is used in combination with the properties login module. - activemq-domain { - org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule sufficient - debug=true - org.apache.activemq.jaas.properties.user="artemis-users.properties" - org.apache.activemq.jaas.properties.role="artemis-roles.properties"; - - org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule sufficient - debug=true - org.apache.activemq.jaas.guest.user="anyone" - org.apache.activemq.jaas.guest.role="restricted"; - }; +``` +activemq-domain { + org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule sufficient + debug=true + org.apache.activemq.jaas.properties.user="artemis-users.properties" + org.apache.activemq.jaas.properties.role="artemis-roles.properties"; + + org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule sufficient + debug=true + org.apache.activemq.jaas.guest.user="anyone" + org.apache.activemq.jaas.guest.role="restricted"; +}; +``` Depending on the user login data, authentication proceeds as follows: -- User logs in with a valid password — the properties login module successfully authenticates the user and returns - immediately. The guest login module is not invoked. - -- User logs in with an invalid password — the properties login module fails to authenticate the user, and authentication - proceeds to the guest login module. The guest login module successfully authenticates the user and returns the guest principal. +- User logs in with a valid password — the properties login module successfully + authenticates the user and returns immediately. The guest login module is not + invoked. -- User logs in with a blank password — the properties login module fails to authenticate the user, and authentication - proceeds to the guest login module. The guest login module successfully authenticates the user and returns the guest principal. +- User logs in with an invalid password — the properties login module fails to + authenticate the user, and authentication proceeds to the guest login module. + The guest login module successfully authenticates the user and returns the + guest principal. -The following snipped shows how to configure a JAAS login entry for the use case where only those users with no -credentials are logged in as guests. To support this use case, you must set the credentialsInvalidate option to true in -the configuration of the guest login module. You should also note that, compared with the preceding example, the order -of the login modules is reversed and the flag attached to the properties login module is changed to requisite. +- User logs in with a blank password — the properties login module fails to + authenticate the user, and authentication proceeds to the guest login module. + The guest login module successfully authenticates the user and returns the + guest principal. - activemq-guest-when-no-creds-only-domain { - org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule sufficient - debug=true - credentialsInvalidate=true - org.apache.activemq.jaas.guest.user="guest" - org.apache.activemq.jaas.guest.role="guests"; +The following snipped shows how to configure a JAAS login entry for the use +case where only those users with no credentials are logged in as guests. To +support this use case, you must set the credentialsInvalidate option to true in +the configuration of the guest login module. You should also note that, +compared with the preceding example, the order of the login modules is reversed +and the flag attached to the properties login module is changed to requisite. - org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule requisite - debug=true - org.apache.activemq.jaas.properties.user="artemis-users.properties" - org.apache.activemq.jaas.properties.role="artemis-roles.properties"; - }; +``` +activemq-guest-when-no-creds-only-domain { + org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule sufficient + debug=true + credentialsInvalidate=true + org.apache.activemq.jaas.guest.user="guest" + org.apache.activemq.jaas.guest.role="guests"; + + org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule requisite + debug=true + org.apache.activemq.jaas.properties.user="artemis-users.properties" + org.apache.activemq.jaas.properties.role="artemis-roles.properties"; +}; +``` Depending on the user login data, authentication proceeds as follows: -- User logs in with a valid password — the guest login module fails to authenticate the user (because the user has - presented a password while the credentialsInvalidate option is enabled) and authentication proceeds to the properties - login module. The properties login module successfully authenticates the user and returns. +- User logs in with a valid password — the guest login module fails to + authenticate the user (because the user has presented a password while the + credentialsInvalidate option is enabled) and authentication proceeds to the + properties login module. The properties login module successfully authenticates + the user and returns. -- User logs in with an invalid password — the guest login module fails to authenticate the user and authentication proceeds - to the properties login module. The properties login module also fails to authenticate the user. The nett result is - authentication failure. +- User logs in with an invalid password — the guest login module fails to + authenticate the user and authentication proceeds to the properties login + module. The properties login module also fails to authenticate the user. The + net result is authentication failure. -- User logs in with a blank password — the guest login module successfully authenticates the user and returns immediately. - The properties login module is not invoked. +- User logs in with a blank password — the guest login module successfully + authenticates the user and returns immediately. The properties login module + is not invoked. #### PropertiesLoginModule -The JAAS properties login module provides a simple store of authentication data, where the relevant user data is stored -in a pair of flat files. This is convenient for demonstrations and testing, but for an enterprise system, the integration -with LDAP is preferable. It is implemented by `org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule`. - -- `org.apache.activemq.jaas.properties.user` - the path to the file which contains user and password properties - -- `org.apache.activemq.jaas.properties.role` - the path to the file which contains user and role properties - -- `reload` - boolean flag; whether or not to reload the properties files when a modification occurs; default is `false` - -- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it -should be set to `false`, or omitted; default is `false` - -In the context of the properties login module, the `artemis-users.properties` file consists of a list of properties of the -form, `UserName=Password`. For example, to define the users `system`, `user`, and `guest`, you could create a file like -the following: - - system=manager - user=password - guest=password - -Passwords in `artemis-users.properties` can be hashed. Such passwords should follow the syntax `ENC()`. Hashed -passwords can easily be added to `artemis-users.properties` using the `user` CLI command, e.g.: +The JAAS properties login module provides a simple store of authentication +data, where the relevant user data is stored in a pair of flat files. This is +convenient for demonstrations and testing, but for an enterprise system, the +integration with LDAP is preferable. It is implemented by +`org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule`. + +- `org.apache.activemq.jaas.properties.user` - the path to the file which + contains user and password properties + +- `org.apache.activemq.jaas.properties.role` - the path to the file which + contains user and role properties + +- `reload` - boolean flag; whether or not to reload the properties files when a + modification occurs; default is `false` + +- `debug` - boolean flag; if `true`, enable debugging; this is used only for + testing or debugging; normally, it should be set to `false`, or omitted; + default is `false` + +In the context of the properties login module, the `artemis-users.properties` +file consists of a list of properties of the form, `UserName=Password`. For +example, to define the users `system`, `user`, and `guest`, you could create a +file like the following: + +```properties +system=manager +user=password +guest=password +``` +Passwords in `artemis-users.properties` can be hashed. Such passwords should +follow the syntax `ENC()`. Hashed passwords can easily be added to +`artemis-users.properties` using the `user` CLI command, e.g.: ```sh - ./artemis user add --username guest --password guest --role admin +./artemis user add --username guest --password guest --role admin ``` -This will use the default `org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` to perform a "one-way" hash of -the password and alter both the `artemis-users.properties` and `artemis-roles.properties` files with the specified values. +This will use the default +`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` to perform a +"one-way" hash of the password and alter both the `artemis-users.properties` +and `artemis-roles.properties` files with the specified values. -The `artemis-roles.properties` file consists of a list of properties of the form, `Role=UserList`, where UserList is a -comma-separated list of users. For example, to define the roles `admins`, `users`, and `guests`, you could create a file -like the following: +The `artemis-roles.properties` file consists of a list of properties of the +form, `Role=UserList`, where UserList is a comma-separated list of users. For +example, to define the roles `admins`, `users`, and `guests`, you could create +a file like the following: - admins=system - users=system,user - guests=guest +```properties +admins=system +users=system,user +guests=guest +``` #### LDAPLoginModule -The LDAP login module enables you to perform authentication and authorization by checking the incoming credentials against -user data stored in a central X.500 directory server. For systems that already have an X.500 directory server in place, -this means that you can rapidly integrate ActiveMQ Artemis with the existing security database and user accounts can be -managed using the X.500 system. It is implemented by `org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule`. - -- `initialContextFactory` - must always be set to `com.sun.jndi.ldap.LdapCtxFactory` - -- `connectionURL` - specify the location of the directory server using an ldap URL, ldap://Host:Port. You can - optionally qualify this URL, by adding a forward slash, `/`, followed by the DN of a particular node in the directory - tree. For example, ldap://ldapserver:10389/ou=system. -- `authentication` - specifies the authentication method used when binding to the LDAP server. Can take either of - the values, `simple` (username and password), `GSSAPI` (Kerberos SASL) or `none` (anonymous). - -- `connectionUsername` - the DN of the user that opens the connection to the directory server. For example, - `uid=admin,ou=system`. Directory servers generally require clients to present username/password credentials in order - to open a connection. - -- `connectionPassword` - the password that matches the DN from `connectionUsername`. In the directory server, - in the DIT, the password is normally stored as a `userPassword` attribute in the corresponding directory entry. - -- `saslLoginConfigScope` - the scope in JAAS configuration (login.config) to use to obtain Kerberos initiator credentials - when the `authentication` method is SASL `GSSAPI`. The default value is `broker-sasl-gssapi`. - -- `connectionProtocol` - currently, the only supported value is a blank string. In future, this option will allow - you to select the Secure Socket Layer (SSL) for the connection to the directory server. This option must be set - explicitly to an empty string, because it has no default value. - -- `userBase` - selects a particular subtree of the DIT to search for user entries. The subtree is specified by a - DN, which specifes the base node of the subtree. For example, by setting this option to `ou=User,ou=ActiveMQ,ou=system`, - the search for user entries is restricted to the subtree beneath the `ou=User,ou=ActiveMQ,ou=system` node. - -- `userSearchMatching` - specifies an LDAP search filter, which is applied to the subtree selected by `userBase`. - Before passing to the LDAP search operation, the string value you provide here is subjected to string substitution, - as implemented by the `java.text.MessageFormat` class. Essentially, this means that the special string, `{0}`, is - substituted by the username, as extracted from the incoming client credentials. - - After substitution, the string is interpreted as an LDAP search filter, where the LDAP search filter syntax is - defined by the IETF standard, RFC 2254. A short introduction to the search filter syntax is available from Oracle's - JNDI tutorial, [Search Filters](https://docs.oracle.com/javase/jndi/tutorial/basics/directory/filter.html). - - For example, if this option is set to `(uid={0})` and the received username is `jdoe`, the search filter becomes - `(uid=jdoe)` after string substitution. If the resulting search filter is applied to the subtree selected by the - user base, `ou=User,ou=ActiveMQ,ou=system`, it would match the entry, `uid=jdoe,ou=User,ou=ActiveMQ,ou=system` - (and possibly more deeply nested entries, depending on the specified search depth—see the `userSearchSubtree` option). - -- `userSearchSubtree` - specify the search depth for user entries, relative to the node specified by `userBase`. - This option is a boolean. `false` indicates it will try to match one of the child entries of the `userBase` node - (maps to `javax.naming.directory.SearchControls.ONELEVEL_SCOPE`). `true` indicates it will try to match any entry - belonging to the subtree of the `userBase` node (maps to `javax.naming.directory.SearchControls.SUBTREE_SCOPE`). - -- `userRoleName` - specifies the name of the multi-valued attribute of the user entry that contains a list of - role names for the user (where the role names are interpreted as group names by the broker's authorization plug-in). - If you omit this option, no role names are extracted from the user entry. - -- `roleBase` - if you want to store role data directly in the directory server, you can use a combination of role - options (`roleBase`, `roleSearchMatching`, `roleSearchSubtree`, and `roleName`) as an alternative to (or in addition - to) specifying the `userRoleName` option. This option selects a particular subtree of the DIT to search for role/group - entries. The subtree is specified by a DN, which specifes the base node of the subtree. For example, by setting this - option to `ou=Group,ou=ActiveMQ,ou=system`, the search for role/group entries is restricted to the subtree beneath - the `ou=Group,ou=ActiveMQ,ou=system` node. - -- `roleName` - specifies the attribute type of the role entry that contains the name of the role/group (e.g. C, O, - OU, etc.). If you omit this option the full DN of the role is used. - -- `roleSearchMatching` - specifies an LDAP search filter, which is applied to the subtree selected by `roleBase`. - This works in a similar manner to the `userSearchMatching` option, except that it supports two substitution strings, - as follows: - - - `{0}` - substitutes the full DN of the matched user entry (that is, the result of the user search). For - example, for the user, `jdoe`, the substituted string could be `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`. - - - `{1}` - substitutes the received username. For example, `jdoe`. - - For example, if this option is set to `(member=uid={1})` and the received username is `jdoe`, the search filter - becomes `(member=uid=jdoe)` after string substitution (assuming ApacheDS search filter syntax). If the resulting - search filter is applied to the subtree selected by the role base, `ou=Group,ou=ActiveMQ,ou=system`, it matches all - role entries that have a `member` attribute equal to `uid=jdoe` (the value of a `member` attribute is a DN). - - This option must always be set to enable role searching because it has no default value. Leaving it unset disables - role searching and the role information must come from `userRoleName`. - - If you use OpenLDAP, the syntax of the search filter is `(member:=uid=jdoe)`. - -- `roleSearchSubtree` - specify the search depth for role entries, relative to the node specified by `roleBase`. - This option can take boolean values, as follows: - - - `false` (default) - try to match one of the child entries of the roleBase node (maps to - `javax.naming.directory.SearchControls.ONELEVEL_SCOPE`). - - - `true` — try to match any entry belonging to the subtree of the roleBase node (maps to - `javax.naming.directory.SearchControls.SUBTREE_SCOPE`). - -- `authenticateUser` - boolean flag to disable authentication. Useful as an optimisation when this module is used just for - role mapping of a Subject's existing authenticated principals; default is `false`. - -- `referral` - specify how to handle referrals; valid values: `ignore`, `follow`, `throw`; default is `ignore`. - -- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it - should be set to `false`, or omitted; default is `false` - -Add user entries under the node specified by the `userBase` option. When creating a new user entry in the directory, -choose an object class that supports the `userPassword` attribute (for example, the `person` or `inetOrgPerson` object -classes are typically suitable). After creating the user entry, add the `userPassword` attribute, to hold the user's -password. - -If you want to store role data in dedicated role entries (where each node represents a particular role), create a role -entry as follows. Create a new child of the `roleBase` node, where the `objectClass` of the child is `groupOfNames`. Set -the `cn` (or whatever attribute type is specified by `roleName`) of the new child node equal to the name of the -role/group. Define a `member` attribute for each member of the role/group, setting the `member` value to the DN of the -corresponding user (where the DN is specified either fully, `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`, or partially, -`uid=jdoe`). - -If you want to add roles to user entries, you would need to customize the directory schema, by adding a suitable -attribute type to the user entry's object class. The chosen attribute type must be capable of handling multiple values. +The LDAP login module enables you to perform authentication and authorization +by checking the incoming credentials against user data stored in a central +X.500 directory server. For systems that already have an X.500 directory server +in place, this means that you can rapidly integrate ActiveMQ Artemis with the +existing security database and user accounts can be managed using the X.500 +system. It is implemented by +`org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule`. + +- `initialContextFactory` - must always be set to + `com.sun.jndi.ldap.LdapCtxFactory` + +- `connectionURL` - specify the location of the directory server using an ldap + URL, ldap://Host:Port. You can optionally qualify this URL, by adding a + forward slash, `/`, followed by the DN of a particular node in the directory + tree. For example, ldap://ldapserver:10389/ou=system. + +- `authentication` - specifies the authentication method used when binding to + the LDAP server. Can take either of the values, `simple` (username and + password), `GSSAPI` (Kerberos SASL) or `none` (anonymous). + +- `connectionUsername` - the DN of the user that opens the connection to the + directory server. For example, `uid=admin,ou=system`. Directory servers + generally require clients to present username/password credentials in order to + open a connection. + +- `connectionPassword` - the password that matches the DN from + `connectionUsername`. In the directory server, in the DIT, the password is + normally stored as a `userPassword` attribute in the corresponding directory + entry. + +- `saslLoginConfigScope` - the scope in JAAS configuration (login.config) to + use to obtain Kerberos initiator credentials when the `authentication` method + is SASL `GSSAPI`. The default value is `broker-sasl-gssapi`. + +- `connectionProtocol` - currently, the only supported value is a blank string. + In future, this option will allow you to select the Secure Socket Layer (SSL) + for the connection to the directory server. This option must be set explicitly + to an empty string, because it has no default value. + +- `userBase` - selects a particular subtree of the DIT to search for user + entries. The subtree is specified by a DN, which specifes the base node of + the subtree. For example, by setting this option to + `ou=User,ou=ActiveMQ,ou=system`, the search for user entries is restricted to + the subtree beneath the `ou=User,ou=ActiveMQ,ou=system` node. + +- `userSearchMatching` - specifies an LDAP search filter, which is applied to + the subtree selected by `userBase`. Before passing to the LDAP search + operation, the string value you provide here is subjected to string + substitution, as implemented by the `java.text.MessageFormat` class. + Essentially, this means that the special string, `{0}`, is substituted by the + username, as extracted from the incoming client credentials. + + After substitution, the string is interpreted as an LDAP search filter, + where the LDAP search filter syntax is defined by the IETF standard, RFC 2254. + A short introduction to the search filter syntax is available from Oracle's + JNDI tutorial, [Search + Filters](https://docs.oracle.com/javase/jndi/tutorial/basics/directory/filter.html). + + For example, if this option is set to `(uid={0})` and the received username + is `jdoe`, the search filter becomes `(uid=jdoe)` after string substitution. If + the resulting search filter is applied to the subtree selected by the user + base, `ou=User,ou=ActiveMQ,ou=system`, it would match the entry, + `uid=jdoe,ou=User,ou=ActiveMQ,ou=system` (and possibly more deeply nested + entries, depending on the specified search depth—see the `userSearchSubtree` + option). + +- `userSearchSubtree` - specify the search depth for user entries, relative to + the node specified by `userBase`. This option is a boolean. `false` + indicates it will try to match one of the child entries of the `userBase` node + (maps to `javax.naming.directory.SearchControls.ONELEVEL_SCOPE`). `true` + indicates it will try to match any entry belonging to the subtree of the + `userBase` node (maps to + `javax.naming.directory.SearchControls.SUBTREE_SCOPE`). + +- `userRoleName` - specifies the name of the multi-valued attribute of the user + entry that contains a list of role names for the user (where the role names + are interpreted as group names by the broker's authorization plug-in). If you + omit this option, no role names are extracted from the user entry. + +- `roleBase` - if you want to store role data directly in the directory server, + you can use a combination of role options (`roleBase`, `roleSearchMatching`, + `roleSearchSubtree`, and `roleName`) as an alternative to (or in addition to) + specifying the `userRoleName` option. This option selects a particular subtree + of the DIT to search for role/group entries. The subtree is specified by a DN, + which specifes the base node of the subtree. For example, by setting this + option to `ou=Group,ou=ActiveMQ,ou=system`, the search for role/group entries + is restricted to the subtree beneath the `ou=Group,ou=ActiveMQ,ou=system` node. + +- `roleName` - specifies the attribute type of the role entry that contains the + name of the role/group (e.g. C, O, OU, etc.). If you omit this option the + full DN of the role is used. + +- `roleSearchMatching` - specifies an LDAP search filter, which is applied to + the subtree selected by `roleBase`. This works in a similar manner to the + `userSearchMatching` option, except that it supports two substitution strings, + as follows: + + - `{0}` - substitutes the full DN of the matched user entry (that is, the + result of the user search). For example, for the user, `jdoe`, the + substituted string could be `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`. + + - `{1}` - substitutes the received username. For example, `jdoe`. + + For example, if this option is set to `(member=uid={1})` and the received + username is `jdoe`, the search filter becomes `(member=uid=jdoe)` after string + substitution (assuming ApacheDS search filter syntax). If the resulting search + filter is applied to the subtree selected by the role base, + `ou=Group,ou=ActiveMQ,ou=system`, it matches all role entries that have a + `member` attribute equal to `uid=jdoe` (the value of a `member` attribute is a + DN). + + This option must always be set to enable role searching because it has no + default value. Leaving it unset disables role searching and the role + information must come from `userRoleName`. + + If you use OpenLDAP, the syntax of the search filter is + `(member:=uid=jdoe)`. + +- `roleSearchSubtree` - specify the search depth for role entries, relative to + the node specified by `roleBase`. This option can take boolean values, as + follows: + + - `false` (default) - try to match one of the child entries of the roleBase + node (maps to `javax.naming.directory.SearchControls.ONELEVEL_SCOPE`). + + - `true` — try to match any entry belonging to the subtree of the roleBase + node (maps to `javax.naming.directory.SearchControls.SUBTREE_SCOPE`). + +- `authenticateUser` - boolean flag to disable authentication. Useful as an + optimisation when this module is used just for role mapping of a Subject's + existing authenticated principals; default is `false`. + +- `referral` - specify how to handle referrals; valid values: `ignore`, + `follow`, `throw`; default is `ignore`. + +- `debug` - boolean flag; if `true`, enable debugging; this is used only for + testing or debugging; normally, it should be set to `false`, or omitted; + default is `false` + +Add user entries under the node specified by the `userBase` option. When +creating a new user entry in the directory, choose an object class that +supports the `userPassword` attribute (for example, the `person` or +`inetOrgPerson` object classes are typically suitable). After creating the user +entry, add the `userPassword` attribute, to hold the user's password. + +If you want to store role data in dedicated role entries (where each node +represents a particular role), create a role entry as follows. Create a new +child of the `roleBase` node, where the `objectClass` of the child is +`groupOfNames`. Set the `cn` (or whatever attribute type is specified by +`roleName`) of the new child node equal to the name of the role/group. Define a +`member` attribute for each member of the role/group, setting the `member` +value to the DN of the corresponding user (where the DN is specified either +fully, `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`, or partially, `uid=jdoe`). + +If you want to add roles to user entries, you would need to customize the +directory schema, by adding a suitable attribute type to the user entry's +object class. The chosen attribute type must be capable of handling multiple +values. #### CertificateLoginModule -The JAAS certificate authentication login module must be used in combination with SSL and the clients must be configured -with their own certificate. In this scenario, authentication is actually performed during the SSL/TLS handshake, not -directly by the JAAS certificate authentication plug-in. The role of the plug-in is as follows: +The JAAS certificate authentication login module must be used in combination +with SSL and the clients must be configured with their own certificate. In this +scenario, authentication is actually performed during the SSL/TLS handshake, +not directly by the JAAS certificate authentication plug-in. The role of the +plug-in is as follows: -- To further constrain the set of acceptable users, because only the user DNs explicitly listed in the relevant - properties file are eligible to be authenticated. +- To further constrain the set of acceptable users, because only the user DNs + explicitly listed in the relevant properties file are eligible to be + authenticated. -- To associate a list of groups with the received user identity, facilitating integration with the authorization feature. +- To associate a list of groups with the received user identity, facilitating + integration with the authorization feature. -- To require the presence of an incoming certificate (by default, the SSL/TLS layer is configured to treat the - presence of a client certificate as optional). +- To require the presence of an incoming certificate (by default, the SSL/TLS + layer is configured to treat the presence of a client certificate as + optional). -The JAAS certificate login module stores a collection of certificate DNs in a pair of flat files. The files associate a -username and a list of group IDs with each DN. +The JAAS certificate login module stores a collection of certificate DNs in a +pair of flat files. The files associate a username and a list of group IDs with +each DN. The certificate login module is implemented by the following class: - org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule +```java +org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule +``` -The following `CertLogin` login entry shows how to configure certificate login module in the login.config file: +The following `CertLogin` login entry shows how to configure certificate login +module in the login.config file: - CertLogin { - org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule - debug=true - org.apache.activemq.jaas.textfiledn.user="users.properties" - org.apache.activemq.jaas.textfiledn.role="roles.properties"; - }; +``` +CertLogin { + org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule + debug=true + org.apache.activemq.jaas.textfiledn.user="users.properties" + org.apache.activemq.jaas.textfiledn.role="roles.properties"; +}; +``` -In the preceding example, the JAAS realm is configured to use a single `org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule` +In the preceding example, the JAAS realm is configured to use a single +`org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule` login module. The options supported by this login module are as follows: - `debug` - boolean flag; if true, enable debugging; this is used only for testing or debugging; normally, @@ -632,116 +781,183 @@ compares it with the subject DNs in the `users.properties` file by testing for s be careful to ensure that the subject DNs appearing in the `users.properties` file are an exact match for the subject DNs extracted from the user certificates. -Note: Technically, there is some residual ambiguity in the DN string format. For example, the `domainComponent` attribute -could be represented in a string either as the string, `DC`, or as the OID, `0.9.2342.19200300.100.1.25`. Normally, you do -not need to worry about this ambiguity. But it could potentially be a problem, if you changed the underlying -implementation of the Java security layer. - -The easiest way to obtain the subject DNs from the user certificates is by invoking the `keytool` utility to print the -certificate contents. To print the contents of a certificate in a keystore, perform the following steps: +- `org.apache.activemq.jaas.textfiledn.user` - specifies the location of the + user properties file (relative to the directory containing the login + configuration file). -1. Export the certificate from the keystore file into a temporary file. For example, to export the certificate with - alias `broker-localhost` from the `broker.ks` keystore file, enter the following command: +- `org.apache.activemq.jaas.textfiledn.role` - specifies the location of the + role properties file (relative to the directory containing the login + configuration file). - keytool -export -file broker.export -alias broker-localhost -keystore broker.ks -storepass password +- `reload` - boolean flag; whether or not to reload the properties files when a + modification occurs; default is `false` - After running this command, the exported certificate is in the file, `broker.export`. +In the context of the certificate login module, the `users.properties` file +consists of a list of properties of the form, `UserName=StringifiedSubjectDN`. +For example, to define the users, system, user, and guest, you could create a +file like the following: -1. Print out the contents of the exported certificate. For example, to print out the contents of `broker.export`, - enter the following command: - - keytool -printcert -file broker.export - - Which should produce output similar to that shown here: - - Owner: CN=localhost, OU=broker, O=Unknown, L=Unknown, ST=Unknown, C=Unknown - Issuer: CN=localhost, OU=broker, O=Unknown, L=Unknown, ST=Unknown, C=Unknown - Serial number: 4537c82e - Valid from: Thu Oct 19 19:47:10 BST 2006 until: Wed Jan 17 18:47:10 GMT 2007 - Certificate fingerprints: - MD5: 3F:6C:0C:89:A8:80:29:CC:F5:2D:DA:5C:D7:3F:AB:37 - SHA1: F0:79:0D:04:38:5A:46:CE:86:E1:8A:20:1F:7B:AB:3A:46:E4:34:5C - - The string following `Owner:` gives the subject DN. The format used to enter the subject DN depends on your - platform. The `Owner:` string above could be represented as either `CN=localhost,\ OU=broker,\ O=Unknown,\ L=Unknown,\ ST=Unknown,\ C=Unknown` - or `CN=localhost,OU=broker,O=Unknown,L=Unknown,ST=Unknown,C=Unknown`. - -The `roles.properties` file consists of a list of properties of the form, `Role=UserList`, where `UserList` is a -comma-separated list of users. For example, to define the roles `admins`, `users`, and `guests`, you could create a file -like the following: +```properties +system=CN=system,O=Progress,C=US +user=CN=humble user,O=Progress,C=US +guest=CN=anon,O=Progress,C=DE +``` - admins=system - users=system,user - guests=guest +Each username is mapped to a subject DN, encoded as a string (where the string +encoding is specified by RFC 2253). For example, the system username is mapped +to the `CN=system,O=Progress,C=US` subject DN. When performing authentication, +the plug-in extracts the subject DN from the received certificate, converts it +to the standard string format, and compares it with the subject DNs in the +`users.properties` file by testing for string equality. Consequently, you must +be careful to ensure that the subject DNs appearing in the `users.properties` +file are an exact match for the subject DNs extracted from the user +certificates. + +**Note:** Technically, there is some residual ambiguity in the DN string format. +For example, the `domainComponent` attribute could be represented in a string +either as the string, `DC`, or as the OID, `0.9.2342.19200300.100.1.25`. +Normally, you do not need to worry about this ambiguity. But it could +potentially be a problem, if you changed the underlying implementation of the +Java security layer. + +The easiest way to obtain the subject DNs from the user certificates is by +invoking the `keytool` utility to print the certificate contents. To print the +contents of a certificate in a keystore, perform the following steps: + +1. Export the certificate from the keystore file into a temporary file. For + example, to export the certificate with alias `broker-localhost` from the +`broker.ks` keystore file, enter the following command: + + ```sh + keytool -export -file broker.export -alias broker-localhost -keystore broker.ks -storepass password + ``` + + After running this command, the exported certificate is in the file, + `broker.export`. + +1. Print out the contents of the exported certificate. For example, to print + out the contents of `broker.export`, enter the following command: + + ```sh + keytool -printcert -file broker.export + ``` + + Which should produce output similar to that shown here: + + ``` + Owner: CN=localhost, OU=broker, O=Unknown, L=Unknown, ST=Unknown, C=Unknown + Issuer: CN=localhost, OU=broker, O=Unknown, L=Unknown, ST=Unknown, C=Unknown + Serial number: 4537c82e + Valid from: Thu Oct 19 19:47:10 BST 2006 until: Wed Jan 17 18:47:10 GMT 2007 + Certificate fingerprints: + MD5: 3F:6C:0C:89:A8:80:29:CC:F5:2D:DA:5C:D7:3F:AB:37 + SHA1: F0:79:0D:04:38:5A:46:CE:86:E1:8A:20:1F:7B:AB:3A:46:E4:34:5C + ``` + + The string following `Owner:` gives the subject DN. The format used to enter + the subject DN depends on your platform. The `Owner:` string above could be + represented as either `CN=localhost,\ OU=broker,\ O=Unknown,\ L=Unknown,\ + ST=Unknown,\ C=Unknown` or + `CN=localhost,OU=broker,O=Unknown,L=Unknown,ST=Unknown,C=Unknown`. + +The `roles.properties` file consists of a list of properties of the form, +`Role=UserList`, where `UserList` is a comma-separated list of users. For +example, to define the roles `admins`, `users`, and `guests`, you could create +a file like the following: + +```properties +admins=system +users=system,user +guests=guest +``` -The simplest way to make the login configuration available to JAAS is to add the directory containing the file, -`login.config`, to your CLASSPATH. +The simplest way to make the login configuration available to JAAS is to add +the directory containing the file, `login.config`, to your CLASSPATH. ### Kerberos Authentication -You must have the Kerberos infrastructure set up in your deployment environment before the server can accept Kerberos credentials. -The server can acquire its Kerberos acceptor credentials by using JAAS and a Kerberos login module. The JDK provides the +You must have the Kerberos infrastructure set up in your deployment environment +before the server can accept Kerberos credentials. The server can acquire its +Kerberos acceptor credentials by using JAAS and a Kerberos login module. The +JDK provides the [Krb5LoginModule](https://docs.oracle.com/javase/8/docs/jre/api/security/jaas/spec/com/sun/security/auth/module/Krb5LoginModule.html) -which executes the necessary Kerberos protocol steps to authenticate and obtain Kerberos credentials. +which executes the necessary Kerberos protocol steps to authenticate and obtain +Kerberos credentials. #### GSSAPI SASL Mechanism -Using SASL over [AMQP](using-AMQP.md), Kerberos authentication is supported using the `GSSAPI` SASL mechanism. -With SASL doing Kerberos authentication, TLS can be used to provide integrity and confidentially to the communications +Using SASL over [AMQP](using-AMQP.md), Kerberos authentication is supported +using the `GSSAPI` SASL mechanism. With SASL doing Kerberos authentication, +TLS can be used to provide integrity and confidentially to the communications channel in the normal way. -The `GSSAPI` SASL mechanism must be enabled on the AMQP acceptor in `broker.xml` by adding it to the `saslMechanisms` list url parameter: + +The `GSSAPI` SASL mechanism must be enabled on the AMQP acceptor in +`broker.xml` by adding it to the `saslMechanisms` list url parameter: `saslMechanisms="GSSAPI<,PLAIN, etc>`. - tcp://0.0.0.0:5672?protocols=AMQP;saslMechanisms=GSSAPI +```xml +tcp://0.0.0.0:5672?protocols=AMQP;saslMechanisms=GSSAPI +``` -The GSSAPI mechanism implementation on the server will use a JAAS configuration scope named `amqp-sasl-gssapi` to -obtain it's Kerberos acceptor credentials. An alternative configuration scope can be specified on the AMQP acceptor -using the url parameter: `saslLoginConfigScope=`. +The GSSAPI mechanism implementation on the server will use a JAAS configuration +scope named `amqp-sasl-gssapi` to obtain it's Kerberos acceptor credentials. An +alternative configuration scope can be specified on the AMQP acceptor using the +url parameter: `saslLoginConfigScope=`. -An example configuration scope for `login.config` that will pick up a Kerberos keyTab for the Kerberos acceptor Principal -`amqp/localhost` is as follows: +An example configuration scope for `login.config` that will pick up a Kerberos +keyTab for the Kerberos acceptor Principal `amqp/localhost` is as follows: - amqp-sasl-gssapi { - com.sun.security.auth.module.Krb5LoginModule required - isInitiator=false - storeKey=true - useKeyTab=true - principal="amqp/localhost" - debug=true; - }; +``` +amqp-sasl-gssapi { + com.sun.security.auth.module.Krb5LoginModule required + isInitiator=false + storeKey=true + useKeyTab=true + principal="amqp/localhost" + debug=true; +}; +``` #### Role Mapping -On the server, the Kerberos authenticated Peer Principal can be added to the Subject's principal set as an Apache ActiveMQ Artemis UserPrincipal -using the Apache ActiveMQ Artemis `Krb5LoginModule` login module. The [PropertiesLoginModule](#propertiesloginmodule) or - [LDAPLoginModule](#ldaploginmodule) can then be used to map -the authenticated Kerberos Peer Principal to an Apache ActiveMQ Artemis [Role](#role-based-security-for-addresses). Note -that the Kerberos Peer Principal does not exist as an Apache ActiveMQ Artemis user, only as a role member. - - org.apache.activemq.artemis.spi.core.security.jaas.Krb5LoginModule required - ; - org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule optional - initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory - connectionURL="ldap://localhost:1024" - authentication=GSSAPI - saslLoginConfigScope=broker-sasl-gssapi - connectionProtocol=s - userBase="ou=users,dc=example,dc=com" - userSearchMatching="(krb5PrincipalName={0})" - userSearchSubtree=true - authenticateUser=false - roleBase="ou=system" - roleName=cn - roleSearchMatching="(member={0})" - roleSearchSubtree=false - ; +On the server, the Kerberos authenticated Peer Principal can be added to the +Subject's principal set as an Apache ActiveMQ Artemis UserPrincipal using the +Apache ActiveMQ Artemis `Krb5LoginModule` login module. The +[PropertiesLoginModule](#propertiesloginmodule) or +[LDAPLoginModule](#ldaploginmodule) can then be used to map the authenticated +Kerberos Peer Principal to an Apache ActiveMQ Artemis +[Role](#role-based-security-for-addresses). Note that the Kerberos Peer +Principal does not exist as an Apache ActiveMQ Artemis user, only as a role +member. + +``` +org.apache.activemq.artemis.spi.core.security.jaas.Krb5LoginModule required + ; +org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule optional + initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory + connectionURL="ldap://localhost:1024" + authentication=GSSAPI + saslLoginConfigScope=broker-sasl-gssapi + connectionProtocol=s + userBase="ou=users,dc=example,dc=com" + userSearchMatching="(krb5PrincipalName={0})" + userSearchSubtree=true + authenticateUser=false + roleBase="ou=system" + roleName=cn + roleSearchMatching="(member={0})" + roleSearchSubtree=false + ; +``` #### TLS Kerberos Cipher Suites -The legacy [rfc2712](https://www.ietf.org/rfc/rfc2712.txt) defines TLS Kerberos cipher suites that can be used by TLS to negotiate -Kerberos authentication. The cypher suites offered by rfc2712 are dated and insecure and rfc2712 has been superseded by -SASL GSSAPI. However, for clients that don't support SASL (core client), using TLS can provide Kerberos authentication -over an *unsecure* channel. +The legacy [rfc2712](https://www.ietf.org/rfc/rfc2712.txt) defines TLS Kerberos +cipher suites that can be used by TLS to negotiate Kerberos authentication. The +cypher suites offered by rfc2712 are dated and insecure and rfc2712 has been +superseded by SASL GSSAPI. However, for clients that don't support SASL (core +client), using TLS can provide Kerberos authentication over an *unsecure* +channel. ## SASL @@ -751,81 +967,95 @@ Note: EXTERNAL will only be chosen if a subject is available from the TLS client ## Changing the username/password for clustering -In order for cluster connections to work correctly, each node in the -cluster must make connections to the other nodes. The username/password -they use for this should always be changed from the installation default -to prevent a security risk. +In order for cluster connections to work correctly, each node in the cluster +must make connections to the other nodes. The username/password they use for +this should always be changed from the installation default to prevent a +security risk. Please see [Management](management.md) for instructions on how to do this. ## Securing the console -Artemis comes with a web console that allows user to browse Artemis documentation via an embedded server. By default the -web access is plain HTTP. It is configured in `bootstrap.xml`: +Artemis comes with a web console that allows user to browse Artemis +documentation via an embedded server. By default the web access is plain HTTP. +It is configured in `bootstrap.xml`: - - - +```xml + + + +``` -Alternatively you can edit the above configuration to enable secure access using HTTPS protocol. e.g.: +Alternatively you can edit the above configuration to enable secure access +using HTTPS protocol. e.g.: - - - +```xml + + + +``` -As shown in the example, to enable https the first thing to do is config the `bind` to be an `https` url. In addition, -You will have to configure a few extra properties desribed as below. +As shown in the example, to enable https the first thing to do is config the +`bind` to be an `https` url. In addition, You will have to configure a few +extra properties desribed as below. -- `keyStorePath` - The path of the key store file. +- `keyStorePath` - The path of the key store file. -- `keyStorePassword` - The key store's password. +- `keyStorePassword` - The key store's password. -- `clientAuth` - The boolean flag indicates whether or not client authentication is required. Default is `false`. +- `clientAuth` - The boolean flag indicates whether or not client + authentication is required. Default is `false`. -- `trustStorePath` - The path of the trust store file. This is needed only if `clientAuth` is `true`. +- `trustStorePath` - The path of the trust store file. This is needed only if + `clientAuth` is `true`. -- `trustStorePassword` - The trust store's password. +- `trustStorePassword` - The trust store's password. ## Controlling JMS ObjectMessage deserialization -Artemis provides a simple class filtering mechanism with which a user can specify which -packages are to be trusted and which are not. Objects whose classes are from trusted packages -can be deserialized without problem, whereas those from 'not trusted' packages will be denied -deserialization. - -Artemis keeps a `black list` to keep track of packages that are not trusted and a `white list` -for trusted packages. By default both lists are empty, meaning any serializable object is -allowed to be deserialized. If an object whose class matches one of the packages in black list, -it is not allowed to be deserialized. If it matches one in the white list -the object can be deserialized. If a package appears in both black list and white list, -the one in black list takes precedence. If a class neither matches with `black list` -nor with the `white list`, the class deserialization will be denied -unless the white list is empty (meaning the user doesn't specify the white list at all). +Artemis provides a simple class filtering mechanism with which a user can +specify which packages are to be trusted and which are not. Objects whose +classes are from trusted packages can be deserialized without problem, whereas +those from 'not trusted' packages will be denied deserialization. + +Artemis keeps a `black list` to keep track of packages that are not trusted and +a `white list` for trusted packages. By default both lists are empty, meaning +any serializable object is allowed to be deserialized. If an object whose class +matches one of the packages in black list, it is not allowed to be +deserialized. If it matches one in the white list the object can be +deserialized. If a package appears in both black list and white list, the one +in black list takes precedence. If a class neither matches with `black list` +nor with the `white list`, the class deserialization will be denied unless the +white list is empty (meaning the user doesn't specify the white list at all). A class is considered as a 'match' if -- its full name exactly matches one of the entries in the list. -- its package matches one of the entries in the list or is a sub-package of one of the entries. +- its full name exactly matches one of the entries in the list. +- its package matches one of the entries in the list or is a sub-package of one + of the entries. -For example, if a class full name is "org.apache.pkg1.Class1", some matching entries could be: +For example, if a class full name is "org.apache.pkg1.Class1", some matching +entries could be: -- `org.apache.pkg1.Class1` - exact match. -- `org.apache.pkg1` - exact package match. -- `org.apache` -- sub package match. +- `org.apache.pkg1.Class1` - exact match. +- `org.apache.pkg1` - exact package match. +- `org.apache` -- sub package match. A `*` means 'match-all' in a black or white list. ### Config via Connection Factories To specify the white and black lists one can use the URL parameters -`deserializationBlackList` and `deserializationWhiteList`. For example, -using JMS: +`deserializationBlackList` and `deserializationWhiteList`. For example, using +JMS: - ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://0?deserializationBlackList=org.apache.pkg1,org.some.pkg2"); +```java +ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://0?deserializationBlackList=org.apache.pkg1,org.some.pkg2"); +``` The above statement creates a factory that has a black list contains two forbidden packages, "org.apache.pkg1" and "org.some.pkg2", separated by a @@ -833,46 +1063,59 @@ comma. ### Config via system properties -There are two system properties available for specifying black list and white list: +There are two system properties available for specifying black list and white +list: -- `org.apache.activemq.artemis.jms.deserialization.whitelist` - comma separated list of entries for the white list. -- `org.apache.activemq.artemis.jms.deserialization.blacklist` - comma separated list of entries for the black list. +- `org.apache.activemq.artemis.jms.deserialization.whitelist` - comma separated + list of entries for the white list. +- `org.apache.activemq.artemis.jms.deserialization.blacklist` - comma separated + list of entries for the black list. -Once defined, all JMS object message deserialization in the VM is subject to checks against the two lists. However if you create a ConnectionFactory -and set a new set of black/white lists on it, the new values will override the system properties. +Once defined, all JMS object message deserialization in the VM is subject to +checks against the two lists. However if you create a ConnectionFactory and set +a new set of black/white lists on it, the new values will override the system +properties. ### Config for resource adapters -Message beans using a JMS resource adapter to receive messages can also control their object deserialization via properly configuring relevant -properties for their resource adapters. There are two properties that you can configure with connection factories in a resource adapter: +Message beans using a JMS resource adapter to receive messages can also control +their object deserialization via properly configuring relevant properties for +their resource adapters. There are two properties that you can configure with +connection factories in a resource adapter: -- `deserializationBlackList` - comma separated values for black list -- `deserializationWhiteList` - comma separated values for white list +- `deserializationBlackList` - comma separated values for black list +- `deserializationWhiteList` - comma separated values for white list -These properties, once specified, are eventually set on the corresponding internal factories. +These properties, once specified, are eventually set on the corresponding +internal factories. ### Config for REST interface -Apache Artemis REST interface ([Rest](rest.md)) allows interactions between jms client and rest clients. -It uses JMS ObjectMessage to wrap the actual user data between the 2 types of clients and deserialization -is needed during this process. If you want to control the deserialization for REST, you need to set the -black/white lists for it separately as Apache Artemis REST Interface is deployed as a web application. -You need to put the black/white lists in its web.xml, as context parameters, as follows - - - - org.apache.activemq.artemis.jms.deserialization.whitelist - some.allowed.class - - - org.apache.activemq.artemis.jms.deserialization.blacklist - some.forbidden.class - - ... - +Apache Artemis REST interface ([Rest](rest.md)) allows interactions between jms +client and rest clients. It uses JMS ObjectMessage to wrap the actual user +data between the 2 types of clients and deserialization is needed during this +process. If you want to control the deserialization for REST, you need to set +the black/white lists for it separately as Apache Artemis REST Interface is +deployed as a web application. You need to put the black/white lists in its +web.xml, as context parameters, as follows + +```xml + + + org.apache.activemq.artemis.jms.deserialization.whitelist + some.allowed.class + + + org.apache.activemq.artemis.jms.deserialization.blacklist + some.forbidden.class + +... + +``` The param-value for each list is a comma separated string value representing the list. ## Masking Passwords -For details about masking passwords in broker.xml please see the [Masking Passwords](masking-passwords.md) chapter. +For details about masking passwords in broker.xml please see the [Masking +Passwords](masking-passwords.md) chapter. diff --git a/docs/user-manual/en/send-guarantees.md b/docs/user-manual/en/send-guarantees.md index 79b9097bd69..da631e47efc 100644 --- a/docs/user-manual/en/send-guarantees.md +++ b/docs/user-manual/en/send-guarantees.md @@ -32,15 +32,15 @@ has definitely reached the server, and a response has been sent back to the client. This can be configured individually for durable and non-durable messages, and is determined by the following two URL parameters: -- `blockOnDurableSend`. If this is set to `true` then all calls to - send for durable messages on non transacted sessions will block - until the message has reached the server, and a response has been - sent back. The default value is `true`. - -- `blockOnNonDurableSend`. If this is set to `true` then all calls to - send for non-durable messages on non transacted sessions will block - until the message has reached the server, and a response has been - sent back. The default value is `false`. +- `blockOnDurableSend`. If this is set to `true` then all calls to + send for durable messages on non transacted sessions will block + until the message has reached the server, and a response has been + sent back. The default value is `true`. + +- `blockOnNonDurableSend`. If this is set to `true` then all calls to + send for non-durable messages on non transacted sessions will block + until the message has reached the server, and a response has been + sent back. The default value is `false`. Setting block on sends to `true` can reduce performance since each send requires a network round trip before the next send can be performed. diff --git a/docs/user-manual/en/slow-consumers.md b/docs/user-manual/en/slow-consumers.md index 49d5141cc4b..fc85d38a009 100644 --- a/docs/user-manual/en/slow-consumers.md +++ b/docs/user-manual/en/slow-consumers.md @@ -36,5 +36,5 @@ the detection algorithm. See [thread pooling](thread-pooling.md) for more detail ## Example -See the [examples](examples.md) chapter for an example which shows how to detect a slow consumer +See the [slow consumer example](examples.md#slow-consumer) which shows how to detect a slow consumer with Apache ActiveMQ Artemis. diff --git a/docs/user-manual/en/spring-integration.md b/docs/user-manual/en/spring-integration.md index 9c7c9390592..f63df5c87a2 100644 --- a/docs/user-manual/en/spring-integration.md +++ b/docs/user-manual/en/spring-integration.md @@ -1,39 +1,16 @@ # Spring Integration Apache ActiveMQ Artemis provides a simple bootstrap class, -`org.apache.activemq.integration.spring.SpringJmsBootstrap`, for -integration with Spring. To use it, you configure Apache ActiveMQ Artemis as you always -would, through its various configuration files like -`broker.xml`. +`org.apache.activemq.artemis.integration.spring.SpringJmsBootstrap`, for +integration with Spring. To use it, you configure Apache ActiveMQ Artemis as +you always would, through its various configuration files like `broker.xml`. -Here we've specified a `javax.jms.ConnectionFactory` we want bound to a -`ConnectionFactory` entry as well as a queue destination bound to a -`/queue/exampleQueue` entry. Using the `SpringJmsBootStrap` bean will -automatically populate the Spring context with references to those beans -so that you can use them. Below is an example Spring JMS bean file -taking advantage of this feature: +The `SpringJmsBootstrap` class extends the EmbeddedJMS class talked about in +[embedding ActiveMQ](embedding-activemq.md) and the same defaults and +configuration options apply. See the javadocs for more details on other +properties of the bean class. -```xml - +## Example - - - - - - - - - - -``` - -As you can see, the `listenerContainer` bean references the components -defined in the `activemq-jms.xml` file. The `SpringJmsBootstrap` class -extends the EmbeddedJMS class talked about in [JMS API](embedding-activemq.md) and the same defaults -and configuration options apply. Also notice that an `init-method` must -be declared with a start value so that the bean's lifecycle is executed. -See the javadocs for more details on other properties of the bean class. +See the [Spring Integration Example](examples.md#spring-integration) for a +demonstration of how this can work. diff --git a/docs/user-manual/en/stomp.md b/docs/user-manual/en/stomp.md new file mode 100644 index 00000000000..8ba11f2bd1f --- /dev/null +++ b/docs/user-manual/en/stomp.md @@ -0,0 +1,293 @@ +# STOMP + +[STOMP](https://stomp.github.io/) is a text-orientated wire protocol that +allows STOMP clients to communicate with STOMP Brokers. Apache ActiveMQ Artemis +supports STOMP 1.0, 1.1 and 1.2. + +STOMP clients are available for several languages and platforms making it a +good choice for interoperability. + +By default there are `acceptor` elements configured to accept STOMP connections +on ports `61616` and `61613`. + +See the general [Protocols and Interoperability](protocols-interoperability.md) +chapter for details on configuring an `acceptor` for STOMP. + +Refer to the STOMP examples for a look at some of this functionality in action. + +## Limitations + +The STOMP specification identifies **transactional acknowledgements** as an +optional feature. Support for transactional acknowledgements is not implemented +in Apache ActiveMQ Artemis. The `ACK` frame can not be part of a transaction. +It will be ignored if its `transaction` header is set. + +## Virtual Hosting + +Apache ActiveMQ Artemis currently doesn't support virtual hosting, which means +the `host` header in `CONNECT` frame will be ignored. + +## Mapping STOMP destinations to addresses and queues + +STOMP clients deals with *destinations* when sending messages and subscribing. +Destination names are simply strings which are mapped to some form of +destination on the server - how the server translates these is left to the +server implementation. + +In Apache ActiveMQ Artemis, these destinations are mapped to *addresses* and +*queues* depending on the operation being done and the desired semantics (e.g. +anycast or multicast). + +## Sending + +When a STOMP client sends a message (using a `SEND` frame), the protocol +manager looks at the message to determine where to route it and potentially how +to create the address and/or queue to which it is being sent. The protocol +manager uses either of the following bits of information from the frame to +determine the routing type: + +1. The value of the `destination-type` header. Valid values are `ANYCAST` and + `MULTICAST` (case sensitive). + +2. The "prefix" on the `destination` header. See [additional + info](address-model.md#using-prefixes-to-determine-routing-type) on + prefixes. + +If no indication of routing type is supplied then anycast semantics are used. + +The `destination` header maps to an address of the same name. If the +`destination` header used a prefix then the prefix is stripped. + +## Subscribing + +When a STOMP client subscribes to a destination (using a `SUBSCRIBE` frame), +the protocol manager looks at the frame to determine what subscription +semantics to use and potentially how to create the address and/or queue for the +subscription. The protocol manager uses either of the following bits of +information from the frame to determine the routing type: + +1. The value of the `subscription-type` header. Valid values are `ANYCAST` and + `MULTICAST` (case sensitive). + +2. The "prefix" on the `destination` header. See [additional + info](address-model.md#using-prefixes-to-determine-routing-type) on + prefixes. + +If no indication of routing type is supplied then anycast semantics are used. + +The `destination` header maps to an address of the same name if multicast is +used or to a queue of the same name if anycast is used. If the `destination` +header used a prefix then the prefix is stripped. + +## STOMP heart-beating and connection-ttl + +Well behaved STOMP clients will always send a `DISCONNECT` frame before closing +their connections. In this case the server will clear up any server side +resources such as sessions and consumers synchronously. However if STOMP +clients exit without sending a `DISCONNECT` frame or if they crash the server +will have no way of knowing immediately whether the client is still alive or +not. STOMP connections therefore default to a `connection-ttl` value of 1 +minute (see chapter on [connection-ttl](connection-ttl.md) for more +information. This value can be overridden using the `connection-ttl-override` +property or if you need a specific connectionTtl for your stomp connections +without affecting the broker-wide `connection-ttl-override` setting, you can +configure your stomp acceptor with the `connectionTtl` property, which is used +to set the ttl for connections that are created from that acceptor. For +example: + +```xml +tcp://localhost:61613?protocols=STOMP;connectionTtl=20000 +``` + +The above configuration will make sure that any STOMP connection that is +created from that acceptor and does not include a `heart-beat` header or +disables client-to-server heart-beats by specifying a `0` value will have its +`connection-ttl` set to 20 seconds. The `connectionTtl` set on an acceptor will +take precedence over `connection-ttl-override`. The default `connectionTtl` is +60,000 milliseconds. + +Since STOMP 1.0 does not support heart-beating then all connections from STOMP +1.0 clients will have a connection TTL imposed upon them by the broker based on +the aforementioned configuration options. Likewise, any STOMP 1.1 or 1.2 +clients that don't specify a `heart-beat` header or disable client-to-server +heart-beating (e.g. by sending `0,X` in the `heart-beat` header) will have a +connection TTL imposed upon them by the broker. + +For STOMP 1.1 and 1.2 clients which send a non-zero client-to-server +`heart-beat` header value then their connection TTL will be set accordingly. +However, the broker will not strictly set the connection TTL to the same value +as the specified in the `heart-beat` since even small network delays could then +cause spurious disconnects. Instead, the client-to-server value in the +`heart-beat` will be multiplied by the `heartBeatConnectionTtlModifer` +specified on the acceptor. The `heartBeatConnectionTtlModifer` is a decimal +value that defaults to `2.0` so for example, if a client sends a `heart-beat` +header of `1000,0` the the connection TTL will be set to `2000` so that the +data or ping frames sent every 1000 milliseconds will have a sufficient cushion +so as not to be considered late and trigger a disconnect. This is also in +accordance with the STOMP 1.1 and 1.2 specifications which both state, "because +of timing inaccuracies, the receiver SHOULD be tolerant and take into account +an error margin." + +The minimum and maximum connection TTL allowed can also be specified on the +acceptor via the `connectionTtlMin` and `connectionTtlMax` properties +respectively. The default `connectionTtlMin` is 1000 and the default +`connectionTtlMax` is Java's `Long.MAX_VALUE` meaning there essentially is no +max connection TTL by default. Keep in mind that the +`heartBeatConnectionTtlModifer` is relevant here. For example, if a client +sends a `heart-beat` header of `20000,0` and the acceptor is using a +`connectionTtlMax` of `30000` and a default `heartBeatConnectionTtlModifer` of +`2.0` then the connection TTL would be `40000` (i.e. `20000` * `2.0`) which +would exceed the `connectionTtlMax`. In this case the server would respond to +the client with a `heart-beat` header of `0,15000` (i.e. `30000` / `2.0`). As +described previously, this is to make sure there is a sufficient cushion for +the client heart-beats in accordance with the STOMP 1.1 and 1.2 specifications. +The same kind of calculation is done for `connectionTtlMin`. + +The minimum server-to-client heart-beat value is 500ms. + +> **Note:** +> +> Please note that the STOMP protocol version 1.0 does not contain any +> heart-beat frame. It is therefore the user's responsibility to make sure data +> is sent within connection-ttl or the server will assume the client is dead +> and clean up server side resources. With STOMP 1.1 users can use heart-beats +> to maintain the life cycle of stomp connections. + +## Selector/Filter expressions + +STOMP subscribers can specify an expression used to select or filter what the +subscriber receives using the `selector` header. The filter expression syntax +follows the *core filter syntax* described in the [Filter +Expressions](filter-expressions.md) documentation. + +## STOMP and JMS interoperability + +### Sending and consuming STOMP message from JMS or Core API + +STOMP is mainly a text-orientated protocol. To make it simpler to interoperate +with JMS and Core API, our STOMP implementation checks for presence of the +`content-length` header to decide how to map a STOMP 1.0 message to a JMS +Message or a Core message. + +If the STOMP 1.0 message does *not* have a `content-length` header, it will be +mapped to a JMS *TextMessage* or a Core message with a *single nullable +SimpleString in the body buffer*. + +Alternatively, if the STOMP 1.0 message *has* a `content-length` header, it +will be mapped to a JMS *BytesMessage* or a Core message with a *byte[] in the +body buffer*. + +The same logic applies when mapping a JMS message or a Core message to STOMP. A +STOMP 1.0 client can check the presence of the `content-length` header to +determine the type of the message body (String or bytes). + +### Message IDs for STOMP messages + +When receiving STOMP messages via a JMS consumer or a QueueBrowser, the +messages have no properties like JMSMessageID by default. However this may +bring some inconvenience to clients who wants an ID for their purpose. The +broker STOMP provides a parameter to enable message ID on each incoming STOMP +message. If you want each STOMP message to have a unique ID, just set the +`stompEnableMessageId` to true. For example: + +```xml +tcp://localhost:61613?protocols=STOMP;stompEnableMessageId=true +``` + +When the server starts with the above setting, each stomp message sent through +this acceptor will have an extra property added. The property key is +`amq-message-id` and the value is a String representation of a long type +internal message id prefixed with `STOMP`, like: + +``` +amq-message-id : STOMP12345 +``` + +The default `stomp-enable-message-id` value is `false`. + +## Durable Subscriptions + +The `SUBSCRIBE` and `UNSUBSCRIBE` frames can be augmented with special headers +to create and destroy durable subscriptions respectively. + +To create a durable subscription the `client-id` header must be set on the +`CONNECT` frame and the `durable-subscription-name` must be set on the +`SUBSCRIBE` frame. The combination of these two headers will form the identity +of the durable subscription. + +To delete a durable subscription the `client-id` header must be set on the +`CONNECT` frame and the `durable-subscription-name` must be set on the +`UNSUBSCRIBE` frame. The values for these headers should match what was set on +the `SUBSCRIBE` frame to delete the corresponding durable subscription. + +It is possible to pre-configure durable subscriptions since the STOMP +implementation creates the queue used for the durable subscription in a +deterministic way (i.e. using the format of `client-id`.`subscription-name`). +For example, if you wanted to configure a durable subscription on the address +`myAddress` with a client-id of `myclientid` and a subscription name of +`mysubscription` then configure the durable subscription: + +```xml + +
+ + + +
+
+``` + +## Handling of Large Messages with STOMP + +STOMP clients may send very large frame bodies which can exceed the size of the +broker's internal buffer, causing unexpected errors. To prevent this situation +from happening, the broker provides a STOMP configuration attribute +`stompMinLargeMessageSize`. This attribute can be configured inside a stomp +acceptor, as a parameter. For example: + +```xml +tcp://localhost:61613?protocols=STOMP;stompMinLargeMessageSize=10240 +``` + +The type of this attribute is integer. When this attributed is configured, the +broker will check the size of the body of each STOMP frame arrived from +connections established with this acceptor. If the size of the body is equal or +greater than the value of `stompMinLargeMessageSize`, the message will be +persisted as a large message. When a large message is delievered to a STOMP +consumer, the broker will automatically handle the conversion from a large +message to a normal message, before sending it to the client. + +If a large message is compressed, the server will uncompressed it before +sending it to stomp clients. The default value of `stompMinLargeMessageSize` is +the same as the default value of +[min-large-message-size](large-messages.md#configuring-parameters). + +## Web Sockets + +Apache ActiveMQ Artemis also support STOMP over [Web +Sockets](https://html.spec.whatwg.org/multipage/web-sockets.html). Modern web +browsers which support Web Sockets can send and receive STOMP messages. + +STOMP over Web Sockets is supported via the normal STOMP acceptor: + +```xml +tcp://localhost:61614?protocols=STOMP +``` + +With this configuration, Apache ActiveMQ Artemis will accept STOMP connections +over Web Sockets on the port `61614`. Web browsers can then connect to +`ws://:61614` using a Web Socket to send and receive STOMP messages. + +A companion JavaScript library to ease client-side development is available +from [GitHub](https://github.com/jmesnil/stomp-websocket) (please see its +[documentation](http://jmesnil.net/stomp-websocket/doc/) for a complete +description). + +The payload length of Web Socket frames can vary between client +implementations. By default the broker will accept frames with a payload length +of 65,536. If the client needs to send payloads longer than this in a single +frame this length can be adjusted by using the `stompMaxFramePayloadLength` URL +parameter on the acceptor. + +The `stomp-websockets` example shows how to configure an Apache ActiveMQ +Artemis broker to have web browsers and Java applications exchanges messages. diff --git a/docs/user-manual/en/syntax.md b/docs/user-manual/en/syntax.md index 71db615ea8d..c37820d5b77 100644 --- a/docs/user-manual/en/syntax.md +++ b/docs/user-manual/en/syntax.md @@ -11,7 +11,7 @@ Somejava s = new SomeJava(); ``` -> **Note** +> **Note:** > > This is a Note diff --git a/docs/user-manual/en/tools.md b/docs/user-manual/en/tools.md deleted file mode 100644 index 4a6aa0de02a..00000000000 --- a/docs/user-manual/en/tools.md +++ /dev/null @@ -1,216 +0,0 @@ -# Tools - -You can use the artemis cli interface to execute data maintenance tools: - -This is a list of sub-commands available - -Name | Description -:--- | :--- -exp | Export the message data using a special and independent XML format -imp | Imports the journal to a running broker using the output from expt -data | Prints a report about journal records and summary of existent records, as well a report on paging -encode | shows an internal format of the journal encoded to String -decode | imports the internal journal format from encode - -You can use the help at the tool for more information on how to execute each of the tools. For example: - -``` -$ ./artemis help data print -NAME - artemis data print - Print data records information (WARNING: don't use - while a production server is running) - -SYNOPSIS - artemis data print [--bindings ] [--journal ] - [--paging ] - -OPTIONS - --bindings - The folder used for bindings (default ../data/bindings) - - --journal - The folder used for messages journal (default ../data/journal) - - --paging - The folder used for paging (default ../data/paging) - - -``` - - -For a full list of data tools commands available use: - -``` -NAME - artemis data - data tools group - (print|imp|exp|encode|decode|compact) (example ./artemis data print) - -SYNOPSIS - artemis data - artemis data compact [--broker ] [--verbose] - [--paging ] [--journal ] - [--large-messages ] [--bindings ] - artemis data decode [--broker ] [--suffix ] - [--verbose] [--paging ] [--prefix ] [--file-size ] - [--directory ] --input [--journal ] - [--large-messages ] [--bindings ] - artemis data encode [--directory ] [--broker ] - [--suffix ] [--verbose] [--paging ] [--prefix ] - [--file-size ] [--journal ] - [--large-messages ] [--bindings ] - artemis data exp [--broker ] [--verbose] - [--paging ] [--journal ] - [--large-messages ] [--bindings ] - artemis data imp [--host ] [--verbose] [--port ] - [--password ] [--transaction] --input [--user ] - artemis data print [--broker ] [--verbose] - [--paging ] [--journal ] - [--large-messages ] [--bindings ] - -COMMANDS - With no arguments, Display help information - - print - Print data records information (WARNING: don't use while a - production server is running) - - With --broker option, This would override the broker configuration - from the bootstrap - - With --verbose option, Adds more information on the execution - - With --paging option, The folder used for paging (default from - broker.xml) - - With --journal option, The folder used for messages journal (default - from broker.xml) - - With --large-messages option, The folder used for large-messages - (default from broker.xml) - - With --bindings option, The folder used for bindings (default from - broker.xml) - - exp - Export all message-data using an XML that could be interpreted by - any system. - - With --broker option, This would override the broker configuration - from the bootstrap - - With --verbose option, Adds more information on the execution - - With --paging option, The folder used for paging (default from - broker.xml) - - With --journal option, The folder used for messages journal (default - from broker.xml) - - With --large-messages option, The folder used for large-messages - (default from broker.xml) - - With --bindings option, The folder used for bindings (default from - broker.xml) - - imp - Import all message-data using an XML that could be interpreted by - any system. - - With --host option, The host used to import the data (default - localhost) - - With --verbose option, Adds more information on the execution - - With --port option, The port used to import the data (default 61616) - - With --password option, User name used to import the data. (default - null) - - With --transaction option, If this is set to true you will need a - whole transaction to commit at the end. (default false) - - With --input option, The input file name (default=exp.dmp) - - With --user option, User name used to import the data. (default - null) - - decode - Decode a journal's internal format into a new journal set of files - - With --broker option, This would override the broker configuration - from the bootstrap - - With --suffix option, The journal suffix (default amq) - - With --verbose option, Adds more information on the execution - - With --paging option, The folder used for paging (default from - broker.xml) - - With --prefix option, The journal prefix (default activemq-data) - - With --file-size option, The journal size (default 10485760) - - With --directory option, The journal folder (default journal folder - from broker.xml) - - With --input option, The input file name (default=exp.dmp) - - With --journal option, The folder used for messages journal (default - from broker.xml) - - With --large-messages option, The folder used for large-messages - (default from broker.xml) - - With --bindings option, The folder used for bindings (default from - broker.xml) - - encode - Encode a set of journal files into an internal encoded data format - - With --directory option, The journal folder (default the journal - folder from broker.xml) - - With --broker option, This would override the broker configuration - from the bootstrap - - With --suffix option, The journal suffix (default amq) - - With --verbose option, Adds more information on the execution - - With --paging option, The folder used for paging (default from - broker.xml) - - With --prefix option, The journal prefix (default activemq-data) - - With --file-size option, The journal size (default 10485760) - - With --journal option, The folder used for messages journal (default - from broker.xml) - - With --large-messages option, The folder used for large-messages - (default from broker.xml) - - With --bindings option, The folder used for bindings (default from - broker.xml) - - compact - Compacts the journal of a non running server - - With --broker option, This would override the broker configuration - from the bootstrap - - With --verbose option, Adds more information on the execution - - With --paging option, The folder used for paging (default from - broker.xml) - - With --journal option, The folder used for messages journal (default - from broker.xml) - - With --large-messages option, The folder used for large-messages - (default from broker.xml) - - With --bindings option, The folder used for bindings (default from - broker.xml) -``` \ No newline at end of file diff --git a/docs/user-manual/en/undelivered-messages.md b/docs/user-manual/en/undelivered-messages.md index 6156c8f8e08..5ef5565085b 100644 --- a/docs/user-manual/en/undelivered-messages.md +++ b/docs/user-manual/en/undelivered-messages.md @@ -8,18 +8,18 @@ in the queue indefinitely, clogging the system. There are 2 ways to deal with these undelivered messages: -- Delayed redelivery. +- Delayed redelivery. - It is possible to delay messages redelivery. This gives the client some - time to recover from any transient failures and to prevent overloading - its network or CPU resources. + It is possible to delay messages redelivery. This gives the client some + time to recover from any transient failures and to prevent overloading + its network or CPU resources. -- Dead Letter Address. +- Dead Letter Address. - It is also possible to configure a dead letter address so that after - a specified number of unsuccessful deliveries, messages are removed - from their queue and sent to the dead letter address. These messages - will not be delivered again from this queue. + It is also possible to configure a dead letter address so that after + a specified number of unsuccessful deliveries, messages are removed + from their queue and sent to the dead letter address. These messages + will not be delivered again from this queue. Both options can be combined for maximum flexibility. @@ -130,15 +130,15 @@ set of addresses (see [Understanding the Wildcard Syntax](wildcard-syntax.md)). Dead letter messages which are consumed from a dead letter address have the following properties: -- `_AMQ_ORIG_ADDRESS` +- `_AMQ_ORIG_ADDRESS` - a String property containing the *original address* of the dead - letter message + a String property containing the *original address* of the dead + letter message -- `_AMQ_ORIG_QUEUE` +- `_AMQ_ORIG_QUEUE` - a String property containing the *original queue* of the dead letter - message + a String property containing the *original queue* of the dead letter + message ### Example diff --git a/docs/user-manual/en/unit-testing.md b/docs/user-manual/en/unit-testing.md index 7beb311cfe0..859ec007ac1 100644 --- a/docs/user-manual/en/unit-testing.md +++ b/docs/user-manual/en/unit-testing.md @@ -1,29 +1,26 @@ # Unit Testing -The package ```artemis-junit``` provides tools to facilitate how to run Artemis resources inside Junit Tests. +The package `artemis-junit` provides tools to facilitate how to run Artemis resources inside JUnit Tests. -These are provided as junit rules and can make it easier to embed Messaging functionality on your tests. +These are provided as JUnit "rules" and can make it easier to embed messaging functionality on your tests. ## Example - ### Import this on your pom.xml ```xml - org.apache.activemq - artemis-junit - - 1.5.0 - test + org.apache.activemq + artemis-junit + + 2.5.0 + test ``` - ### Declare a rule on your JUnit Test - ```java import org.apache.activemq.artemis.junit.EmbeddedJMSResource; import org.junit.Rule; @@ -44,34 +41,31 @@ public class MyTest { This will start a server that will be available for your test: ``` -ain] 17:00:16,644 INFO [org.apache.activemq.artemis.core.server] AMQ221000: live Message Broker is starting with configuration Broker Configuration (clustered=false,journalDirectory=data/journal,bindingsDirectory=data/bindings,largeMessagesDirectory=data/largemessages,pagingDirectory=data/paging) +[main] 17:00:16,644 INFO [org.apache.activemq.artemis.core.server] AMQ221000: live Message Broker is starting with configuration Broker Configuration (clustered=false,journalDirectory=data/journal,bindingsDirectory=data/bindings,largeMessagesDirectory=data/largemessages,pagingDirectory=data/paging) [main] 17:00:16,666 INFO [org.apache.activemq.artemis.core.server] AMQ221045: libaio is not available, switching the configuration into NIO [main] 17:00:16,688 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-server]. Adding protocol support for: CORE [main] 17:00:16,801 INFO [org.apache.activemq.artemis.core.server] AMQ221007: Server is now live [main] 17:00:16,801 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 1.5.0-SNAPSHOT [embedded-jms-server, nodeID=39e78380-842c-11e6-9e43-f45c8992f3c7] [main] 17:00:16,891 INFO [org.apache.activemq.artemis.core.server] AMQ221002: Apache ActiveMQ Artemis Message Broker version 1.5.0-SNAPSHOT [39e78380-842c-11e6-9e43-f45c8992f3c7] stopped, uptime 0.272 seconds - ``` - ### Ordering rules -This is actually a Junit feature, but this could be helpful on pre-determining the order on which rules are executed. +This is actually a JUnit feature, but this could be helpful on pre-determining the order on which rules are executed. ```java - ActiveMQDynamicProducerResource producer = new ActiveMQDynamicProducerResource(server.getVmURL()); - - @Rule - public RuleChain ruleChain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server).around(producer); +ActiveMQDynamicProducerResource producer = new ActiveMQDynamicProducerResource(server.getVmURL()); +@Rule +public RuleChain ruleChain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server).around(producer); ``` ### Available Rules Name | Description -:--- | :--- -EmbeddedActiveMQResource | It will run a Server, without the JMS manager -EmbeddedJMSResource | It will run a Server, including the JMS Manager -ActiveMQConsumerResource | It will automate the creation of a consumer -ActiveMQProducerResource | It will automate the creation of a producer -ThreadLeakCheckRule | It will check that all threads have been finished after the test is finished +--- | --- +EmbeddedActiveMQResource | Run a Server, without the JMS manager +EmbeddedJMSResource | Run a Server, including the JMS Manager +ActiveMQConsumerResource | Automate the creation of a consumer +ActiveMQProducerResource | Automate the creation of a producer +ThreadLeakCheckRule | Check that all threads have been finished after the test is finished diff --git a/docs/user-manual/en/upgrading.md b/docs/user-manual/en/upgrading.md index e564f49b2d2..61609b8c9b3 100644 --- a/docs/user-manual/en/upgrading.md +++ b/docs/user-manual/en/upgrading.md @@ -1,38 +1,41 @@ # Upgrading the Broker -Apache ActiveMQ 5.x (and previous versions) is runnable out of the box by executing -the command: `./bin/activemq run`. The ActiveMQ Artemis broker follows a different -paradigm where the project distribution serves as the broker "home" and one or more -broker "instances" are created which reference the "home" for resources (e.g. jar files) -which can be safely shared between broker instances. Therefore, an instance of the broker -must be created before it can be run. This may seems like an overhead at first -glance, but it becomes very practical when updating to a new Artemis version for example. - -To create an Artemis broker instance navigate into the Artemis home folder and run: -`./bin/artemis create /path/to/myBrokerInstance` on the command line. - -> **Note** -> -> It's recommended to choose a folder different than the on where Apache Artemis was -> downloaded. This separation allows you run multiple broker instances with the same -> Artemis "home" for example. It also simplifies updating to newer versions of Artemis. +Apache ActiveMQ 5.x (and previous versions) is runnable out of the box by +executing the command: `./bin/activemq run`. The ActiveMQ Artemis broker +follows a different paradigm where the project distribution serves as the +broker "home" and one or more broker "instances" are created which reference +the "home" for resources (e.g. jar files) which can be safely shared between +broker instances. Therefore, an instance of the broker must be created before +it can be run. This may seems like an overhead at first glance, but it becomes +very practical when updating to a new Artemis version for example. + +To create an Artemis broker instance navigate into the Artemis home folder and +run: `./bin/artemis create /path/to/myBrokerInstance` on the command line. -Because of this separation it's very easy to upgrade Artemis in most cases. +Because of this separation it's very easy to upgrade Artemis in most cases. + +> **Note:** +> +> It's recommended to choose a folder different than the on where Apache +> Artemis was downloaded. This separation allows you run multiple broker +> instances with the same Artemis "home" for example. It also simplifies +> updating to newer versions of Artemis. ## General Upgrade Procedure -Upgrading may require some specific steps noted in the [versions](versions.md), but the -general process is as follows: +Upgrading may require some specific steps noted in the [versions](versions.md), +but the general process is as follows: 1. Navigate to the `etc` folder of the broker instance that's being upgraded -1. Open `artemis.profile` (`artemis.profile.cmd` on Windows). It contains a property - which is relevant for the upgrade: +1. Open `artemis.profile` (`artemis.profile.cmd` on Windows). It contains a + property which is relevant for the upgrade: ``` ARTEMIS_HOME='/path/to/apache-artemis-version' ``` -The `ARTEMIS_HOME` property is used to link the instance with the home. -_In most cases_ the instance can be upgraded to a newer version simply by changing the -value of this property to the location of the new broker home. Please refer to the -aforementioned [versions](versions.md) document for additional upgrade steps (if required). \ No newline at end of file +The `ARTEMIS_HOME` property is used to link the instance with the home. _In +most cases_ the instance can be upgraded to a newer version simply by changing +the value of this property to the location of the new broker home. Please refer +to the aforementioned [versions](versions.md) document for additional upgrade +steps (if required). diff --git a/docs/user-manual/en/using-AMQP.md b/docs/user-manual/en/using-AMQP.md deleted file mode 100644 index 49d04782179..00000000000 --- a/docs/user-manual/en/using-AMQP.md +++ /dev/null @@ -1,74 +0,0 @@ -# Using AMQP - -Apache ActiveMQ Artemis is also a pure AMQP 1.0 broker, with a high performant and feature complete protocol manager for AMQP. - -You can use *any* AMQP 1.0 compatible clients. - -A short list includes: - -- qpid clients at the [qpid project](https://qpid.apache.org/download.html) -- [.NET Clients](https://blogs.apache.org/activemq/entry/using-net-libraries-with-activemq) -- [Javascript NodeJS](https://github.com/noodlefrenzy/node-amqp10) -- [Java Script RHEA](https://github.com/grs/rhea) - - -... and many others. - - -# Message Conversions - -The broker will not perform any message conversion to any other protocols when sending AMQP and receiving AMQP. - -However if you intend your message to be received on a AMQP JMS Client, you must follow the JMS Mapping convention: - -- [JMS Mapping Conventions](https://www.oasis-open.org/committees/download.php/53086/amqp-bindmap-jms-v1.0-wd05.pdf) - - -If you send a body type that is not recognized by this specification the conversion between AMQP and any other protocol will make it a Binary Message. - -So, make sure you follow these conventions if you intend to cross protocols or languages. Especially on the message body. - -A compatibility setting, allows aligning the naming convention of AMQP queues (JMS Durable and Shared Subscriptions) with CORE. -For backwards compatibility reasons, you need to explicitly enable this via broker configuration: - -* amqp-use-core-subscription-naming - * true - use queue naming convention that is aligned with CORE. - * false (DEFAULT) - use older naming convention. - - -# Example - -We have a few examples as part of the Artemis distribution: - - -- .NET: - * ./examples/protocols/amqp/dotnet - -- ProtonCPP - * ./examples/protocols/amqp/proton-cpp - -- Ruby - * ./examples/protocols/amqp/proton-ruby - -- Java (Using the qpid JMS Client) - * ./examples/protocols/amqp/queue - -- Interceptors - * ./examples/features/standard/broker-plugin - - - # Intercepting and changing messages - - We don't recommend changing messages at the server's side for a few reasons: - - - AMQPMessages are meant to be immutable - - The message won't be the original message the user sent - - AMQP has the possibility of signing messages. The signature would be broken. - - For performance reasons. We try not to re-encode (or even decode) messages. - -If regardless these recommendations you still need and want to intercept and change AMQP Messages, look at the example under ./examples/features/standard/broker-plugin. - -This example will send AMQP Message and modify properties before they reach the journals and are sent to the consumers. - - - diff --git a/docs/user-manual/en/using-core.md b/docs/user-manual/en/using-core.md deleted file mode 100644 index 78c08a5589c..00000000000 --- a/docs/user-manual/en/using-core.md +++ /dev/null @@ -1,212 +0,0 @@ -# Using Core - -Apache ActiveMQ Artemis core is a completely JMS-agnostic messaging system with its own -API. We call this the *core API*. - -If you don't want to use JMS or other protocols you can use the core API directly. The core -API provides all the functionality of JMS but without much of the -complexity. It also provides features that are not available using JMS. - -## Core Messaging Concepts - -Some of the core messaging concepts are similar to JMS concepts, but -core messaging concepts differ in some ways. In general the core -messaging API is simpler than the JMS API, since we remove distinctions -between queues, topics and subscriptions. We'll discuss each of the -major core messaging concepts in turn, but to see the API in detail, -please consult the Javadoc. - -### Message - -- A message is the unit of data which is sent between clients and - servers. - -- A message has a body which is a buffer containing convenient methods - for reading and writing data into it. - -- A message has a set of properties which are key-value pairs. Each - property key is a string and property values can be of type integer, - long, short, byte, byte[], String, double, float or boolean. - -- A message has an *address* it is being sent to. When the message - arrives on the server it is routed to any queues that are bound to - the address - if the queues are bound with any filter, the message - will only be routed to that queue if the filter matches. An address - may have many queues bound to it or even none. There may also be - entities other than queues, like *diverts* bound to addresses. - -- Messages can be either durable or non durable. Durable messages in a - durable queue will survive a server crash or restart. Non durable - messages will never survive a server crash or restart. - -- Messages can be specified with a priority value between 0 and 9. 0 - represents the lowest priority and 9 represents the highest. - Apache ActiveMQ Artemis will attempt to deliver higher priority messages before - lower priority ones. - -- Messages can be specified with an optional expiry time. Apache ActiveMQ Artemis - will not deliver messages after its expiry time has been exceeded. - -- Messages also have an optional timestamp which represents the time - the message was sent. - -- Apache ActiveMQ Artemis also supports the sending/consuming of very large messages - much larger than can fit in available RAM at any one time. - -### Address - -A server maintains a mapping between an address and a set of queues. -Zero or more queues can be bound to a single address. Each queue can be -bound with an optional message filter. When a message is routed, it is -routed to the set of queues bound to the message's address. If any of -the queues are bound with a filter expression, then the message will -only be routed to the subset of bound queues which match that filter -expression. - -Other entities, such as *diverts* can also be bound to an address and -messages will also be routed there. - -> **Note** -> -> In core, there is no concept of a Topic, Topic is a JMS only term. -> Instead, in core, we just deal with *addresses* and *queues*. -> -> For example, a JMS topic would be implemented by a single address to -> which many queues are bound. Each queue represents a subscription of -> the topic. A JMS Queue would be implemented as a single address to -> which one queue is bound - that queue represents the JMS queue. - -### Queue - -Queues can be durable, meaning the messages they contain survive a -server crash or restart, as long as the messages in them are durable. -Non durable queues do not survive a server restart or crash even if the -messages they contain are durable. - -Queues can also be temporary, meaning they are automatically deleted -when the client connection is closed, if they are not explicitly deleted -before that. - -Queues can be bound with an optional filter expression. If a filter -expression is supplied then the server will only route messages that -match that filter expression to any queues bound to the address. - -Many queues can be bound to a single address. A particular queue is only -bound to a maximum of one address. - -### ServerLocator - -Clients use `ServerLocator` instances to create `ClientSessionFactory` -instances. `ServerLocator` instances are used to locate servers and -create connections to them. - -In JMS terms think of a `ServerLocator` in the same way you would a JMS -Connection Factory. - -`ServerLocator` instances are created using the `ActiveMQClient` factory -class. - -### ClientSessionFactory - -Clients use `ClientSessionFactory` instances to create `ClientSession` -instances. `ClientSessionFactory` instances are basically the connection -to a server - -In JMS terms think of them as JMS Connections. - -`ClientSessionFactory` instances are created using the `ServerLocator` -class. - -### ClientSession - -A client uses a ClientSession for consuming and producing messages and -for grouping them in transactions. ClientSession instances can support -both transactional and non transactional semantics and also provide an -`XAResource` interface so messaging operations can be performed as part -of a -[JTA](http://www.oracle.com/technetwork/java/javaee/tech/jta-138684.html) -transaction. - -ClientSession instances group ClientConsumers and ClientProducers. - -ClientSession instances can be registered with an optional -`SendAcknowledgementHandler`. This allows your client code to be -notified asynchronously when sent messages have successfully reached the -server. This unique Apache ActiveMQ Artemis feature, allows you to have full guarantees -that sent messages have reached the server without having to block on -each message sent until a response is received. Blocking on each -messages sent is costly since it requires a network round trip for each -message sent. By not blocking and receiving send acknowledgements -asynchronously you can create true end to end asynchronous systems which -is not possible using the standard JMS API. For more information on this -advanced feature please see the section [Guarantees of sends and commits](send-guarantees.md). - -### ClientConsumer - -Clients use `ClientConsumer` instances to consume messages from a queue. -Core Messaging supports both synchronous and asynchronous message -consumption semantics. `ClientConsumer` instances can be configured with -an optional filter expression and will only consume messages which match -that expression. - -### ClientProducer - -Clients create `ClientProducer` instances on `ClientSession` instances -so they can send messages. ClientProducer instances can specify an -address to which all sent messages are routed, or they can have no -specified address, and the address is specified at send time for the -message. - -> **Warning** -> -> Please note that ClientSession, ClientProducer and ClientConsumer -> instances are *designed to be re-used*. -> -> It's an anti-pattern to create new ClientSession, ClientProducer and -> ClientConsumer instances for each message you produce or consume. If -> you do this, your application will perform very poorly. This is -> discussed further in the section on performance tuning [Performance Tuning](perf-tuning.md). - -## A simple example of using Core - -Here's a very simple program using the core messaging API to send and -receive a message. Logically it's comprised of two sections: firstly -setting up the producer to write a message to an *addresss*, and -secondly, creating a *queue* for the consumer, creating the consumer and -*starting* it. -```java -ServerLocator locator = ActiveMQClient.createServerLocatorWithoutHA(new TransportConfiguration( - InVMConnectorFactory.class.getName())); - -// In this simple example, we just use one session for both producing and receiving - -ClientSessionFactory factory = locator.createClientSessionFactory(); -ClientSession session = factory.createSession(); - -// A producer is associated with an address ... - -ClientProducer producer = session.createProducer("example"); -ClientMessage message = session.createMessage(true); -message.getBodyBuffer().writeString("Hello"); - -// We need a queue attached to the address ... - -session.createQueue("example", "example", true); - -// And a consumer attached to the queue ... - -ClientConsumer consumer = session.createConsumer("example"); - -// Once we have a queue, we can send the message ... - -producer.send(message); - -// We need to start the session before we can -receive- messages ... - -session.start(); -ClientMessage msgReceived = consumer.receive(); - -System.out.println("message = " + msgReceived.getBodyBuffer().readString()); - -session.close(); -``` diff --git a/docs/user-manual/en/using-jms.md b/docs/user-manual/en/using-jms.md index bf654b918c4..bc9f3f2729e 100644 --- a/docs/user-manual/en/using-jms.md +++ b/docs/user-manual/en/using-jms.md @@ -1,186 +1,193 @@ # Using JMS -Although Apache ActiveMQ Artemis provides a JMS agnostic messaging API, many users will -be more comfortable using JMS. +Although Apache ActiveMQ Artemis provides a JMS agnostic messaging API, many +users will be more comfortable using JMS. -JMS is a very popular API standard for messaging, and most messaging -systems provide a JMS API. If you are completely new to JMS we suggest -you follow the [Oracle JMS tutorial](https://docs.oracle.com/javaee/7/tutorial/partmessaging.htm) - -a full JMS tutorial is out of scope for this guide. +JMS is a very popular API standard for messaging, and most messaging systems +provide a JMS API. If you are completely new to JMS we suggest you follow the +[Oracle JMS +tutorial](https://docs.oracle.com/javaee/7/tutorial/partmessaging.htm) - a full +JMS tutorial is out of scope for this guide. Apache ActiveMQ Artemis also ships with a wide range of examples, many of which -demonstrate JMS API usage. A good place to start would be to play around -with the simple JMS Queue and Topic example, but we also provide -examples for many other parts of the JMS API. A full description of the -examples is available in [Examples](examples.md). +demonstrate JMS API usage. A good place to start would be to play around with +the simple JMS Queue and Topic example, but we also provide examples for many +other parts of the JMS API. A full description of the examples is available in +[Examples](examples.md). -In this section we'll go through the main steps in configuring the -server for JMS and creating a simple JMS program. We'll also show how to -configure and use JNDI, and also how to use JMS with Apache ActiveMQ Artemis without -using any JNDI. +In this section we'll go through the main steps in configuring the server for +JMS and creating a simple JMS program. We'll also show how to configure and use +JNDI, and also how to use JMS with Apache ActiveMQ Artemis without using any +JNDI. ## A simple ordering system For this chapter we're going to use a very simple ordering system as our -example. It is a somewhat contrived example because of its extreme -simplicity, but it serves to demonstrate the very basics of setting up -and using JMS. - -We will have a single JMS Queue called `OrderQueue`, and we will have a -single `MessageProducer` sending an order message to the queue and a -single `MessageConsumer` consuming the order message from the queue. - -The queue will be a `durable` queue, i.e. it will survive a server -restart or crash. We also want to pre-deploy the queue, i.e. specify the -queue in the server configuration so it is created automatically -without us having to explicitly create it from the client. - -## JNDI Configuration - -The JMS specification establishes the convention that *administered -objects* (i.e. JMS queue, topic and connection factory instances) are -made available via the JNDI API. Brokers are free to implement JNDI as -they see fit assuming the implementation fits the API. Apache ActiveMQ Artemis does not -have a JNDI server. Rather, it uses a client-side JNDI implementation -that relies on special properties set in the environment to construct -the appropriate JMS objects. In other words, no objects are stored in -JNDI on the Apache ActiveMQ Artemis server, instead they are simply instantiated on the -client based on the provided configuration. Let's look at the different -kinds of administered objects and how to configure them. - -> **Note** +example. It is a somewhat contrived example because of its extreme simplicity, +but it serves to demonstrate the very basics of setting up and using JMS. + +We will have a single JMS Queue called `OrderQueue`, and we will have a single +`MessageProducer` sending an order message to the queue and a single +`MessageConsumer` consuming the order message from the queue. + +The queue will be a `durable` queue, i.e. it will survive a server restart or +crash. We also want to pre-deploy the queue, i.e. specify the queue in the +server configuration so it is created automatically without us having to +explicitly create it from the client. + +## JNDI + +The JMS specification establishes the convention that *administered objects* +(i.e. JMS queue, topic and connection factory instances) are made available via +the JNDI API. Brokers are free to implement JNDI as they see fit assuming the +implementation fits the API. Apache ActiveMQ Artemis does not have a JNDI +server. Rather, it uses a client-side JNDI implementation that relies on +special properties set in the environment to construct the appropriate JMS +objects. In other words, no objects are stored in JNDI on the Apache ActiveMQ +Artemis server, instead they are simply instantiated on the client based on the +provided configuration. Let's look at the different kinds of administered +objects and how to configure them. + +> **Note:** > -> The following configuration properties *are strictly required when -> Apache ActiveMQ Artemis is running in stand-alone mode*. When Apache ActiveMQ Artemis is integrated -> to an application server (e.g. Wildfly) the application server itself -> will almost certainly provide a JNDI client with its own properties. +> The following configuration properties *are strictly required when Apache +> ActiveMQ Artemis is running in stand-alone mode*. When Apache ActiveMQ +> Artemis is integrated to an application server (e.g. Wildfly) the application +> server itself will almost certainly provide a JNDI client with its own +> properties. ### ConnectionFactory JNDI -A JMS connection factory is used by the client to make connections to -the server. It knows the location of the server it is connecting to, as -well as many other configuration parameters. +A JMS connection factory is used by the client to make connections to the +server. It knows the location of the server it is connecting to, as well as +many other configuration parameters. -Here's a simple example of the JNDI context environment for a client -looking up a connection factory to access an *embedded* instance of -Apache ActiveMQ Artemis: +Here's a simple example of the JNDI context environment for a client looking up +a connection factory to access an *embedded* instance of Apache ActiveMQ +Artemis: - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - connectionFactory.invmConnectionFactory=vm://0 -In this instance we have created a connection factory that is bound to -`invmConnectionFactory`, any entry with prefix `connectionFactory.` will - create a connection factory. +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.invmConnectionFactory=vm://0 +``` -In certain situations there could be multiple server instances running -within a particular JVM. In that situation each server would typically -have an InVM acceptor with a unique server-ID. A client using JMS and -JNDI can account for this by specifying a connction factory for each -server, like so: +In this instance we have created a connection factory that is bound to +`invmConnectionFactory`, any entry with prefix `connectionFactory.` will create +a connection factory. - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - connectionFactory.invmConnectionFactory0=vm://0 - connectionFactory.invmConnectionFactory1=vm://1 - connectionFactory.invmConnectionFactory2=vm://2 +In certain situations there could be multiple server instances running within a +particular JVM. In that situation each server would typically have an InVM +acceptor with a unique server-ID. A client using JMS and JNDI can account for +this by specifying a connction factory for each server, like so: -Here is a list of all the supported URL schemes: -- `vm` +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.invmConnectionFactory0=vm://0 +connectionFactory.invmConnectionFactory1=vm://1 +connectionFactory.invmConnectionFactory2=vm://2 +``` -- `tcp` +Here is a list of all the supported URL schemes: -- `udp` +- `vm` +- `tcp` +- `udp` +- `jgroups` -- `jgroups` +Most clients won't be connecting to an embedded broker. Clients will most +commonly connect across a network a remote broker. Here's a simple example of a +client configuring a connection factory to connect to a remote broker running +on myhost:5445: -Most clients won't be connecting to an embedded broker. Clients will -most commonly connect across a network a remote broker. Here's a simple -example of a client configuring a connection factory to connect to a -remote broker running on myhost:5445: - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - connectionFactory.ConnectionFactory=tcp://myhost:5445 +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.ConnectionFactory=tcp://myhost:5445 +``` -In the example above the client is using the `tcp` scheme for the -provider URL. A client may also specify multiple comma-delimited -host:port combinations in the URL (e.g. -`(tcp://remote-host1:5445,remote-host2:5445)`). Whether there is one or -many host:port combinations in the URL they are treated as the *initial +In the example above the client is using the `tcp` scheme for the provider URL. +A client may also specify multiple comma-delimited host:port combinations in +the URL (e.g. `(tcp://remote-host1:5445,remote-host2:5445)`). Whether there is +one or many host:port combinations in the URL they are treated as the *initial connector(s)* for the underlying connection. -The `udp` scheme is also supported which should use a host:port -combination that matches the `group-address` and `group-port` from the -corresponding `broadcast-group` configured on the ActiveMQ Artemis server(s). +The `udp` scheme is also supported which should use a host:port combination +that matches the `group-address` and `group-port` from the corresponding +`broadcast-group` configured on the ActiveMQ Artemis server(s). Each scheme has a specific set of properties which can be set using the traditional URL query string format (e.g. -`scheme://host:port?key1=value1&key2=value2`) to customize the -underlying transport mechanism. For example, if a client wanted to -connect to a remote server using TCP and SSL it would create a connection -factory like so, `tcp://remote-host:5445?ssl-enabled=true`. +`scheme://host:port?key1=value1&key2=value2`) to customize the underlying +transport mechanism. For example, if a client wanted to connect to a remote +server using TCP and SSL it would create a connection factory like so, +`tcp://remote-host:5445?ssl-enabled=true`. All the properties available for the `tcp` scheme are described in [the documentation regarding the Netty transport](configuring-transports.md#configuring-the-netty-transport). -Note if you are using the `tcp` scheme and multiple addresses then a query -can be applied to all the url's or just to an individual connector, so where -you have +Note if you are using the `tcp` scheme and multiple addresses then a query can +be applied to all the url's or just to an individual connector, so where you +have -- `(tcp://remote-host1:5445?httpEnabled=true,remote-host2:5445?httpEnabled=true)?clientID=1234` +- `(tcp://remote-host1:5445?httpEnabled=true,remote-host2:5445?httpEnabled=true)?clientID=1234` -then the `httpEnabled` property is only set on the individual connectors where as the `clientId` -is set on the actual connection factory. Any connector specific properties set on the whole -URI will be applied to all the connectors. +then the `httpEnabled` property is only set on the individual connectors where +as the `clientId` is set on the actual connection factory. Any connector +specific properties set on the whole URI will be applied to all the connectors. The `udp` scheme supports 4 properties: -- `localAddress` - If you are running with multiple network - interfaces on the same machine, you may want to specify that the - discovery group listens only only a specific interface. To do this - you can specify the interface address with this parameter. - -- `localPort` - If you want to specify a local port to which the - datagram socket is bound you can specify it here. Normally you would - just use the default value of -1 which signifies that an anonymous - port should be used. This parameter is always specified in - conjunction with `localAddress`. - -- `refreshTimeout` - This is the period the discovery group waits - after receiving the last broadcast from a particular server before - removing that servers connector pair entry from its list. You would - normally set this to a value significantly higher than the - broadcast-period on the broadcast group otherwise servers might - intermittently disappear from the list even though they are still - broadcasting due to slight differences in timing. This parameter is - optional, the default value is 10000 milliseconds (10 seconds). - -- `discoveryInitialWaitTimeout` - If the connection factory is used - immediately after creation then it may not have had enough time to - received broadcasts from all the nodes in the cluster. On first - usage, the connection factory will make sure it waits this long - since creation before creating the first connection. The default - value for this parameter is 10000 milliseconds. - -Lastly, the `jgroups` scheme is supported which provides an alternative -to the `udp` scheme for server discovery. The URL pattern is either +- `localAddress` - If you are running with multiple network + interfaces on the same machine, you may want to specify that the + discovery group listens only only a specific interface. To do this + you can specify the interface address with this parameter. + +- `localPort` - If you want to specify a local port to which the + datagram socket is bound you can specify it here. Normally you would + just use the default value of -1 which signifies that an anonymous + port should be used. This parameter is always specified in + conjunction with `localAddress`. + +- `refreshTimeout` - This is the period the discovery group waits after + receiving the last broadcast from a particular server before removing that + servers connector pair entry from its list. You would normally set this to a + value significantly higher than the broadcast-period on the broadcast group + otherwise servers might intermittently disappear from the list even though they + are still broadcasting due to slight differences in timing. This parameter is + optional, the default value is 10000 milliseconds (10 seconds). + +- `discoveryInitialWaitTimeout` - If the connection factory is used immediately + after creation then it may not have had enough time to received broadcasts + from all the nodes in the cluster. On first usage, the connection factory will + make sure it waits this long since creation before creating the first + connection. The default value for this parameter is 10000 milliseconds. + +Lastly, the `jgroups` scheme is supported which provides an alternative to the +`udp` scheme for server discovery. The URL pattern is either `jgroups://channelName?file=jgroups-xml-conf-filename` -where`jgroups-xml-conf-filename` refers to an XML file on the classpath -that contains the JGroups configuration or it can be -`jgroups://channelName?properties=some-jgroups-properties`. In both instance the -`channelName` is the name given to the jgroups channel created. +where`jgroups-xml-conf-filename` refers to an XML file on the classpath that +contains the JGroups configuration or it can be +`jgroups://channelName?properties=some-jgroups-properties`. In both instance +the `channelName` is the name given to the jgroups channel created. + +The `refreshTimeout` and `discoveryInitialWaitTimeout` properties are supported +just like with `udp`. -The `refreshTimeout` and `discoveryInitialWaitTimeout` properties -are supported just like with `udp`. +The default type for the default connection factory is of type +`javax.jms.ConnectionFactory`. This can be changed by setting the type like so -The default type for the default connection factory is of type `javax.jms.ConnectionFactory`. -This can be changed by setting the type like so - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - java.naming.provider.url=tcp://localhost:5445?type=CF +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +java.naming.provider.url=tcp://localhost:5445?type=CF +``` -In this example it is still set to the default, below shows a list of types that can be set. +In this example it is still set to the default, below shows a list of types +that can be set. #### Configuration for Connection Factory Types @@ -195,42 +202,46 @@ TOPIC_XA_CF | javax.jms.XATopicConnectionFactory ### Destination JNDI -JMS destinations are also typically looked up via JNDI. As with -connection factories, destinations can be configured using special -properties in the JNDI context environment. The property *name* should -follow the pattern: `queue.` or `topic.`. -The property *value* should be the name of the queue hosted by the -Apache ActiveMQ Artemis server. For example, if the server had a JMS queue configured -like so: +JMS destinations are also typically looked up via JNDI. As with connection +factories, destinations can be configured using special properties in the JNDI +context environment. The property *name* should follow the pattern: +`queue.` or `topic.`. The property *value* should +be the name of the queue hosted by the Apache ActiveMQ Artemis server. For +example, if the server had a JMS queue configured like so: ```xml - +
+ +
``` -And if the client wanted to bind this queue to "queues/OrderQueue" then -the JNDI properties would be configured like so: +And if the client wanted to bind this queue to "queues/OrderQueue" then the +JNDI properties would be configured like so: - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - java.naming.provider.url=tcp://myhost:5445 - queue.queues/OrderQueue=OrderQueue +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +java.naming.provider.url=tcp://myhost:5445 +queue.queues/OrderQueue=OrderQueue +``` -It is also possible to look-up JMS destinations which haven't been -configured explicitly in the JNDI context environment. This is possible -using `dynamicQueues/` or `dynamicTopics/` in the look-up string. For -example, if the client wanted to look-up the aforementioned "OrderQueue" -it could do so simply by using the string "dynamicQueues/OrderQueue". -Note, the text that follows `dynamicQueues/` or `dynamicTopics/` must -correspond *exactly* to the name of the destination on the server. +It is also possible to look-up JMS destinations which haven't been configured +explicitly in the JNDI context environment. This is possible using +`dynamicQueues/` or `dynamicTopics/` in the look-up string. For example, if the +client wanted to look-up the aforementioned "OrderQueue" it could do so simply +by using the string "dynamicQueues/OrderQueue". Note, the text that follows +`dynamicQueues/` or `dynamicTopics/` must correspond *exactly* to the name of +the destination on the server. ### The code Here's the code for the example: -First we'll create a JNDI initial context from which to lookup our JMS -objects. If the above properties are set in `jndi.properties` and it is -on the classpath then any new, empty `InitialContext` will be -initialized using those properties: -```java +First we'll create a JNDI initial context from which to lookup our JMS objects. +If the above properties are set in `jndi.properties` and it is on the classpath +then any new, empty `InitialContext` will be initialized using those +properties: + +```java InitialContext ic = new InitialContext(); //Now we'll look up the connection factory from which we can create @@ -280,35 +291,36 @@ see the examples directory in the distribution. > **Warning** > -> Please note that JMS connections, sessions, producers and consumers -> are *designed to be re-used*. +> Please note that JMS connections, sessions, producers and consumers are +> *designed to be re-used*. > -> It is an anti-pattern to create new connections, sessions, producers -> and consumers for each message you produce or consume. If you do this, -> your application will perform very poorly. This is discussed further -> in the section on performance tuning [Performance Tuning](perf-tuning.md). +> It is an anti-pattern to create new connections, sessions, producers and +> consumers for each message you produce or consume. If you do this, your +> application will perform very poorly. This is discussed further in the +> section on performance tuning [Performance Tuning](perf-tuning.md). ## Directly instantiating JMS Resources without using JNDI -Although it is a very common JMS usage pattern to lookup JMS -*Administered Objects* (that's JMS Queue, Topic and ConnectionFactory -instances) from JNDI, in some cases you just think "Why do I need JNDI? -Why can't I just instantiate these objects directly?" +Although it is a very common JMS usage pattern to lookup JMS *Administered +Objects* (that's JMS Queue, Topic and ConnectionFactory instances) from JNDI, +in some cases you just think "Why do I need JNDI? Why can't I just instantiate +these objects directly?" -With Apache ActiveMQ Artemis you can do exactly that. Apache ActiveMQ Artemis supports the direct -instantiation of JMS Queue, Topic and ConnectionFactory instances, so -you don't have to use JNDI at all. +With Apache ActiveMQ Artemis you can do exactly that. Apache ActiveMQ Artemis +supports the direct instantiation of JMS Queue, Topic and ConnectionFactory +instances, so you don't have to use JNDI at all. ->For a full working example of direct instantiation please look at the ->"Instantiate JMS Objects Directly" example under the JMS section of the ->examples. See the [Examples](examples.md) section for more info. +> For a full working example of direct instantiation please look at the +> [Instantiate JMS Objects +> Directly](examples.md#instantiate-jms-objects-directly) example under the JMS +> section of the examples. Here's our simple example, rewritten to not use JNDI at all: -We create the JMS ConnectionFactory object via the ActiveMQJMSClient -Utility class, note we need to provide connection parameters and specify -which transport we are using, for more information on connectors please -see [Configuring the Transport](configuring-transports.md). +We create the JMS ConnectionFactory object via the ActiveMQJMSClient Utility +class, note we need to provide connection parameters and specify which +transport we are using, for more information on connectors please see +[Configuring the Transport](configuring-transports.md). ```java TransportConfiguration transportConfiguration = new TransportConfiguration(NettyConnectorFactory.class.getName()); @@ -355,35 +367,33 @@ System.out.println("Got order: " + receivedMessage.getText()); ## Setting The Client ID -This represents the client id for a JMS client and is needed for -creating durable subscriptions. It is possible to configure this on the -connection factory and can be set via the `clientId` element. Any -connection created by this connection factory will have this set as its -client id. +This represents the client id for a JMS client and is needed for creating +durable subscriptions. It is possible to configure this on the connection +factory and can be set via the `clientId` element. Any connection created by +this connection factory will have this set as its client id. ## Setting The Batch Size for DUPS_OK -When the JMS acknowledge mode is set to `DUPS_OK` it is possible to -configure the consumer so that it sends acknowledgements in batches -rather that one at a time, saving valuable bandwidth. This can be -configured via the connection factory via the `dupsOkBatchSize` -element and is set in bytes. The default is 1024 \* 1024 bytes = 1 MiB. +When the JMS acknowledge mode is set to `DUPS_OK` it is possible to configure +the consumer so that it sends acknowledgements in batches rather that one at a +time, saving valuable bandwidth. This can be configured via the connection +factory via the `dupsOkBatchSize` element and is set in bytes. The default is +1024 \* 1024 bytes = 1 MiB. ## Setting The Transaction Batch Size When receiving messages in a transaction it is possible to configure the -consumer to send acknowledgements in batches rather than individually -saving valuable bandwidth. This can be configured on the connection -factory via the `transactionBatchSize` element and is set in bytes. -The default is 1024 \* 1024. +consumer to send acknowledgements in batches rather than individually saving +valuable bandwidth. This can be configured on the connection factory via the +`transactionBatchSize` element and is set in bytes. The default is 1024 \* +1024. ## Setting The Destination Cache -Many frameworks such as Spring resolve the destination by name on every operation, -this can cause a performance issue and extra calls to the broker, -in a scenario where destinations (addresses) are permanent broker side, -such as they are managed by a platform or operations team. -using `destinationCache` element, you can toggle on the destination cache -to improve the performance and reduce the calls to the broker. -This should not be used if destinations (addresses) are not permanent broker side, -as in dynamic creation/deletion. +Many frameworks such as Spring resolve the destination by name on every +operation, this can cause a performance issue and extra calls to the broker, in +a scenario where destinations (addresses) are permanent broker side, such as +they are managed by a platform or operations team. using `destinationCache` +element, you can toggle on the destination cache to improve the performance and +reduce the calls to the broker. This should not be used if destinations +(addresses) are not permanent broker side, as in dynamic creation/deletion. diff --git a/docs/user-manual/en/using-server.md b/docs/user-manual/en/using-server.md index dea76930717..1e7b7436ae2 100644 --- a/docs/user-manual/en/using-server.md +++ b/docs/user-manual/en/using-server.md @@ -1,20 +1,22 @@ # Using the Server -This chapter will familiarise you with how to use the Apache ActiveMQ Artemis server. +This chapter will familiarise you with how to use the Apache ActiveMQ Artemis +server. We'll show where it is, how to start and stop it, and we'll describe the directory layout and what all the files are and what they do. -For the remainder of this chapter when we talk about the Apache ActiveMQ Artemis server -we mean the Apache ActiveMQ Artemis standalone server, in its default configuration -with a JMS Service enabled. +For the remainder of this chapter when we talk about the Apache ActiveMQ +Artemis server we mean the Apache ActiveMQ Artemis standalone server, in its +default configuration with a JMS Service enabled. This document will refer to the full path of the directory where the ActiveMQ distribution has been extracted to as `${ARTEMIS_HOME}` directory. ## Installation -After downloading the distribution, the following highlights some important folders on the distribution: +After downloading the distribution, the following highlights some important +folders on the distribution: |___ bin | @@ -36,48 +38,52 @@ After downloading the distribution, the following highlights some important fold |___ user-manual -- `bin` - binaries and scripts needed to run ActiveMQ Artemis. +- `bin` - binaries and scripts needed to run ActiveMQ Artemis. -- `examples` - All manner of examples. Please refer to the [examples](examples.md) - chapter for details on how to run them. +- `examples` - All manner of examples. Please refer to the [examples](examples.md) + chapter for details on how to run them. -- `lib` - jars and libraries needed to run ActiveMQ Artemis +- `lib` - jars and libraries needed to run ActiveMQ Artemis -- `schema` - XML Schemas used to validate ActiveMQ Artemis configuration files +- `schema` - XML Schemas used to validate ActiveMQ Artemis configuration files -- `web` - The folder where the web context is loaded when the broker runs. +- `web` - The folder where the web context is loaded when the broker runs. -- `api` - The api documentation is placed under the web folder. +- `api` - The api documentation is placed under the web folder. -- `user-manual` - The user manual is placed under the web folder. +- `user-manual` - The user manual is placed under the web folder. ## Creating a Broker Instance A broker instance is the directory containing all the configuration and runtime -data, such as logs and data files, associated with a broker process. It is recommended that -you do *not* create the instance directory under `${ARTEMIS_HOME}`. This separation is -encouraged so that you can more easily upgrade when the next version of ActiveMQ Artemis is released. - -On Unix systems, it is a common convention to store this kind of runtime data under -the `/var/lib` directory. For example, to create an instance at '/var/lib/mybroker', run -the following commands in your command line shell: - - cd /var/lib - ${ARTEMIS_HOME}/bin/artemis create mybroker +data, such as logs and data files, associated with a broker process. It is +recommended that you do *not* create the instance directory under +`${ARTEMIS_HOME}`. This separation is encouraged so that you can more easily +upgrade when the next version of ActiveMQ Artemis is released. + +On Unix systems, it is a common convention to store this kind of runtime data +under the `/var/lib` directory. For example, to create an instance at +'/var/lib/mybroker', run the following commands in your command line shell: + +```sh +cd /var/lib +${ARTEMIS_HOME}/bin/artemis create mybroker +``` A broker instance directory will contain the following sub directories: - * `bin`: holds execution scripts associated with this instance. - * `etc`: hold the instance configuration files - * `data`: holds the data files used for storing persistent messages - * `log`: holds rotating log files - * `tmp`: holds temporary files that are safe to delete between broker runs +- `bin`: holds execution scripts associated with this instance. +- `etc`: hold the instance configuration files +- `data`: holds the data files used for storing persistent messages +- `log`: holds rotating log files +- `tmp`: holds temporary files that are safe to delete between broker runs -At this point you may want to adjust the default configuration located in -the `etc` directory. +At this point you may want to adjust the default configuration located in the +`etc` directory. ### Options + There are several options you can use when creating an instance. For a full list of updated properties always use: @@ -288,35 +294,35 @@ For a full list of updated properties always use: Path must be writable. ``` - -Some of these properties may be mandatory in certain configurations and the system may ask you for additional input. +Some of these properties may be mandatory in certain configurations and the +system may ask you for additional input. ``` - ./artemis create /usr/server - Creating ActiveMQ Artemis instance at: /user/server +./artemis create /usr/server +Creating ActiveMQ Artemis instance at: /user/server - --user: is a mandatory property! - Please provide the default username: - admin +--user: is a mandatory property! +Please provide the default username: +admin - --password: is mandatory with this configuration: - Please provide the default password: +--password: is mandatory with this configuration: +Please provide the default password: - --allow-anonymous | --require-login: is a mandatory property! - Allow anonymous access?, valid values are Y,N,True,False - y +--allow-anonymous | --require-login: is a mandatory property! +Allow anonymous access?, valid values are Y,N,True,False +y - Auto tuning journal ... - done! Your system can make 0.34 writes per millisecond, your journal-buffer-timeout will be 2956000 +Auto tuning journal ... +done! Your system can make 0.34 writes per millisecond, your journal-buffer-timeout will be 2956000 - You can now start the broker by executing: +You can now start the broker by executing: - "/user/server/bin/artemis" run + "/user/server/bin/artemis" run - Or you can run the broker in the background using: +Or you can run the broker in the background using: - "/user/server/bin/artemis-service" start + "/user/server/bin/artemis-service" start ``` @@ -325,22 +331,26 @@ Some of these properties may be mandatory in certain configurations and the syst Assuming you created the broker instance under `/var/lib/mybroker` all you need to do start running the broker instance is execute: - /var/lib/mybroker/bin/artemis run +```sh +/var/lib/mybroker/bin/artemis run +``` Now that the broker is running, you can optionally run some of the included examples to verify the the broker is running properly. -To stop the Apache ActiveMQ Artemis instance you will use the same `artemis` script, but with -the `stop argument`. Example: +To stop the Apache ActiveMQ Artemis instance you will use the same `artemis` +script, but with the `stop` argument. Example: - /var/lib/mybroker/bin/artemis stop +```sh +/var/lib/mybroker/bin/artemis stop +``` -Please note that Apache ActiveMQ Artemis requires a Java 7 or later runtime to run. +Please note that Apache ActiveMQ Artemis requires a Java 7 or later runtime to +run. -By default the `etc/bootstrap.xml` configuration is -used. The configuration can be changed e.g. by running -`./artemis run -- xml:path/to/bootstrap.xml` or another -config of your choosing. +By default the `etc/bootstrap.xml` configuration is used. The configuration can +be changed e.g. by running `./artemis run -- xml:path/to/bootstrap.xml` or +another config of your choosing. Environment variables are used to provide ease of changing ports, hosts and data directories used and can be found in `etc/artemis.profile` on linux and @@ -348,65 +358,64 @@ data directories used and can be found in `etc/artemis.profile` on linux and ## Server JVM settings -The run scripts set some JVM settings for tuning the garbage collection -policy and heap size. We recommend using a parallel garbage collection -algorithm to smooth out latency and minimise large GC pauses. +The run scripts set some JVM settings for tuning the garbage collection policy +and heap size. We recommend using a parallel garbage collection algorithm to +smooth out latency and minimise large GC pauses. -By default Apache ActiveMQ Artemis runs in a maximum of 1GiB of RAM. To increase the -memory settings change the `-Xms` and `-Xmx` memory settings as you -would for any Java program. +By default Apache ActiveMQ Artemis runs in a maximum of 1GiB of RAM. To +increase the memory settings change the `-Xms` and `-Xmx` memory settings as +you would for any Java program. -If you wish to add any more JVM arguments or tune the existing ones, the -run scripts are the place to do it. +If you wish to add any more JVM arguments or tune the existing ones, the run +scripts are the place to do it. ## Library Path -If you're using the [Asynchronous IO Journal](libaio.md) on Linux, -you need to specify `java.library.path` as a property on your Java -options. This is done automatically in the scripts. +If you're using the [Asynchronous IO Journal](libaio.md) on Linux, you need to +specify `java.library.path` as a property on your Java options. This is done +automatically in the scripts. -If you don't specify `java.library.path` at your Java options then the -JVM will use the environment variable `LD_LIBRARY_PATH`. +If you don't specify `java.library.path` at your Java options then the JVM will +use the environment variable `LD_LIBRARY_PATH`. -You will need to make sure libaio is installed on Linux. For more information refer to the libaio chapter at -[Runtime Dependencies](libaio.html#runtime-dependencies) +You will need to make sure libaio is installed on Linux. For more information +refer to the [libaio chapter](libaio.html#runtime-dependencies). ## System properties -Apache ActiveMQ Artemis can take a system property on the command line for configuring -logging. +Apache ActiveMQ Artemis can take a system property on the command line for +configuring logging. For more information on configuring logging, please see the section on [Logging](logging.md). ## Configuration files -The configuration file used to bootstrap the server (e.g. -`bootstrap.xml` by default) references the specific broker configuration -files. +The configuration file used to bootstrap the server (e.g. `bootstrap.xml` by +default) references the specific broker configuration files. -- `broker.xml`. This is the main ActiveMQ - configuration file. All the parameters in this file are - described [here](configuration-index.md) +- `broker.xml`. This is the main ActiveMQ configuration file. All the + parameters in this file are described [here](configuration-index.md) It is also possible to use system property substitution in all the -configuration files. by replacing a value with the name of a system -property. Here is an example of this with a connector configuration: +configuration files. by replacing a value with the name of a system property. +Here is an example of this with a connector configuration: - tcp://${activemq.remoting.netty.host:localhost}:${activemq.remoting.netty.port:61616} +```xml +tcp://${activemq.remoting.netty.host:localhost}:${activemq.remoting.netty.port:61616} +``` Here you can see we have replaced 2 values with system properties -`activemq.remoting.netty.host` and `activemq.remoting.netty.port`. These -values will be replaced by the value found in the system property if -there is one, if not they default back to localhost or 61616 -respectively. It is also possible to not supply a default. i.e. -`${activemq.remoting.netty.host}`, however the system property *must* be -supplied in that case. +`activemq.remoting.netty.host` and `activemq.remoting.netty.port`. These values +will be replaced by the value found in the system property if there is one, if +not they default back to localhost or 61616 respectively. It is also possible +to not supply a default. i.e. `${activemq.remoting.netty.host}`, however the +system property *must* be supplied in that case. ### Bootstrap configuration file -The stand-alone server is basically a set of POJOs which are -instantiated by Airline commands. +The stand-alone server is basically a set of POJOs which are instantiated by +Airline commands. The bootstrap file is very simple. Let's take a look at an example: @@ -425,48 +434,47 @@ The bootstrap file is very simple. Let's take a look at an example: ``` +- `server` - Instantiates a core server using the configuration file from the + `configuration` attribute. This is the main broker POJO necessary to do all + the real messaging work. -- `server` - Instantiates a core server using the configuration file from the - `configuration` attribute. This is the main broker POJO necessary to - do all the real messaging work. +- `jaas-security` - Configures security for the server. The `domain` attribute + refers to the relevant login module entry in `login.config`. -- `jaas-security` - Configures security for the server. The `domain` attribute - refers to the relevant login module entry in `login.config`. - -- `web` - Configures an embedded Jetty instance to serve web applications like - the admin console. +- `web` - Configures an embedded Jetty instance to serve web applications like + the admin console. ### Broker configuration file The configuration for the Apache ActiveMQ Artemis core server is contained in -`broker.xml`. This is what the FileConfiguration bean -uses to configure the messaging server. +`broker.xml`. This is what the FileConfiguration bean uses to configure the +messaging server. -There are many attributes which you can configure Apache ActiveMQ Artemis. In most -cases the defaults will do fine, in fact every attribute can be -defaulted which means a file with a single empty `configuration` element -is a valid configuration file. The different configuration will be -explained throughout the manual or you can refer to the configuration -reference [here](configuration-index.md). +There are many attributes which you can configure Apache ActiveMQ Artemis. In +most cases the defaults will do fine, in fact every attribute can be defaulted +which means a file with a single empty `configuration` element is a valid +configuration file. The different configuration will be explained throughout +the manual or you can refer to the configuration reference +[here](configuration-index.md). ## Windows Server -On windows you will have the option to run ActiveMQ Artemis as a service. -Just use the following command to install it: +On windows you will have the option to run ActiveMQ Artemis as a service. Just +use the following command to install it: ``` $ ./artemis-service.exe install ``` - -The create process should give you a hint of the available commands available for the artemis-service.exe +The create process should give you a hint of the available commands available +for the artemis-service.exe ## Adding Runtime Dependencies Runtime dependencies like diverts, transformers, broker plugins, JDBC drivers, -password decoders, etc. must be accessible by the broker at runtime. Package +password decoders, etc. must be accessible by the broker at runtime. Package the dependency in a jar, and put it on the broker's classpath. This can be done -by placing the jar file in the `lib` directory of the broker distribution itself -or in the `lib` directory of the broker instance. A broker instance does not have -a `lib` directory by default so it may need to be created. It should be on the -"top" level with the `bin`, `data`, `log`, etc. directories. \ No newline at end of file +by placing the jar file in the `lib` directory of the broker distribution +itself or in the `lib` directory of the broker instance. A broker instance does +not have a `lib` directory by default so it may need to be created. It should +be on the "top" level with the `bin`, `data`, `log`, etc. directories. diff --git a/docs/user-manual/en/versions.md b/docs/user-manual/en/versions.md index 526fa30efba..9bb33d6387e 100644 --- a/docs/user-manual/en/versions.md +++ b/docs/user-manual/en/versions.md @@ -4,9 +4,9 @@ This chapter provides the information for each release: - A link to the full release notes which includes all issues resolved in the release. - A brief list of "highlights." - If necessary, specific steps required when upgrading from the previous version. - - _NOTE:_ If the upgrade spans multiple versions then the steps from each version need to be followed in order. - - _NOTE:_ Follow the general upgrade procedure outlined in the [Upgrading the Broker](upgrading.md) - chapter in addition to any version-specific upgrade instructions. + - **Note:** If the upgrade spans multiple versions then the steps from **each** version need to be followed in order. + - **Note:** Follow the general upgrade procedure outlined in the [Upgrading the Broker](upgrading.md) + chapter in addition to any version-specific upgrade instructions outlined here. ## 2.5.0 @@ -58,7 +58,7 @@ Highlights: ```xml ``` - _NOTE:_ the Jolokia REST interface URL will now be at `http://:/console/jolokia` + **Note:** the Jolokia REST interface URL will now be at `http://:/console/jolokia` ## 2.3.0 diff --git a/docs/user-manual/en/wildcard-routing.md b/docs/user-manual/en/wildcard-routing.md index 96540f35684..0db77485432 100644 --- a/docs/user-manual/en/wildcard-routing.md +++ b/docs/user-manual/en/wildcard-routing.md @@ -8,7 +8,7 @@ receive any messages sent to addresses that match this, for instance you create a consumer on this queue, this allows a consumer to consume messages which are sent to a *hierarchy* of addresses. -> **Note** +> **Note:** > > In JMS terminology this allows "topic hierarchies" to be created. diff --git a/examples/features/standard/spring-integration/src/main/resources/broker.xml b/examples/features/standard/spring-integration/src/main/resources/broker.xml index 9d703b3ce99..505b37fee79 100644 --- a/examples/features/standard/spring-integration/src/main/resources/broker.xml +++ b/examples/features/standard/spring-integration/src/main/resources/broker.xml @@ -26,11 +26,10 @@ under the License. vm://0 - - - + + @@ -39,13 +38,5 @@ under the License. - - -
- - - -
-
diff --git a/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml b/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml index 7db39ea9bca..07545fecac2 100644 --- a/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml +++ b/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml @@ -49,16 +49,11 @@ under the License. - + - - - - - - + From de0747a9a40f44c309287a54f324cc9a21f31b93 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Fri, 8 Jun 2018 16:27:42 -0400 Subject: [PATCH 023/207] ARTEMIS-1920 AMQP throw NPE if can't find a backup server --- .../amqp/broker/AMQPConnectionCallback.java | 4 +- .../core/postoffice/impl/BindingsImpl.java | 2 +- .../ProtocolsMessageLoadBalancingTest.java | 85 +++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallback.java index 05b4f4f7051..84fdd24aa67 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallback.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallback.java @@ -291,7 +291,9 @@ public URI getFailoverList() { ClusterConnection clusterConnection = clusterManager.getDefaultConnection(null); if (clusterConnection != null) { TopologyMemberImpl member = clusterConnection.getTopology().getMember(server.getNodeID().toString()); - return member.toBackupURI(); + if (member != null) { + return member.toBackupURI(); + } } return null; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/BindingsImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/BindingsImpl.java index 478c700b4be..c669ebace1c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/BindingsImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/BindingsImpl.java @@ -258,7 +258,7 @@ private void route(final Message message, if (entry.getValue() instanceof RemoteQueueBinding) { RemoteQueueBinding remoteQueueBinding = (RemoteQueueBinding) entry.getValue(); if (remoteQueueBinding.getRemoteQueueID() == id) { - message.putBytesProperty(Message.HDR_ROUTE_TO_IDS, ByteBuffer.allocate(8).putLong(remoteQueueBinding.getID()).array()); + message.putExtraBytesProperty(Message.HDR_ROUTE_TO_IDS, ByteBuffer.allocate(8).putLong(remoteQueueBinding.getID()).array()); } } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java index 1c981575085..8ed685cf362 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.Collection; +import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; @@ -37,7 +38,9 @@ import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; +import org.apache.activemq.artemis.junit.Wait; import org.apache.activemq.artemis.protocol.amqp.broker.ProtonProtocolManagerFactory; +import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.tests.integration.cluster.distribution.ClusterTestBase; import org.apache.qpid.jms.JmsConnectionFactory; import org.junit.Assert; @@ -282,6 +285,88 @@ public void testExpireRedistributed() throws Exception { connection.close(); } + @Test + public void testRestartConnection() throws Exception { + + startServers(MessageLoadBalancingType.STRICT); + + System.out.println("connections " + servers[1].getRemotingService().getConnections().size()); + + Wait.assertEquals(3, () -> servers[1].getRemotingService().getConnections().size()); + Wait.assertEquals(3, () -> servers[0].getRemotingService().getConnections().size()); + + RemotingConnection[] connectionsServer1 = servers[1].getRemotingService().getConnections().toArray(new RemotingConnection[3]); + RemotingConnection[] connectionsServer0 = servers[0].getRemotingService().getConnections().toArray(new RemotingConnection[3]); + + ConnectionFactory[] factory = new ConnectionFactory[NUMBER_OF_SERVERS]; + Connection[] connection = new Connection[NUMBER_OF_SERVERS]; + Session[] session = new Session[NUMBER_OF_SERVERS]; + MessageConsumer[] consumer = new MessageConsumer[NUMBER_OF_SERVERS]; + + // this will pre create consumers to make sure messages are distributed evenly without redistribution + for (int node = 0; node < NUMBER_OF_SERVERS; node++) { + factory[node] = getJmsConnectionFactory(node); + connection[node] = factory[node].createConnection(); + session[node] = connection[node].createSession(false, Session.AUTO_ACKNOWLEDGE); + consumer[node] = session[node].createConsumer(session[node].createQueue(queueName.toString())); + } + + waitForBindings(0, "queues.0", 1, 1, true); + waitForBindings(1, "queues.0", 1, 1, true); + + waitForBindings(0, "queues.0", 1, 1, false); + waitForBindings(1, "queues.0", 1, 1, false); + + for (RemotingConnection remotingConnection : servers[1].getRemotingService().getConnections()) { + remotingConnection.fail(new ActiveMQException("forcing failure")); + } + for (RemotingConnection remotingConnection : servers[1].getRemotingService().getConnections()) { + remotingConnection.fail(new ActiveMQException("forcing failure")); + } + + // this is to allow reconnects + Thread.sleep(500); + + // this will pre create consumers to make sure messages are distributed evenly without redistribution + for (int node = 0; node < NUMBER_OF_SERVERS; node++) { + try { + connection[node].close(); + } catch (Throwable e) { + e.printStackTrace(); + } + factory[node] = getJmsConnectionFactory(node); + connection[node] = factory[node].createConnection(); + session[node] = connection[node].createSession(false, Session.AUTO_ACKNOWLEDGE); + consumer[node] = session[node].createConsumer(session[node].createQueue(queueName.toString())); + } + + waitForBindings(0, "queues.0", 1, 1, true); + waitForBindings(1, "queues.0", 1, 1, true); + + waitForBindings(0, "queues.0", 1, 1, false); + waitForBindings(1, "queues.0", 1, 1, false); + + System.out.println("connections " + servers[1].getRemotingService().getConnections().size()); + + // sending Messages.. they should be load balanced + { + ConnectionFactory cf = getJmsConnectionFactory(0); + Connection cn = cf.createConnection(); + Session sn = cn.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer pd = sn.createProducer(sn.createQueue(queueName.toString())); + + for (int i = 0; i < NUMBER_OF_MESSAGES; i++) { + pd.send(sn.createTextMessage("hello " + i)); + } + + cn.close(); + } + + receiveMessages(connection[0], consumer[0], NUMBER_OF_MESSAGES / 2, true); + receiveMessages(connection[1], consumer[1], NUMBER_OF_MESSAGES / 2, true); + + } + private void receiveMessages(Connection connection, MessageConsumer messageConsumer, int messageCount, From cb793e0e9827535fc3ce1c66b4ffbc41f513e9f5 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Mon, 11 Jun 2018 17:11:25 -0400 Subject: [PATCH 024/207] ARTEMIS-1924 Add amqpIdleTimeout --- .../amqp/broker/ProtonProtocolManager.java | 28 ++++++- .../amqp/proton/AMQPConnectionContext.java | 7 +- .../amqp/proton/handler/ProtonHandler.java | 17 +++- docs/user-manual/en/amqp.md | 29 +++++++ ... => AmqpBrokerRequestedHearbeatsTest.java} | 30 ++++++- .../integration/amqp/AmqpNoHearbeatsTest.java | 83 +++++++++++++++++++ 6 files changed, 185 insertions(+), 9 deletions(-) rename tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/{AmqpBrokerReuqestedHearbeatsTest.java => AmqpBrokerRequestedHearbeatsTest.java} (85%) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/ProtonProtocolManager.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/ProtonProtocolManager.java index c1a92e01029..d86dc81826b 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/ProtonProtocolManager.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/ProtonProtocolManager.java @@ -45,12 +45,15 @@ import org.apache.activemq.artemis.spi.core.remoting.Connection; import io.netty.channel.ChannelPipeline; +import org.jboss.logging.Logger; /** * A proton protocol manager, basically reads the Proton Input and maps proton resources to ActiveMQ Artemis resources */ public class ProtonProtocolManager extends AbstractProtocolManager implements NotificationListener { + private static final Logger logger = Logger.getLogger(ProtonProtocolManager.class); + private static final List websocketRegistryNames = Arrays.asList("amqp"); private final List incomingInterceptors = new ArrayList<>(); @@ -72,6 +75,9 @@ public class ProtonProtocolManager extends AbstractProtocolManager flushExecutor.execute(() -> { + this.readyListener = () -> this.flushExecutor.execute(() -> { flush(); }); this.creationTime = System.currentTimeMillis(); @@ -105,8 +105,17 @@ public ProtonHandler(Executor flushExecutor, boolean isServer) { connection.collect(collector); } - public long tick(boolean firstTick) { - lock.lock(); + public Long tick(boolean firstTick) { + if (firstTick) { + // the first tick needs to guarantee a lock here + lock.lock(); + } else { + if (!lock.tryLock()) { + log.debug("Cannot hold a lock on ProtonHandler for Tick, it will retry shortly"); + // if we can't lock the scheduler will retry in a very short period of time instead of holding the lock here + return null; + } + } try { if (!firstTick) { try { @@ -122,7 +131,7 @@ public long tick(boolean firstTick) { transport.close(); connection.setCondition(new ErrorCondition()); } - return 0; + return 0L; } return transport.tick(TimeUnit.NANOSECONDS.toMillis(System.nanoTime())); } finally { diff --git a/docs/user-manual/en/amqp.md b/docs/user-manual/en/amqp.md index a201fbd998b..30cb2aa081f 100644 --- a/docs/user-manual/en/amqp.md +++ b/docs/user-manual/en/amqp.md @@ -127,3 +127,32 @@ message for later delivery: If both annotations are present in the same message then the broker will prefer the more specific `x-opt-delivery-time` value. + +## Configuring AMQP Idle Timeout + +It is possible to configure the AMQP Server's IDLE Timeout by setting the property amqpIdleTimeout in milliseconds on the acceptor. + +This will make the server to send an AMQP frame open to the client, with your configured timeout / 2. + +So, if you configured your AMQP Idle Timeout to be 60000, the server will tell the client to send frames every 30,000 milliseconds. + + +```xml +.... ;amqpIdleTimeout=; ..... +``` + + +### Disabling Keep alive checks + +if you set amqpIdleTimeout=0 that will tell clients to not sending keep alive packets towards the server. On this case +you will rely on TCP to determine when the socket needs to be closed. + +```xml +.... ;amqpIdleTimeout=0; ..... +``` + +This contains a real example for configuring amqpIdleTimeout: + +```xml +tcp://0.0.0.0:5672?amqpIdleTimeout=0;tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=AMQP;useEpoll=true;amqpCredits=1000;amqpMinCredits=300;directDeliver=false;batchDelay=10 +``` \ No newline at end of file diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpBrokerReuqestedHearbeatsTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpBrokerRequestedHearbeatsTest.java similarity index 85% rename from tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpBrokerReuqestedHearbeatsTest.java rename to tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpBrokerRequestedHearbeatsTest.java index 8221ef6d0f4..fcc7acdd33a 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpBrokerReuqestedHearbeatsTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpBrokerRequestedHearbeatsTest.java @@ -16,6 +16,9 @@ */ package org.apache.activemq.artemis.tests.integration.amqp; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -27,18 +30,41 @@ import org.apache.activemq.transport.amqp.client.AmqpValidator; import org.apache.qpid.proton.engine.Connection; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; /** * Test handling of heartbeats requested by the broker. */ -public class AmqpBrokerReuqestedHearbeatsTest extends AmqpClientTestSupport { +@RunWith(Parameterized.class) +public class AmqpBrokerRequestedHearbeatsTest extends AmqpClientTestSupport { private final int TEST_IDLE_TIMEOUT = 1000; + @Parameterized.Parameters(name = "useOverride={0}") + public static Collection parameters() { + return Arrays.asList(new Object[][] { + {true}, {false} + }); + } + + @Parameterized.Parameter(0) + public boolean useOverride; + + @Override + protected void configureAMQPAcceptorParameters(Map params) { + if (!useOverride) { + params.put("amqpIdleTimeout", "" + TEST_IDLE_TIMEOUT); + } + } + + @Override protected void addConfiguration(ActiveMQServer server) { server.getConfiguration().setConnectionTtlCheckInterval(TEST_IDLE_TIMEOUT / 3); - server.getConfiguration().setConnectionTTLOverride(TEST_IDLE_TIMEOUT); + if (useOverride) { + server.getConfiguration().setConnectionTTLOverride(TEST_IDLE_TIMEOUT); + } } @Test(timeout = 60000) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java new file mode 100644 index 00000000000..2d5b3cf599c --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java @@ -0,0 +1,83 @@ +/* + * 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.artemis.tests.integration.amqp; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; + +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.transport.amqp.client.AmqpClient; +import org.apache.activemq.transport.amqp.client.AmqpConnection; +import org.apache.activemq.transport.amqp.client.AmqpValidator; +import org.apache.qpid.proton.engine.Connection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class AmqpNoHearbeatsTest extends AmqpClientTestSupport { + + @Parameterized.Parameters(name = "useOverride={0}") + public static Collection parameters() { + return Arrays.asList(new Object[][] { + {true}, {false} + }); + } + + @Parameterized.Parameter(0) + public boolean useOverride; + + @Override + protected void addConfiguration(ActiveMQServer server) { + if (useOverride) { + server.getConfiguration().setConnectionTTLOverride(0); + } else { + server.getConfiguration().setConnectionTtlCheckInterval(500); + } + } + + + @Override + protected void configureAMQPAcceptorParameters(Map params) { + if (!useOverride) { + params.put("amqpIdleTimeout", "0"); + } + } + + + @Test(timeout = 60000) + public void testBrokerSendsHalfConfiguredIdleTimeout() throws Exception { + AmqpClient client = createAmqpClient(); + assertNotNull(client); + + client.setValidator(new AmqpValidator() { + + @Override + public void inspectOpenedResource(Connection connection) { + assertEquals("idle timeout was not disabled", 0, connection.getTransport().getRemoteIdleTimeout()); + } + }); + + AmqpConnection connection = addConnection(client.connect()); + assertNotNull(connection); + + connection.getStateInspector().assertValid(); + connection.close(); + } + +} From 42fa84fa51d4d7b3dea62a23058a0a79d3c2f205 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Mon, 11 Jun 2018 18:41:57 -0400 Subject: [PATCH 025/207] ARTEMIS-1924 Renaming test to a proper name --- .../artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java index 2d5b3cf599c..b35ef7d34db 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java @@ -61,7 +61,7 @@ protected void configureAMQPAcceptorParameters(Map params) { @Test(timeout = 60000) - public void testBrokerSendsHalfConfiguredIdleTimeout() throws Exception { + public void testHeartless() throws Exception { AmqpClient client = createAmqpClient(); assertNotNull(client); From f752268a3338bc7445bda05794a21c7c9582cebc Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Mon, 11 Jun 2018 10:32:38 -0500 Subject: [PATCH 026/207] ARTEMIS-1922 clarify doc on updating xincluded files --- docs/user-manual/en/configuration-index.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/user-manual/en/configuration-index.md b/docs/user-manual/en/configuration-index.md index 0064a776387..69714d3180f 100644 --- a/docs/user-manual/en/configuration-index.md +++ b/docs/user-manual/en/configuration-index.md @@ -46,6 +46,15 @@ For further information on XInclude see: [https://www.w3.org/TR/xinclude/](https://www.w3.org/TR/xinclude/) +##### Reloading modular configuration files + +Certain changes in `broker.xml` can be picked up at runtime as discussed in the [Configuration Reload](config-reload.md) +chapter. Changes made directly to files which are included in `broker.xml` via `xi:include` will not be automatically +picked up unless the file timestamp on `broker.xml` is also modified. For example, if `broker.xml` is including +`my-address-settings.xml` and `my-address-settings.xml` is modified those changes won't be loaded until the user uses +something like the [touch](https://en.wikipedia.org/wiki/Touch_%28Unix%29) command to update the `broker.xml` file's +timestamp to trigger a reload. + ### System properties It is possible to use System properties to replace some of the configuration properties. If you define a System property starting with "brokerconfig." that will be passed along to Bean Utils and the configuration would be replaced. From 89f6350c1c730eee1faf2a699d1e88f452e5147f Mon Sep 17 00:00:00 2001 From: Keith Wall Date: Mon, 11 Jun 2018 08:48:50 +0100 Subject: [PATCH 027/207] ARTEMIS-1893: Treat the detach of a link with expiry policy of link-detatch as if closed flag were true. --- .../amqp/proton/AMQPConnectionContext.java | 21 +++-- .../amqp/AmqpNonDurableReceiverTest.java | 81 +++++++++++++++++++ 2 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNonDurableReceiverTest.java diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/AMQPConnectionContext.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/AMQPConnectionContext.java index 4612a975559..35521064e8f 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/AMQPConnectionContext.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/AMQPConnectionContext.java @@ -46,6 +46,8 @@ import org.apache.activemq.artemis.utils.ByteUtil; import org.apache.activemq.artemis.utils.VersionLoader; import org.apache.qpid.proton.amqp.Symbol; +import org.apache.qpid.proton.amqp.messaging.Source; +import org.apache.qpid.proton.amqp.messaging.TerminusExpiryPolicy; import org.apache.qpid.proton.amqp.transaction.Coordinator; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.Connection; @@ -485,12 +487,19 @@ public void onRemoteClose(Link link) throws Exception { @Override public void onRemoteDetach(Link link) throws Exception { - lock(); - try { - link.detach(); - link.free(); - } finally { - unlock(); + boolean handleAsClose = link.getSource() != null + && ((Source) link.getSource()).getExpiryPolicy() == TerminusExpiryPolicy.LINK_DETACH; + + if (handleAsClose) { + onRemoteClose(link); + } else { + lock(); + try { + link.detach(); + link.free(); + } finally { + unlock(); + } } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNonDurableReceiverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNonDurableReceiverTest.java new file mode 100644 index 00000000000..69f8b531c1f --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNonDurableReceiverTest.java @@ -0,0 +1,81 @@ +/* + * 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.artemis.tests.integration.amqp; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.transport.amqp.client.AmqpClient; +import org.apache.activemq.transport.amqp.client.AmqpConnection; +import org.apache.activemq.transport.amqp.client.AmqpReceiver; +import org.apache.activemq.transport.amqp.client.AmqpSession; +import org.apache.activemq.transport.amqp.client.AmqpValidator; +import org.junit.Test; + +import org.apache.qpid.proton.amqp.messaging.Terminus; +import org.apache.qpid.proton.amqp.messaging.TerminusExpiryPolicy; +import org.apache.qpid.proton.engine.Receiver; + +public class AmqpNonDurableReceiverTest extends AmqpClientTestSupport { + + @Test(timeout = 60000) + public void testLinkDetachReleasesResources() throws Exception { + + AmqpClient client = createAmqpClient(); + AmqpConnection connection = addConnection(client.createConnection()); + connection.connect(); + + AmqpSession session = connection.createSession(); + + SimpleString simpleTopicName = SimpleString.toSimpleString(getTopicName()); + final int bindingsBefore = server.getPostOffice().getBindingsForAddress(simpleTopicName).getBindings().size(); + + + AmqpReceiver receiver = session.createReceiver(getTopicName()); + + AtomicBoolean remoteLinkClosed = new AtomicBoolean(); + assertEquals("Unexpected source expiry policy", TerminusExpiryPolicy.LINK_DETACH, + ((Terminus) receiver.getEndpoint().getSource()).getExpiryPolicy()); + + receiver.setStateInspector(new AmqpValidator() { + @Override + public void inspectDetachedResource(final Receiver receiver) { + super.inspectDetachedResource(receiver); + fail("Remote link detached in unexpected manner"); + } + + @Override + public void inspectClosedResource(final Receiver receiver) { + super.inspectClosedResource(receiver); + remoteLinkClosed.set(true); + } + }); + + assertEquals("Unexpected number of bindings before attach", + bindingsBefore + 1, server.getPostOffice().getBindingsForAddress(simpleTopicName).getBindings().size()); + + receiver.detach(); + + assertEquals("Unexpected number of bindings after detach", + bindingsBefore, + server.getPostOffice().getBindingsForAddress(simpleTopicName).getBindings().size()); + + assertTrue("Remote link was not closed", remoteLinkClosed.get()); + + receiver.getStateInspector().assertValid(); + } +} From 1d5128839f0cffe32cdd85032310665450792837 Mon Sep 17 00:00:00 2001 From: yang wei Date: Mon, 11 Jun 2018 19:27:00 +0800 Subject: [PATCH 028/207] ARTEMIS-1923 fix constructing binding journalimpl bug Clebert Suconic ammended this commit by adding a test and getter properties to satisfy the test This closes #2133 --- .../journal/impl/JournalFilesRepository.java | 4 + .../core/journal/impl/JournalImpl.java | 11 ++ .../impl/journal/JournalStorageManager.java | 2 +- .../JournalStorageManagerConstantTest.java | 167 ++++++++++++++++++ 4 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 artemis-server/src/test/java/org/apache/activemq/artemis/core/server/journal/JournalStorageManagerConstantTest.java diff --git a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalFilesRepository.java b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalFilesRepository.java index 488aa1f91d3..5896fba68e6 100644 --- a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalFilesRepository.java +++ b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalFilesRepository.java @@ -130,6 +130,10 @@ public JournalFilesRepository(final SequentialFileFactory fileFactory, // Public -------------------------------------------------------- + public int getPoolSize() { + return poolSize; + } + public void setExecutor(final Executor fileExecutor) { this.openFilesExecutor = fileExecutor; } diff --git a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java index 849dd3e4fc7..55b92c5470d 100644 --- a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java +++ b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java @@ -215,6 +215,17 @@ public class JournalImpl extends JournalBase implements TestableJournal, Journal private final Reclaimer reclaimer = new Reclaimer(); + public float getCompactPercentage() { + return compactPercentage; + } + + public int getCompactMinFiles() { + return compactMinFiles; + } + + public JournalFilesRepository getFilesRepository() { + return filesRepository; + } // Constructors -------------------------------------------------- public JournalImpl(final int fileSize, diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java index 9199bbee975..dd8bb22a7e1 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java @@ -129,7 +129,7 @@ protected void init(Configuration config, IOCriticalErrorListener criticalErrorL bindingsFF = new NIOSequentialFileFactory(config.getBindingsLocation(), criticalErrorListener, config.getJournalMaxIO_NIO()); bindingsFF.setDatasync(config.isJournalDatasync()); - Journal localBindings = new JournalImpl(ioExecutorFactory, 1024 * 1024, 2, config.getJournalCompactMinFiles(), config.getJournalPoolFiles(), config.getJournalCompactPercentage(), config.getJournalFileOpenTimeout(), bindingsFF, "activemq-bindings", "bindings", 1, 0, criticalErrorListener); + Journal localBindings = new JournalImpl(ioExecutorFactory, 1024 * 1024, 2, config.getJournalPoolFiles(), config.getJournalCompactMinFiles(), config.getJournalCompactPercentage(), config.getJournalFileOpenTimeout(), bindingsFF, "activemq-bindings", "bindings", 1, 0, criticalErrorListener); bindingsJournal = localBindings; originalBindingsJournal = localBindings; diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/journal/JournalStorageManagerConstantTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/journal/JournalStorageManagerConstantTest.java new file mode 100644 index 00000000000..ee66fd16d85 --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/journal/JournalStorageManagerConstantTest.java @@ -0,0 +1,167 @@ +/* + * 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.artemis.core.server.journal; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.journal.impl.JournalImpl; +import org.apache.activemq.artemis.core.persistence.impl.journal.JournalStorageManager; +import org.apache.activemq.artemis.core.server.JournalType; +import org.apache.activemq.artemis.utils.ExecutorFactory; +import org.apache.activemq.artemis.utils.actors.ArtemisExecutor; +import org.junit.Assert; +import org.junit.Test; + +public class JournalStorageManagerConstantTest { + + @Test + public void testConstant() { + Configuration configuration = new ConfigurationImpl(); + configuration.setJournalType(JournalType.NIO); + configuration.setJournalPoolFiles(11); + configuration.setJournalCompactMinFiles(22); + configuration.setJournalCompactPercentage(33); + JournalStorageManager journalStorageManager = new JournalStorageManager(configuration, null, dumbExecutor, dumbScheduler, dumbExecutor); + + JournalImpl journal = (JournalImpl)journalStorageManager.getBindingsJournal(); + assertJournalConstants(journal); + journal = (JournalImpl)journalStorageManager.getMessageJournal(); + assertJournalConstants(journal); + + } + + private void assertJournalConstants(JournalImpl journal) { + Assert.assertEquals("0.33", "" + journal.getCompactPercentage()); + Assert.assertEquals(22, journal.getCompactMinFiles()); + Assert.assertEquals(11, journal.getFilesRepository().getPoolSize()); + } + + ExecutorFactory dumbExecutor = new ExecutorFactory() { + @Override + public ArtemisExecutor getExecutor() { + return new ArtemisExecutor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }; + } + }; + + ScheduledExecutorService dumbScheduler = new ScheduledExecutorService() { + @Override + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + return null; + } + + @Override + public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { + return null; + } + + @Override + public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { + return null; + } + + @Override + public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { + return null; + } + + @Override + public void shutdown() { + + } + + @Override + public List shutdownNow() { + return null; + } + + @Override + public boolean isShutdown() { + return false; + } + + @Override + public boolean isTerminated() { + return false; + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return false; + } + + @Override + public Future submit(Callable task) { + return null; + } + + @Override + public Future submit(Runnable task, T result) { + return null; + } + + @Override + public Future submit(Runnable task) { + return null; + } + + @Override + public List> invokeAll(Collection> tasks) throws InterruptedException { + return null; + } + + @Override + public List> invokeAll(Collection> tasks, + long timeout, + TimeUnit unit) throws InterruptedException { + return null; + } + + @Override + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { + return null; + } + + @Override + public T invokeAny(Collection> tasks, + long timeout, + TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return null; + } + + @Override + public void execute(Runnable command) { + + } + }; + +} From 88833c790af1459990bc4203414966afd7438e02 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 12 Jun 2018 16:29:13 -0400 Subject: [PATCH 029/207] ARTEMIS-1927 treat exceptions and disconnect properly I couldn't do this with a test as this is dependent on windows. Regular testsuite should be sufficient as long as you run in Windows. --- .../impl/netty/ActiveMQChannelHandler.java | 3 +-- .../remoting/server/impl/RemotingServiceImpl.java | 15 ++++++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/ActiveMQChannelHandler.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/ActiveMQChannelHandler.java index d0d5c0e9924..de8b49ef560 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/ActiveMQChannelHandler.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/ActiveMQChannelHandler.java @@ -26,7 +26,6 @@ import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.core.buffers.impl.ChannelBufferWrapper; import org.apache.activemq.artemis.core.client.ActiveMQClientLogger; -import org.apache.activemq.artemis.core.client.ActiveMQClientMessageBundle; import org.apache.activemq.artemis.spi.core.remoting.BaseConnectionLifeCycleListener; import org.apache.activemq.artemis.spi.core.remoting.BufferHandler; @@ -99,7 +98,7 @@ public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cau // and we don't want to spew out stack traces in that event // The user has access to this exeception anyway via the ActiveMQException initial cause - ActiveMQException me = ActiveMQClientMessageBundle.BUNDLE.nettyError(); + ActiveMQException me = new ActiveMQException(cause.getMessage()); me.initCause(cause); synchronized (listener) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/server/impl/RemotingServiceImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/server/impl/RemotingServiceImpl.java index 17351d9af8a..5a1ddefae38 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/server/impl/RemotingServiceImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/server/impl/RemotingServiceImpl.java @@ -539,6 +539,10 @@ public void connectionDestroyed(final Object connectionID) { logger.trace("Connection removed " + connectionID + " from server " + this.server, new Exception("trace")); } + issueFailure(connectionID, new ActiveMQRemoteDisconnectException()); + } + + private void issueFailure(Object connectionID, ActiveMQException e) { ConnectionEntry conn = connections.get(connectionID); if (conn != null && !conn.connection.isSupportReconnect()) { @@ -554,20 +558,13 @@ public void connectionDestroyed(final Object connectionID) { return; } } - conn.connection.fail(new ActiveMQRemoteDisconnectException()); + conn.connection.fail(e); } } @Override public void connectionException(final Object connectionID, final ActiveMQException me) { - // We DO NOT call fail on connection exception, otherwise in event of real connection failure, the - // connection will be failed, the session will be closed and won't be able to reconnect - - // E.g. if live server fails, then this handler wil be called on backup server for the server - // side replicating connection. - // If the connection fail() is called then the sessions on the backup will get closed. - - // Connections should only fail when TTL is exceeded + issueFailure(connectionID, me); } @Override From ed095a0523f58b305e90952ab2782fcf87a1602a Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Tue, 12 Jun 2018 10:34:31 -0500 Subject: [PATCH 030/207] NO-JIRA fix JMS destination cache doc --- docs/user-manual/en/perf-tuning.md | 2 +- docs/user-manual/en/using-jms.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user-manual/en/perf-tuning.md b/docs/user-manual/en/perf-tuning.md index 3b2d37a64ad..19c2595a14b 100644 --- a/docs/user-manual/en/perf-tuning.md +++ b/docs/user-manual/en/perf-tuning.md @@ -139,7 +139,7 @@ some tuning: then you can avoid some unnecessary copying. - If using frameworks like Spring, configure destinations permanently broker - side and enable `destinationCache` on the client side. See the [Setting The + side and enable `cacheDestinations` on the client side. See the [Setting The Destination Cache](using-jms.md) for more information on this. ## Tuning Transport Settings diff --git a/docs/user-manual/en/using-jms.md b/docs/user-manual/en/using-jms.md index bc9f3f2729e..26342663f9a 100644 --- a/docs/user-manual/en/using-jms.md +++ b/docs/user-manual/en/using-jms.md @@ -393,7 +393,7 @@ valuable bandwidth. This can be configured on the connection factory via the Many frameworks such as Spring resolve the destination by name on every operation, this can cause a performance issue and extra calls to the broker, in a scenario where destinations (addresses) are permanent broker side, such as -they are managed by a platform or operations team. using `destinationCache` +they are managed by a platform or operations team. using `cacheDestinations` element, you can toggle on the destination cache to improve the performance and reduce the calls to the broker. This should not be used if destinations (addresses) are not permanent broker side, as in dynamic creation/deletion. From 07a3248563e504d4184f652dcc73bc75134037e1 Mon Sep 17 00:00:00 2001 From: Shailendra Kumar Singh Date: Fri, 15 Jun 2018 15:33:03 +0530 Subject: [PATCH 031/207] [ARTEMIS-1936]getQueueNames(String routingType) method should be mark as Operation instead of Attribute --- .../api/core/management/ActiveMQServerControl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java index 473a9edecc0..9e8c9dfee6e 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java @@ -395,12 +395,6 @@ public interface ActiveMQServerControl { @Attribute(desc = "Names of the queues created on this server") String[] getQueueNames(); - /** - * Returns the names of the queues created on this server with the given routing-type. - */ - @Attribute(desc = "Names of the queues created on this server with the given routing-type (i.e. ANYCAST or MULTICAST)") - String[] getQueueNames(String routingType); - /** * Returns the uptime of this server. */ @@ -1246,5 +1240,11 @@ String listAddresses(@Parameter(name = "Options") String options, String listQueues(@Parameter(name = "Options") String options, @Parameter(name = "Page Number") int page, @Parameter(name = "Page Size") int pageSize) throws Exception; + + /** + * Returns the names of the queues created on this server with the given routing-type. + */ + @Operation(desc = "Names of the queues created on this server with the given routing-type (i.e. ANYCAST or MULTICAST)", impact = MBeanOperationInfo.INFO) + String[] getQueueNames(@Parameter(name = "routingType", desc = "The routing type, MULTICAST or ANYCAST") String routingType); } From 39871157b40f8ec1c6e1efc37a83ec1f9892e030 Mon Sep 17 00:00:00 2001 From: Benjamin Graf Date: Thu, 14 Jun 2018 20:34:04 +0200 Subject: [PATCH 032/207] ARTEMIS-1935: Close of openwire connection should closes all open sessions --- .../protocol/openwire/OpenWireConnection.java | 14 +++++--------- .../openwire/SessionHandlingOpenWireTest.java | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java index 225aac4aeea..bafce92cb9b 100644 --- a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java +++ b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java @@ -183,12 +183,6 @@ public class OpenWireConnection extends AbstractRemotingConnection implements Se */ private ServerSession internalSession; - /** - * Used for proper closing of internal sessions like OpenWire advisory - * session at disconnect. - */ - private final Set internalSessionIds = new ConcurrentHashSet<>(); - private final OperationContext operationContext; private static final AtomicLongFieldUpdater LAST_SENT_UPDATER = AtomicLongFieldUpdater.newUpdater(OpenWireConnection.class, "lastSent"); @@ -616,8 +610,11 @@ private void disconnect(ActiveMQException me, String reason, boolean fail) { state.shutdown(); try { - for (SessionId sessionId : internalSessionIds) { - sessions.get(sessionId).close(); + for (SessionId sessionId : sessionIdMap.values()) { + AMQSession session = sessions.get(sessionId); + if (session != null) { + session.close(); + } } internalSession.close(false); } catch (Exception e) { @@ -993,7 +990,6 @@ public void onSlowConsumer(ServerConsumer consumer) { public void addSessions(Set sessionSet) { for (SessionId sid : sessionSet) { addSession(getState().getSessionState(sid).getInfo(), true); - internalSessionIds.add(sid); } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SessionHandlingOpenWireTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SessionHandlingOpenWireTest.java index 9c476a9f885..989b62d5f04 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SessionHandlingOpenWireTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SessionHandlingOpenWireTest.java @@ -56,4 +56,19 @@ public void testInternalSessionHandling() throws Exception { assertFalse(AssertionLoggerHandler.findText("Client connection failed, clearing up resources for session")); assertFalse(AssertionLoggerHandler.findText("Cleared up resources for session")); } + + @Test + public void testInternalSessionHandlingNoSessionClose() throws Exception { + try (Connection conn = factory.createConnection()) { + conn.start(); + Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination dest = createDestination(session,ActiveMQDestination.QUEUE_TYPE); + sendMessages(session, dest, 1); + MessageConsumer consumer = session.createConsumer(dest); + Message m = consumer.receive(2000); + assertNotNull(m); + } + assertFalse(AssertionLoggerHandler.findText("Client connection failed, clearing up resources for session")); + assertFalse(AssertionLoggerHandler.findText("Cleared up resources for session")); + } } From b360fa6063b4060ad91a478d3b46dba557d9a72b Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Fri, 15 Jun 2018 10:32:39 -0400 Subject: [PATCH 033/207] ARTEMIS-1935: Close of openwire connection should closes all open sessions This is my ammending to the last commit I'm adding some extra verifications to the test and I fixed a leak that already existed on the previous map --- .../protocol/openwire/OpenWireConnection.java | 1 + .../openwire/SessionHandlingOpenWireTest.java | 22 ++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java index bafce92cb9b..b5adf346ff1 100644 --- a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java +++ b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java @@ -1013,6 +1013,7 @@ public AMQSession addSession(SessionInfo ss, boolean internal) { public void removeSession(AMQConnectionContext context, SessionInfo info) throws Exception { AMQSession session = sessions.remove(info.getSessionId()); if (session != null) { + sessionIdMap.remove(session.getCoreSession().getName()); session.close(); } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SessionHandlingOpenWireTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SessionHandlingOpenWireTest.java index 989b62d5f04..c43d5f7cd29 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SessionHandlingOpenWireTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SessionHandlingOpenWireTest.java @@ -61,12 +61,22 @@ public void testInternalSessionHandling() throws Exception { public void testInternalSessionHandlingNoSessionClose() throws Exception { try (Connection conn = factory.createConnection()) { conn.start(); - Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - Destination dest = createDestination(session,ActiveMQDestination.QUEUE_TYPE); - sendMessages(session, dest, 1); - MessageConsumer consumer = session.createConsumer(dest); - Message m = consumer.receive(2000); - assertNotNull(m); + for (int i = 0; i < 100; i++) { + Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination dest = createDestination(session,ActiveMQDestination.QUEUE_TYPE); + sendMessages(session, dest, 1); + MessageConsumer consumer = session.createConsumer(dest); + Message m = consumer.receive(2000); + consumer.close(); + assertNotNull(m); + + if (i % 2 == 1) { + // it will close only half of the sessions + // just to introduce error conditions + session.close(); + } + + } } assertFalse(AssertionLoggerHandler.findText("Client connection failed, clearing up resources for session")); assertFalse(AssertionLoggerHandler.findText("Cleared up resources for session")); From 9a3442f0bd29d77e02eca2f57b4e3adfec5b2f75 Mon Sep 17 00:00:00 2001 From: Robbie Gemmell Date: Fri, 15 Jun 2018 20:27:28 +0100 Subject: [PATCH 034/207] ARTEMIS-1934: fix handling/accounting of sent amqp connection data updates max frame size tests to verify behaviour seen with standalone brokers rather than non represenative test-only conditions, as well as more closely validate the recieved messages --- .../apache/activemq/artemis/junit/Wait.java | 6 +- .../amqp/proton/handler/ProtonHandler.java | 4 +- .../transport/amqp/client/AmqpConnection.java | 7 +- .../amqp/AmqpMaxFrameSizeTest.java | 236 +++++++++++++++--- 4 files changed, 221 insertions(+), 32 deletions(-) diff --git a/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/Wait.java b/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/Wait.java index 679b112dd6d..e37539a3a65 100644 --- a/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/Wait.java +++ b/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/Wait.java @@ -65,7 +65,11 @@ public static void assertEquals(int size, IntCondition condition) throws Excepti } public static void assertEquals(int size, IntCondition condition, long timeout) throws Exception { - boolean result = waitFor(() -> condition.getCount() == size, timeout); + assertEquals(size, condition, timeout, SLEEP_MILLIS); + } + + public static void assertEquals(int size, IntCondition condition, long timeout, long sleepMillis) throws Exception { + boolean result = waitFor(() -> condition.getCount() == size, timeout, sleepMillis); if (!result) { Assert.fail(size + " != " + condition.getCount()); diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java index 11a89d9d838..38ca7a7978f 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java @@ -209,7 +209,8 @@ public void flushBytes() { lock.lock(); try { while (true) { - int pending = transport.pending(); + ByteBuffer head = transport.head(); + int pending = head.remaining(); if (pending <= 0) { break; @@ -217,7 +218,6 @@ public void flushBytes() { // We allocated a Pooled Direct Buffer, that will be sent down the stream ByteBuf buffer = PooledByteBufAllocator.DEFAULT.directBuffer(pending); - ByteBuffer head = transport.head(); buffer.writeBytes(head); for (EventHandler handler : handlers) { diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpConnection.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpConnection.java index c77184fead5..a00f4ae7e99 100644 --- a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpConnection.java +++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpConnection.java @@ -103,6 +103,7 @@ public class AmqpConnection extends AmqpAbstractResource implements private boolean idleProcessingDisabled; private String containerId; private boolean authenticated; + private int maxFrameSize = DEFAULT_MAX_FRAME_SIZE; private int channelMax = DEFAULT_CHANNEL_MAX; private long connectTimeout = DEFAULT_CONNECT_TIMEOUT; private long closeTimeout = DEFAULT_CLOSE_TIMEOUT; @@ -367,7 +368,11 @@ public void setContainerId(String containerId) { * @return the currently set Max Frame Size value. */ public int getMaxFrameSize() { - return DEFAULT_MAX_FRAME_SIZE; + return maxFrameSize; + } + + public void setMaxFrameSize(int maxFrameSize) { + this.maxFrameSize = maxFrameSize; } public int getChannelMax() { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpMaxFrameSizeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpMaxFrameSizeTest.java index de3ada48b82..5931ec83965 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpMaxFrameSizeTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpMaxFrameSizeTest.java @@ -20,7 +20,10 @@ import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; +import org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport; +import org.apache.activemq.artemis.tests.util.Wait; import org.apache.activemq.transport.amqp.client.AmqpClient; import org.apache.activemq.transport.amqp.client.AmqpConnection; import org.apache.activemq.transport.amqp.client.AmqpMessage; @@ -28,22 +31,40 @@ import org.apache.activemq.transport.amqp.client.AmqpSender; import org.apache.activemq.transport.amqp.client.AmqpSession; import org.apache.activemq.transport.amqp.client.AmqpValidator; +import org.apache.qpid.proton.amqp.Binary; import org.apache.qpid.proton.amqp.messaging.Data; import org.apache.qpid.proton.engine.Connection; import org.apache.qpid.proton.message.impl.MessageImpl; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AmqpMaxFrameSizeTest extends AmqpClientTestSupport { - private static final int FRAME_SIZE = 512; + protected static final Logger LOG = LoggerFactory.getLogger(AmqpMaxFrameSizeTest.class); + + private boolean maxFrameSizeConfigSet = false; + private static final int CONFIGURED_FRAME_SIZE = 4321; @Override protected void configureAMQPAcceptorParameters(Map params) { - params.put("maxFrameSize", FRAME_SIZE); + if ("testBrokerAdvertisedConfiguredMaxFrameSize".equals(getTestName())) { + maxFrameSizeConfigSet = true; + params.put("maxFrameSize", CONFIGURED_FRAME_SIZE); + } + } + + @Override + protected void addConfiguration(ActiveMQServer server) { + // Make the journal file size larger than the frame+message sizes used in the tests, + // since it is by default for external brokers and it changes the behaviour. + server.getConfiguration().setJournalFileSize(2 * 1024 * 1024); } @Test(timeout = 60000) - public void testBrokerHonorsSetMaxFrameSize() throws Exception { + public void testBrokerAdvertisedDefaultMaxFrameSize() throws Exception { + assertFalse("maxFrameSize should not be explicitly configured", maxFrameSizeConfigSet); + AmqpClient client = createAmqpClient(); assertNotNull(client); @@ -52,7 +73,7 @@ public void testBrokerHonorsSetMaxFrameSize() throws Exception { @Override public void inspectOpenedResource(Connection connection) { int brokerMaxFrameSize = connection.getTransport().getRemoteMaxFrameSize(); - if (brokerMaxFrameSize != FRAME_SIZE) { + if (brokerMaxFrameSize != AmqpSupport.MAX_FRAME_SIZE_DEFAULT) { markAsInvalid("Broker did not send the expected max Frame Size"); } } @@ -68,56 +89,215 @@ public void inspectOpenedResource(Connection connection) { } @Test(timeout = 60000) - public void testMultipleTransfers() throws Exception { + public void testBrokerAdvertisedConfiguredMaxFrameSize() throws Exception { + assertTrue("maxFrameSize should be explicitly configured", maxFrameSizeConfigSet); + + AmqpClient client = createAmqpClient(); + assertNotNull(client); + + client.setValidator(new AmqpValidator() { + + @Override + public void inspectOpenedResource(Connection connection) { + int brokerMaxFrameSize = connection.getTransport().getRemoteMaxFrameSize(); + if (brokerMaxFrameSize != CONFIGURED_FRAME_SIZE) { + markAsInvalid("Broker did not send the expected max Frame Size"); + } + } + }); + + AmqpConnection connection = addConnection(client.connect()); + try { + assertNotNull(connection); + connection.getStateInspector().assertValid(); + } finally { + connection.close(); + } + } + + @Test(timeout = 60000) + public void testManyMultiFrameTransfersWithClientMaxFrameSizeSmallerThanBrokers() throws Exception { + final int clientMaxFrameSize = 1024; + final int brokerMaxFrameSize = AmqpSupport.MAX_FRAME_SIZE_DEFAULT; + final int messageSize = 2 * AmqpSupport.MAX_FRAME_SIZE_DEFAULT + 5; + + assertTrue("Client maxFrameSize should be smaller than brokers", clientMaxFrameSize < brokerMaxFrameSize); + + doManyMultiFrameTransfersTestImpl(clientMaxFrameSize, messageSize, brokerMaxFrameSize); + } + + @Test(timeout = 60000) + public void testManyMultiFrameTransfersWithClientMaxFrameSizeLargerThanBrokers() throws Exception { + final int clientMaxFrameSize = 2 * AmqpSupport.MAX_FRAME_SIZE_DEFAULT; + final int brokerMaxFrameSize = AmqpSupport.MAX_FRAME_SIZE_DEFAULT; + final int messageSize = 2 * AmqpSupport.MAX_FRAME_SIZE_DEFAULT + 5; + + assertTrue("Client maxFrameSize should be larger than brokers", clientMaxFrameSize > brokerMaxFrameSize); + + doManyMultiFrameTransfersTestImpl(clientMaxFrameSize, messageSize, brokerMaxFrameSize); + } + + private void doManyMultiFrameTransfersTestImpl(int maxFrameSize, int payloadSize, int brokerMaxFrameSize) throws Exception { server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST)); - String testQueueName = "ConnectionFrameSize"; - int nMsgs = 200; + int numMsgs = 200; + String testQueueName = getTestName(); AmqpClient client = createAmqpClient(); - AmqpConnection connection = addConnection(client.connect()); - try { - connection.connect(); + client.setValidator(new AmqpValidator() { + @Override + public void inspectOpenedResource(Connection connection) { + int brokerMaxFrameSize = connection.getTransport().getRemoteMaxFrameSize(); + if (brokerMaxFrameSize != AmqpSupport.MAX_FRAME_SIZE_DEFAULT) { + markAsInvalid("Broker did not send the expected max Frame Size"); + } + } + }); + + AmqpConnection connection = client.createConnection(); + connection.setMaxFrameSize(maxFrameSize); + connection.connect(); + addConnection(connection); + + try { AmqpSession session = connection.createSession(); AmqpSender sender = session.createSender(testQueueName); - final int payload = FRAME_SIZE * 16; - - for (int i = 0; i < nMsgs; ++i) { - AmqpMessage message = createAmqpMessage((byte) 'A', payload); + for (int i = 0; i < numMsgs; ++i) { + AmqpMessage message = createAmqpMessage(payloadSize); sender.send(message); } - int count = getMessageCount(server.getPostOffice(), testQueueName); - assertEquals(nMsgs, count); + Wait.assertEquals(numMsgs, () -> getMessageCount(server.getPostOffice(), testQueueName), 5000, 10); AmqpReceiver receiver = session.createReceiver(testQueueName); - receiver.flow(nMsgs); - - for (int i = 0; i < nMsgs; ++i) { - AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS); - assertNotNull("failed at " + i, message); - MessageImpl wrapped = (MessageImpl) message.getWrappedMessage(); - Data data = (Data) wrapped.getBody(); - System.out.println("received : message: " + data.getValue().getLength()); - assertEquals(payload, data.getValue().getLength()); - message.accept(); + receiver.flow(numMsgs); + + for (int i = 1; i <= numMsgs; ++i) { + AmqpMessage receivedMessage = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull("Did not recieve message " + i, receivedMessage); + + verifyMessage(receivedMessage, payloadSize); + + LOG.trace("received : message " + i); + receivedMessage.accept(); + } + + } finally { + connection.close(); + } + } + + @Test(timeout = 60000) + public void testSingleAndMultiFrameTransferClientMaxFrameSizeSmallerThanBrokers() throws Exception { + final int clientMaxFrameSize = 1024; + final int brokerMaxFrameSize = AmqpSupport.MAX_FRAME_SIZE_DEFAULT; + + assertTrue("Client maxFrameSize should be smaller than brokers", clientMaxFrameSize < brokerMaxFrameSize); + + doSingleAndMultiFrameTransferTestImpl(clientMaxFrameSize, brokerMaxFrameSize); + } + + @Test(timeout = 60000) + public void testSingleAndMultiFrameTransferWithClientMaxFrameSizeLargerThanBrokers() throws Exception { + final int clientMaxFrameSize = 2 * AmqpSupport.MAX_FRAME_SIZE_DEFAULT; + final int brokerMaxFrameSize = AmqpSupport.MAX_FRAME_SIZE_DEFAULT; + + assertTrue("Client maxFrameSize should be larger than brokers", clientMaxFrameSize > brokerMaxFrameSize); + + doSingleAndMultiFrameTransferTestImpl(clientMaxFrameSize, brokerMaxFrameSize); + } + + private void doSingleAndMultiFrameTransferTestImpl(int maxFrameSize, int brokerMaxFrameSize) throws Exception { + final int messageSize1 = 128; + final int messageSize2 = 2 * AmqpSupport.MAX_FRAME_SIZE_DEFAULT + 5; + + assertTrue("messageSize1 should be much smaller than both of the maxFrameSizes", + messageSize1 < maxFrameSize / 2 && messageSize1 < brokerMaxFrameSize / 2); + assertTrue("messageSize2 should be larger than one of the maxFrameSizes", + messageSize2 > maxFrameSize || messageSize2 > brokerMaxFrameSize); + + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST)); + + String testQueueName = getTestName(); + + AmqpClient client = createAmqpClient(); + + client.setValidator(new AmqpValidator() { + @Override + public void inspectOpenedResource(Connection connection) { + int brokerMaxFrameSize = connection.getTransport().getRemoteMaxFrameSize(); + if (brokerMaxFrameSize != AmqpSupport.MAX_FRAME_SIZE_DEFAULT) { + markAsInvalid("Broker did not send the expected max Frame Size"); + } } + }); + + AmqpConnection connection = client.createConnection(); + connection.setMaxFrameSize(maxFrameSize); + + connection.connect(); + addConnection(connection); + + try { + AmqpSession session = connection.createSession(); + AmqpSender sender = session.createSender(testQueueName); + + AmqpMessage message1 = createAmqpMessage(messageSize1); + AmqpMessage message2 = createAmqpMessage(messageSize2); + sender.send(message1); + sender.send(message2); + + Wait.assertEquals(2, () -> getMessageCount(server.getPostOffice(), testQueueName), 5000, 10); + + AmqpReceiver receiver = session.createReceiver(testQueueName); + receiver.flow(2); + + AmqpMessage receivedMessage1 = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull("Did not recieve message 1", receivedMessage1); + verifyMessage(receivedMessage1, messageSize1); + receivedMessage1.accept(); + + AmqpMessage receivedMessage2 = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull("Did not recieve message 2", receivedMessage2); + verifyMessage(receivedMessage2, messageSize2); + receivedMessage2.accept(); } finally { connection.close(); } } - private AmqpMessage createAmqpMessage(byte value, int payloadSize) { + private AmqpMessage createAmqpMessage(final int payloadSize) { AmqpMessage message = new AmqpMessage(); byte[] payload = new byte[payloadSize]; for (int i = 0; i < payload.length; i++) { - payload[i] = value; + // An odd number of digit characters + int offset = i % 7; + payload[i] = (byte) (48 + offset); } message.setBytes(payload); return message; } + + private void verifyMessage(final AmqpMessage message, final int payloadSize) { + MessageImpl wrapped = (MessageImpl) message.getWrappedMessage(); + + assertNotNull("Message has no body", wrapped.getBody()); + assertTrue("Unexpected body type: " + wrapped.getBody().getClass(), wrapped.getBody() instanceof Data); + + Data data = (Data) wrapped.getBody(); + Binary binary = data.getValue(); + assertNotNull("Data section has no content", binary); + assertEquals("Unexpected payload length", payloadSize, binary.getLength()); + + byte[] binaryContent = binary.getArray(); + int offset = binary.getArrayOffset(); + for (int i = 0; i < payloadSize; i++) { + byte expected = (byte) (48 + (i % 7)); + assertEquals("Unexpected content at payload index " + i, expected, binaryContent[i + offset]); + } + } } From 952bf12eeb9a6f2fe3528d46fd748adeb6be5d16 Mon Sep 17 00:00:00 2001 From: Timothy Bish Date: Fri, 15 Jun 2018 17:10:18 -0400 Subject: [PATCH 035/207] ARTEMIS-1938 Update to Qpid JMS 0.33.0 Update to latest release of Qpid JMS --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7249c2dfb3e..221cff985ff 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ 0.27.1 3.0.19.Final 1.7.21 - 0.32.0 + 0.33.0 0.9.5 1.0-alpha-1 1 From ed2a18f1c49bc1f45a7978f01989fd4b253f0693 Mon Sep 17 00:00:00 2001 From: Robbie Gemmell Date: Mon, 18 Jun 2018 19:30:26 +0100 Subject: [PATCH 036/207] ARTEMIS-1940: restore use of copying send in certain edge cases Avoids pooling corner cases interacting with ARTEMIS 1843 + ARTEMIS 1861 improvements. Also tagging ARTEMIS-1941 to note test needs altered along with it. --- .../proton/ProtonServerSenderContext.java | 14 ++- .../transport/amqp/client/AmqpConnection.java | 14 ++- .../amqp/AmqpLargeMessageTest.java | 86 +++++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerSenderContext.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerSenderContext.java index ddd9b39e847..0b40ee21409 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerSenderContext.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerSenderContext.java @@ -689,6 +689,7 @@ public int deliverMessage(MessageReference messageReference, int deliveryCount, byte[] tag = preSettle ? new byte[0] : protonSession.getTag(); // Let the Message decide how to present the message bytes + boolean attemptRelease = true; ReadableBuffer sendBuffer = message.getSendBuffer(deliveryCount); try { @@ -712,7 +713,16 @@ public int deliverMessage(MessageReference messageReference, int deliveryCount, delivery.setMessageFormat((int) message.getMessageFormat()); delivery.setContext(messageReference); - sender.sendNoCopy(sendBuffer); + if (sendBuffer instanceof NettyReadable) { + sender.send(sendBuffer); + // Above send copied, so release now if needed + attemptRelease = false; + ((NettyReadable) sendBuffer).getByteBuf().release(); + } else { + // Don't have pooled content, no need to release or copy. + attemptRelease = false; + sender.sendNoCopy(sendBuffer); + } if (preSettle) { // Presettled means the client implicitly accepts any delivery we send it. @@ -728,7 +738,7 @@ public int deliverMessage(MessageReference messageReference, int deliveryCount, return size; } finally { - if (sendBuffer instanceof NettyReadable) { + if (attemptRelease && sendBuffer instanceof NettyReadable) { ((NettyReadable) sendBuffer).getByteBuf().release(); } } diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpConnection.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpConnection.java index a00f4ae7e99..9181ceab386 100644 --- a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpConnection.java +++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpConnection.java @@ -49,6 +49,7 @@ import org.apache.qpid.proton.engine.Event; import org.apache.qpid.proton.engine.Event.Type; import org.apache.qpid.proton.engine.Sasl; +import org.apache.qpid.proton.engine.Session; import org.apache.qpid.proton.engine.Transport; import org.apache.qpid.proton.engine.impl.CollectorImpl; import org.apache.qpid.proton.engine.impl.TransportImpl; @@ -105,6 +106,7 @@ public class AmqpConnection extends AmqpAbstractResource implements private boolean authenticated; private int maxFrameSize = DEFAULT_MAX_FRAME_SIZE; private int channelMax = DEFAULT_CHANNEL_MAX; + private int sessionIncomingCapacity = 0; private long connectTimeout = DEFAULT_CONNECT_TIMEOUT; private long closeTimeout = DEFAULT_CLOSE_TIMEOUT; private long drainTimeout = DEFAULT_DRAIN_TIMEOUT; @@ -279,7 +281,9 @@ public AmqpSession createSession() throws Exception { @Override public void run() { checkClosed(); - session.setEndpoint(getEndpoint().session()); + Session protonSession = getEndpoint().session(); + protonSession.setIncomingCapacity(sessionIncomingCapacity); + session.setEndpoint(protonSession); session.setStateInspector(getStateInspector()); session.open(request); pumpToProtonTransport(request); @@ -383,6 +387,14 @@ public void setChannelMax(int channelMax) { this.channelMax = channelMax; } + public int getSessionIncomingCapacity() { + return sessionIncomingCapacity; + } + + public void setSessionIncomingCapacity(int capacity) { + this.sessionIncomingCapacity = capacity; + } + public long getConnectTimeout() { return connectTimeout; } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java index 7e80c8ff741..6bd550a27ee 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java @@ -17,6 +17,7 @@ package org.apache.activemq.artemis.tests.integration.amqp; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.Map; import java.util.Random; @@ -39,6 +40,7 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; +import org.apache.activemq.artemis.tests.util.Wait; import org.apache.activemq.transport.amqp.client.AmqpClient; import org.apache.activemq.transport.amqp.client.AmqpConnection; import org.apache.activemq.transport.amqp.client.AmqpMessage; @@ -46,7 +48,10 @@ import org.apache.activemq.transport.amqp.client.AmqpSender; import org.apache.activemq.transport.amqp.client.AmqpSession; import org.apache.qpid.jms.JmsConnectionFactory; +import org.apache.qpid.proton.amqp.Binary; +import org.apache.qpid.proton.amqp.messaging.AmqpValue; import org.apache.qpid.proton.amqp.messaging.Data; +import org.apache.qpid.proton.amqp.messaging.Section; import org.apache.qpid.proton.message.impl.MessageImpl; import org.junit.Assert; import org.junit.Ignore; @@ -65,6 +70,13 @@ public class AmqpLargeMessageTest extends AmqpClientTestSupport { String testQueueName = "ConnectionFrameSize"; + @Override + protected void addConfiguration(ActiveMQServer server) { + // Make the journal file size larger than the frame+message sizes used in the tests, + // since it is by default for external brokers and it changes the behaviour. + server.getConfiguration().setJournalFileSize(5 * 1024 * 1024); + } + @Override protected void configureAMQPAcceptorParameters(Map params) { params.put("maxFrameSize", FRAME_SIZE); @@ -325,6 +337,80 @@ public void doTestSendLargeMessage(int expectedSize) throws Exception { } } + @Test(timeout = 60000) + public void testReceiveRedeliveredLargeMessagesWithSessionFlowControl() throws Exception { + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST)); + + int numMsgs = 10; + int msgSize = 2_000_000; + int maxFrameSize = FRAME_SIZE; // Match the brokers outgoing frame size limit to make window sizing easy + int sessionCapacity = 2_500_000; // Restrict session to 1.x messages in flight at once, make it likely send is partial. + + byte[] payload = createLargePayload(msgSize); + assertEquals(msgSize, payload.length); + + AmqpClient client = createAmqpClient(); + + AmqpConnection connection = client.createConnection(); + connection.setMaxFrameSize(maxFrameSize); + connection.setSessionIncomingCapacity(sessionCapacity); + + connection.connect(); + addConnection(connection); + try { + String testQueueName = getTestName(); + AmqpSession session = connection.createSession(); + AmqpSender sender = session.createSender(testQueueName); + + for (int i = 0; i < numMsgs; ++i) { + AmqpMessage message = new AmqpMessage(); + message.setBytes(payload); + + sender.send(message); + } + + Wait.assertEquals(numMsgs, () -> getMessageCount(server.getPostOffice(), testQueueName), 5000, 10); + + AmqpReceiver receiver = session.createReceiver(testQueueName); + receiver.flow(numMsgs); + + ArrayList messages = new ArrayList<>(); + for (int i = 0; i < numMsgs; ++i) { + AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull("failed at " + i, message); + messages.add(message); + } + + for (int i = 0; i < numMsgs; ++i) { + AmqpMessage msg = messages.get(i); + msg.modified(true, false); + } + + receiver.close(); + + AmqpReceiver receiver2 = session.createReceiver(testQueueName); + receiver2.flow(numMsgs); + for (int i = 0; i < numMsgs; ++i) { + AmqpMessage message = receiver2.receive(5, TimeUnit.SECONDS); + assertNotNull("failed at " + i, message); + + Section body = message.getWrappedMessage().getBody(); + assertNotNull("No message body for msg " + i, body); + + //TODO: ARTEMIS-1941 raised. This is wrong, test sent a Data section, it got converted in transit. + assertTrue("Unexpected message body type for msg " + body.getClass(), body instanceof AmqpValue); + assertEquals("Unexpected body content for msg", new Binary(payload, 0, payload.length), ((AmqpValue) body).getValue()); + + message.accept(); + } + + session.close(); + + } finally { + connection.close(); + } + } + private void sendObjectMessages(int nMsgs, ConnectionFactory factory) throws Exception { try (Connection connection = factory.createConnection()) { Session session = connection.createSession(); From f10c64fc87fbbaa15f1f5e801326845f3e96daa2 Mon Sep 17 00:00:00 2001 From: gtully Date: Fri, 15 Jun 2018 19:55:31 +0100 Subject: [PATCH 037/207] ARTEMIS-1942 replace properties removed in error from internal config map --- .../core/security/jaas/LDAPLoginModule.java | 6 ++- .../security/jaas/LDAPLoginModuleTest.java | 37 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java index 99648c278b0..cc3c824d02e 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java @@ -84,7 +84,6 @@ public class LDAPLoginModule implements LoginModule { private static final String SASL_LOGIN_CONFIG_SCOPE = "saslLoginConfigScope"; private static final String AUTHENTICATE_USER = "authenticateUser"; private static final String REFERRAL = "referral"; - private static final String MASK_PASSWORD = "maskPassword"; private static final String PASSWORD_CODEC = "passwordCodec"; protected DirContext context; @@ -126,6 +125,9 @@ public void initialize(Subject subject, new LDAPLoginProperty(USER_ROLE_NAME, (String) options.get(USER_ROLE_NAME)), new LDAPLoginProperty(EXPAND_ROLES, (String) options.get(EXPAND_ROLES)), new LDAPLoginProperty(EXPAND_ROLES_MATCHING, (String) options.get(EXPAND_ROLES_MATCHING)), + new LDAPLoginProperty(PASSWORD_CODEC, (String) options.get(PASSWORD_CODEC)), + new LDAPLoginProperty(SASL_LOGIN_CONFIG_SCOPE, (String) options.get(SASL_LOGIN_CONFIG_SCOPE)), + new LDAPLoginProperty(AUTHENTICATE_USER, (String) options.get(AUTHENTICATE_USER)), new LDAPLoginProperty(REFERRAL, (String) options.get(REFERRAL))}; if (isLoginPropertySet(AUTHENTICATE_USER)) { @@ -133,7 +135,7 @@ public void initialize(Subject subject, } isRoleAttributeSet = isLoginPropertySet(ROLE_NAME); roleAttributeName = getLDAPPropertyValue(ROLE_NAME); - codecClass = (String) options.get(PASSWORD_CODEC); + codecClass = getLDAPPropertyValue(PASSWORD_CODEC); } private String getPlainPassword(String password) { diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java index 75316e67cd5..4fbd2c8057f 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java @@ -31,12 +31,15 @@ import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler; import org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule; +import org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginProperty; import org.apache.directory.server.annotations.CreateLdapServer; import org.apache.directory.server.annotations.CreateTransport; import org.apache.directory.server.core.annotations.ApplyLdifFiles; @@ -162,4 +165,38 @@ public void testCommitOnFailedLogin() throws LoginException { // since login failed commit should return false as well assertFalse(loginModule.commit()); } + + @Test + public void testPropertyConfigMap() throws Exception { + LDAPLoginModule loginModule = new LDAPLoginModule(); + JaasCallbackHandler callbackHandler = new JaasCallbackHandler(null, null, null); + + Field configMap = null; + HashMap options = new HashMap<>(); + for (Field field: loginModule.getClass().getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && field.getType().isAssignableFrom(String.class)) { + field.setAccessible(true); + options.put((String)field.get(loginModule), "SET"); + } + if (field.getName().equals("config")) { + field.setAccessible(true); + configMap = field; + } + } + loginModule.initialize(new Subject(), callbackHandler, null, options); + + LDAPLoginProperty[] ldapProps = (LDAPLoginProperty[]) configMap.get(loginModule); + for (String key: options.keySet()) { + assertTrue("val set: " + key, presentInArray(ldapProps, key)); + } + } + + private boolean presentInArray(LDAPLoginProperty[] ldapProps, String propertyName) { + for (LDAPLoginProperty conf : ldapProps) { + if (conf.getPropertyName().equals(propertyName) && (conf.getPropertyValue() != null && !"".equals(conf.getPropertyValue()))) + return true; + } + return false; + } + } From 8e9bcf2d60f5e6a04784c4e09f9a95339e5d5fb1 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 17 Nov 2017 12:19:27 -0600 Subject: [PATCH 038/207] ARTEMIS-1931 Support web dependencies in 'create' plugin --- .../artemis/maven/ArtemisCreatePlugin.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java b/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java index 9bbb2a1b26b..05595000b0c 100644 --- a/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java +++ b/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java @@ -127,6 +127,12 @@ public class ArtemisCreatePlugin extends ArtemisAbstractPlugin { @Parameter private String[] libListWithDeps; + @Parameter + private String[] webList; + + @Parameter + private String[] webListWithDeps; + @Parameter(defaultValue = "${localRepository}") private org.apache.maven.artifact.repository.ArtifactRepository localRepository; @@ -261,7 +267,18 @@ protected void doExecute() throws MojoExecutionException, MojoFailureException { commandLineStream.println("# This is a list of files that need to be installed under ./lib."); commandLineStream.println("# We are copying them from your maven lib home"); for (File file : files) { - copyToLib(file, commandLineStream); + copyToDir("lib", file, commandLineStream); + } + } + + files = resolveDependencies(webListWithDeps, webList); + + if (!files.isEmpty()) { + commandLineStream.println(); + commandLineStream.println("# This is a list of files that need to be installed under ./web."); + commandLineStream.println("# We are copying them from your maven lib home"); + for (File file : files) { + copyToDir("web", file, commandLineStream); } } @@ -313,8 +330,8 @@ private String getCommandline(ArrayList listCommands) { return buffer.toString(); } - private void copyToLib(File projectLib, PrintStream commandLineStream) throws IOException { - Path target = instance.toPath().resolve("lib").resolve(projectLib.getName()); + private void copyToDir(String destination, File projectLib, PrintStream commandLineStream) throws IOException { + Path target = instance.toPath().resolve(destination).resolve(projectLib.getName()); File file = target.toFile(); File parent = file.getParentFile(); if (!parent.exists()) { From e2e1034bdb9ddf0bc41b842f13f357f531cea562 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Mon, 18 Jun 2018 15:44:57 -0400 Subject: [PATCH 039/207] ARTEMIS-1931 Support Property replacement on the CLI --- .../artemis/maven/ArtemisCreatePlugin.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java b/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java index 05595000b0c..07981d1bd06 100644 --- a/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java +++ b/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java @@ -20,6 +20,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -58,6 +60,9 @@ public class ArtemisCreatePlugin extends ArtemisAbstractPlugin { @Parameter(defaultValue = "${activemq.basedir}", required = true) private File home; + @Parameter + private String[] replacePairs; + @Parameter(defaultValue = "${activemq.basedir}/artemis-distribution/target/apache-artemis-${project.version}-bin/apache-artemis-${project.version}/", required = true) private File alternateHome; @@ -299,11 +304,26 @@ private void copyConfigurationFiles(String[] list, Path sourcePath, Path targetPath, PrintStream commandLineStream) throws IOException { + boolean hasReplacements = false; + if (replacePairs != null && replacePairs.length > 0) { + hasReplacements = true; + if (replacePairs.length % 2 == 1) { + throw new IllegalArgumentException("You need to pass an even number of replacement pairs"); + } + for (int i = 0; i < replacePairs.length; i += 2) { + commandLineStream.println("# replace " + replacePairs[i] + " by " + replacePairs[i + 1] + " on these files"); + } + } for (String file : list) { Path target = targetPath.resolve(file); Path originalFile = sourcePath.resolve(file); - Files.copy(originalFile, target, StandardCopyOption.REPLACE_EXISTING); + + if (hasReplacements) { + copyWithReplacements(originalFile, target); + } else { + Files.copy(originalFile, target, StandardCopyOption.REPLACE_EXISTING); + } commandLineStream.println(""); @@ -321,6 +341,16 @@ private void copyConfigurationFiles(String[] list, } } + private void copyWithReplacements(Path original, Path target) throws IOException { + Charset charset = StandardCharsets.UTF_8; + + String content = new String(Files.readAllBytes(original), charset); + for (int i = 0; i < replacePairs.length; i += 2) { + content = content.replaceAll(replacePairs[i], replacePairs[i + 1]); + } + Files.write(target, content.getBytes(charset)); + } + private String getCommandline(ArrayList listCommands) { StringBuffer buffer = new StringBuffer(); buffer.append(home.getAbsolutePath() + "/bin/artemis "); From 3b6c0107397e1db208591f2238fadcc9d5b05753 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 17 Nov 2017 12:29:48 -0600 Subject: [PATCH 040/207] ARTEMIS-1931 Add camel example --- docs/user-manual/en/examples.md | 5 + .../standard/camel/camel-broker/pom.xml | 132 ++++++++++++++++++ .../artemis/jms/example/CamelExample.java | 102 ++++++++++++++ .../resources/activemq/server0/bootstrap.xml | 30 ++++ .../features/standard/camel/camel-war/pom.xml | 76 ++++++++++ .../webapp/WEB-INF/applicationContext.xml | 45 ++++++ .../camel-war/src/main/webapp/WEB-INF/web.xml | 27 ++++ examples/features/standard/camel/pom.xml | 43 ++++++ examples/features/standard/camel/readme.md | 15 ++ examples/features/standard/pom.xml | 2 + 10 files changed, 477 insertions(+) create mode 100644 examples/features/standard/camel/camel-broker/pom.xml create mode 100644 examples/features/standard/camel/camel-broker/src/main/java/org/apache/activemq/artemis/jms/example/CamelExample.java create mode 100644 examples/features/standard/camel/camel-broker/src/main/resources/activemq/server0/bootstrap.xml create mode 100644 examples/features/standard/camel/camel-war/pom.xml create mode 100644 examples/features/standard/camel/camel-war/src/main/webapp/WEB-INF/applicationContext.xml create mode 100644 examples/features/standard/camel/camel-war/src/main/webapp/WEB-INF/web.xml create mode 100644 examples/features/standard/camel/pom.xml create mode 100644 examples/features/standard/camel/readme.md diff --git a/docs/user-manual/en/examples.md b/docs/user-manual/en/examples.md index 70d5ee0d8d8..7cd88ff6e99 100644 --- a/docs/user-manual/en/examples.md +++ b/docs/user-manual/en/examples.md @@ -270,6 +270,11 @@ A `QueueBrowser` is used to look at messages on the queue without removing them. It can scan the entire content of a queue or only messages matching a message selector. +## Camel + +The `camel` example demonstrates how to build and deploy a Camel route to the +broker using a web application archive (i.e. `war` file). + ## Client Kickoff The `client-kickoff` example shows how to terminate client connections given an diff --git a/examples/features/standard/camel/camel-broker/pom.xml b/examples/features/standard/camel/camel-broker/pom.xml new file mode 100644 index 00000000000..9e63f698144 --- /dev/null +++ b/examples/features/standard/camel/camel-broker/pom.xml @@ -0,0 +1,132 @@ + + + + + 4.0.0 + + + org.apache.activemq.examples.broker.camel + camel + 2.7.0-SNAPSHOT + + + camel-broker + jar + ActiveMQ Artemis Camel Broker + + + ${project.basedir}/../../../../.. + + + + + org.apache.activemq + artemis-server + ${project.version} + + + org.apache.activemq.examples.broker.camel + camel-war + ${project.version} + war + + + org.apache.geronimo.specs + geronimo-jms_2.0_spec + + + + + + + org.apache.activemq + artemis-maven-plugin + ${project.version} + + + create0 + + create + + + + + org.apache.activemq.examples.broker.camel:camel-war:war:${project.version} + + + WARFILE + camel-war-${project.version}.war + + ${noServer} + ${basedir}/target/server0 + ${basedir}/target/classes/activemq/server0 + + + + start0 + + cli + + + ${noServer} + true + ${basedir}/target/server0 + tcp://localhost:61616 + + run + + server0 + + + + runClient + + runClient + + + org.apache.activemq.artemis.jms.example.CamelExample + + + + stop0 + + cli + + + ${noServer} + ${basedir}/target/server0 + + stop + + + + + + + org.apache.activemq.examples.broker.camel + camel-broker + ${project.version} + + + + + + diff --git a/examples/features/standard/camel/camel-broker/src/main/java/org/apache/activemq/artemis/jms/example/CamelExample.java b/examples/features/standard/camel/camel-broker/src/main/java/org/apache/activemq/artemis/jms/example/CamelExample.java new file mode 100644 index 00000000000..f6a71cc8934 --- /dev/null +++ b/examples/features/standard/camel/camel-broker/src/main/java/org/apache/activemq/artemis/jms/example/CamelExample.java @@ -0,0 +1,102 @@ +/* + * 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.artemis.jms.example; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.naming.InitialContext; +import java.util.Hashtable; + +public class CamelExample { + + public static void main(final String[] args) throws Exception { + Connection connection = null; + + InitialContext ic = null; + + try { + // Step 1. - we create an initial context for looking up JNDI + + Hashtable properties = new Hashtable<>(); + properties.put("java.naming.factory.initial", "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory"); + properties.put("connectionFactory.ConnectionFactory", "tcp://127.0.0.1:61616"); + properties.put("queue.queue/sausage-factory", "sausage-factory"); + properties.put("queue.queue/mincing-machine", "mincing-machine"); + ic = new InitialContext(properties); + + // Step 2. - we look up the sausage-factory queue from node 0 + + Queue sausageFactory = (Queue) ic.lookup("queue/sausage-factory"); + + Queue mincingMachine = (Queue) ic.lookup("queue/mincing-machine"); + + // Step 3. - we look up a JMS ConnectionFactory object from node 0 + + ConnectionFactory cf = (ConnectionFactory) ic.lookup("ConnectionFactory"); + + // Step 4. We create a JMS Connection connection which is a connection to server 0 + + connection = cf.createConnection(); + + // Step 5. We create a JMS Session on server 0 + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Step 6. We start the connection to ensure delivery occurs on them + + connection.start(); + + // Step 7. We create JMS MessageConsumer object + MessageConsumer consumer = session.createConsumer(mincingMachine); + + // Step 8. We create a JMS MessageProducer object + MessageProducer producer = session.createProducer(sausageFactory); + + // Step 9. We create and send a message to the sausage-factory + Message message = session.createMessage(); + + producer.send(message); + + System.out.println("Sent message to sausage-factory"); + + // Step 10. - we successfully receive the message from the mincing-machine. + + Message receivedMessage = consumer.receive(5000); + + if (receivedMessage == null) { + throw new IllegalStateException(); + } + + System.out.println("Received message from mincing-machine"); + } finally { + // Step 15. Be sure to close our resources! + + if (connection != null) { + connection.close(); + } + + if (ic != null) { + ic.close(); + } + } + } +} diff --git a/examples/features/standard/camel/camel-broker/src/main/resources/activemq/server0/bootstrap.xml b/examples/features/standard/camel/camel-broker/src/main/resources/activemq/server0/bootstrap.xml new file mode 100644 index 00000000000..6c58a273d7d --- /dev/null +++ b/examples/features/standard/camel/camel-broker/src/main/resources/activemq/server0/bootstrap.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + diff --git a/examples/features/standard/camel/camel-war/pom.xml b/examples/features/standard/camel/camel-war/pom.xml new file mode 100644 index 00000000000..61e887cfa5c --- /dev/null +++ b/examples/features/standard/camel/camel-war/pom.xml @@ -0,0 +1,76 @@ + + + + + 4.0.0 + + + org.apache.activemq.examples.broker.camel + camel + 2.7.0-SNAPSHOT + + + camel-war + war + ActiveMQ Artemis Camel WAR Application + + + ${project.basedir}/../../../../.. + + + + + org.springframework + spring-web + ${spring.version} + + + org.springframework + spring-context + + + org.apache.camel + camel-spring + 2.20.0 + + + org.slf4j + slf4j-api + + + + + org.apache.camel + camel-jms + 2.20.0 + + + org.slf4j + slf4j-api + + + org.apache.geronimo.specs + geronimo-jms_2.0_spec + + + + + + diff --git a/examples/features/standard/camel/camel-war/src/main/webapp/WEB-INF/applicationContext.xml b/examples/features/standard/camel/camel-war/src/main/webapp/WEB-INF/applicationContext.xml new file mode 100644 index 00000000000..d4d269c1b3d --- /dev/null +++ b/examples/features/standard/camel/camel-war/src/main/webapp/WEB-INF/applicationContext.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/features/standard/camel/camel-war/src/main/webapp/WEB-INF/web.xml b/examples/features/standard/camel/camel-war/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..c5c9531b720 --- /dev/null +++ b/examples/features/standard/camel/camel-war/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,27 @@ + + + + + + org.springframework.web.context.ContextLoaderListener + + + \ No newline at end of file diff --git a/examples/features/standard/camel/pom.xml b/examples/features/standard/camel/pom.xml new file mode 100644 index 00000000000..c6be9ee10a2 --- /dev/null +++ b/examples/features/standard/camel/pom.xml @@ -0,0 +1,43 @@ + + + + + 4.0.0 + + + org.apache.activemq.examples.broker + jms-examples + 2.7.0-SNAPSHOT + + + org.apache.activemq.examples.broker.camel + camel + pom + ActiveMQ Artemis Camel Example + + + ${project.basedir}/../../../.. + + + + camel-war + camel-broker + + diff --git a/examples/features/standard/camel/readme.md b/examples/features/standard/camel/readme.md new file mode 100644 index 00000000000..a39fa7cb11d --- /dev/null +++ b/examples/features/standard/camel/readme.md @@ -0,0 +1,15 @@ +# Camel Example + +To run the example, simply type **mvn verify** from this directory, or **mvn -PnoServer verify** if you want to start and create the broker manually. + +This example contains 2 different Maven modules: + +1) `camel-broker` The module responsible for creating the broker, deploying the WAR-based Camel application, and running the client. +2) `camel-war` The module used to build the WAR-based Camel application. + +The overall goal of this example is to demonstrate how to build and deploy a Camel route to the broker. + +The client itself is essentially the same as the one in the `core-bridge` example except there is only 1 broker in this +example rather than 2. A Camel route defined in the WAR is responsible for moving messages between 2 queues. The client +sends a message to one queue, the Camel route moves that message to a second queue, and then the client reads that +message from the second queue. \ No newline at end of file diff --git a/examples/features/standard/pom.xml b/examples/features/standard/pom.xml index 13e006b1851..0493457d8bf 100644 --- a/examples/features/standard/pom.xml +++ b/examples/features/standard/pom.xml @@ -44,6 +44,7 @@ under the License. auto-closeable browser broker-plugin + camel cdi client-kickoff completion-listener @@ -114,6 +115,7 @@ under the License. auto-closeable browser broker-plugin + camel cdi client-kickoff completion-listener From 42f3cf07653ecb9f8215b6fb27470bf5b42ed998 Mon Sep 17 00:00:00 2001 From: Shailendra Kumar Singh Date: Sat, 16 Jun 2018 09:19:54 +0530 Subject: [PATCH 041/207] ARTEMIS-1939 Remove space from parameter annotations name in ActiveMQServerControl --- .../management/ActiveMQServerControl.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java index 9e8c9dfee6e..83f66b7aea9 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java @@ -1212,34 +1212,34 @@ void createConnectorService(@Parameter(name = "name", desc = "Name of the connec String listAddresses(@Parameter(name = "separator", desc = "Separator used on the string listing") String separator) throws Exception; @Operation(desc = "Search for Connections", impact = MBeanOperationInfo.INFO) - String listConnections(@Parameter(name = "Options") String options, - @Parameter(name = "Page Number") int page, - @Parameter(name = "Page Size") int pageSize) throws Exception; + String listConnections(@Parameter(name = "options", desc = "Options") String options, + @Parameter(name = "pageNumber", desc = "Page Number") int page, + @Parameter(name = "pageSize", desc = "Page Size") int pageSize) throws Exception; @Operation(desc = "Search for Sessions", impact = MBeanOperationInfo.INFO) - String listSessions(@Parameter(name = "Options") String options, - @Parameter(name = "Page Number") int page, - @Parameter(name = "Page Size") int pageSize) throws Exception; + String listSessions(@Parameter(name = "options", desc = "Options") String options, + @Parameter(name = "pageNumber", desc = "Page Number") int page, + @Parameter(name = "pageSize", desc = "Page Size") int pageSize) throws Exception; @Operation(desc = "Search for Consumers", impact = MBeanOperationInfo.INFO) - String listConsumers(@Parameter(name = "Options") String options, - @Parameter(name = "Page Number") int page, - @Parameter(name = "Page Size") int pageSize) throws Exception; + String listConsumers(@Parameter(name = "options", desc = "Options") String options, + @Parameter(name = "pageNumber", desc = "Page Number") int page, + @Parameter(name = "pageSize", desc = "Page Size") int pageSize) throws Exception; @Operation(desc = "Search for Consumers", impact = MBeanOperationInfo.INFO) - String listProducers(@Parameter(name = "Options") String options, - @Parameter(name = "Page Number") int page, - @Parameter(name = "Page Size") int pageSize) throws Exception; + String listProducers(@Parameter(name = "options", desc = "Options") String options, + @Parameter(name = "pageNumber", desc = "Page Number") int page, + @Parameter(name = "pageSize", desc = "Page Size") int pageSize) throws Exception; @Operation(desc = "Search for Addresses", impact = MBeanOperationInfo.INFO) - String listAddresses(@Parameter(name = "Options") String options, - @Parameter(name = "Page Number") int page, - @Parameter(name = "Page Size") int pageSize) throws Exception; + String listAddresses(@Parameter(name = "options", desc = "Options") String options, + @Parameter(name = "pageNumber", desc = "Page Number") int page, + @Parameter(name = "pageSize", desc = "Page Size") int pageSize) throws Exception; @Operation(desc = "Search for Queues", impact = MBeanOperationInfo.INFO) - String listQueues(@Parameter(name = "Options") String options, - @Parameter(name = "Page Number") int page, - @Parameter(name = "Page Size") int pageSize) throws Exception; + String listQueues(@Parameter(name = "options", desc = "Options") String options, + @Parameter(name = "pageNumber", desc = "Page Number") int page, + @Parameter(name = "pageSize", desc = "Page Size") int pageSize) throws Exception; /** * Returns the names of the queues created on this server with the given routing-type. From 7df3bcecefe3979b3bbb09f7745199e5af061962 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Mon, 11 Jun 2018 21:35:33 -0500 Subject: [PATCH 042/207] Revert "[ARTEMIS-1819] Missing fields on listAllConsumersAsJSON, listConsumersAsJSON and listConnectionsAsJSON" This reverts commit c3fbd1b9e479f87898e0acd53f386d588c997632. Based on the discussion on the PR https://github.com/apache/activemq-artemis/pull/2035 this shouldn't have been merged. It's importing JMS-specific code into the core broker which is something we've worked hard to eliminate in recent releases. --- .../impl/ActiveMQServerControlImpl.java | 26 ------------------- .../management/ActiveMQServerControlTest.java | 14 ---------- 2 files changed, 40 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java index f4537a0ec08..58f26131858 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java @@ -53,7 +53,6 @@ import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.TransportConfiguration; -import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl; import org.apache.activemq.artemis.api.core.management.AddressControl; import org.apache.activemq.artemis.api.core.management.BridgeControl; @@ -1832,19 +1831,6 @@ public String listConnectionsAsJSON() throws Exception { for (RemotingConnection connection : connections) { JsonObjectBuilder obj = JsonLoader.createObjectBuilder().add("connectionID", connection.getID().toString()).add("clientAddress", connection.getRemoteAddress()).add("creationTime", connection.getCreationTime()).add("implementation", connection.getClass().getSimpleName()).add("sessionCount", server.getSessions(connection.getID().toString()).size()); - - List sessions = server.getSessions(connection.getID().toString()); - - if (sessions.size() > 0) { - if (sessions.get(0).getMetaData(ClientSession.JMS_SESSION_CLIENT_ID_PROPERTY) != null) { - obj.add("clientID", sessions.get(0).getMetaData(ClientSession.JMS_SESSION_CLIENT_ID_PROPERTY)); - } else { - obj.add("clientID", ""); - } - } else { - obj.add("clientID", ""); - } - array.add(obj); } return array.build().toString(); @@ -1962,18 +1948,6 @@ private JsonObject toJSONObject(ServerConsumer consumer) throws Exception { obj.add("filter", consumer.getFilter().getFilterString().toString()); } - obj.add("destinationName", consumer.getQueue().getAddress().toString()); - - if (consumer.getQueueType().getType() == 0) { - obj.add("destinationType", "topic"); - } else if (consumer.getQueueType().getType() == 1) { - obj.add("destinationType", "queue"); - } else { - obj.add("destinationType", ""); - } - - obj.add("durable", consumer.getQueue().isDurable()); - return obj.build(); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java index cdf7a08c41a..b79fceda167 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java @@ -1317,13 +1317,11 @@ public void testListConnectionsAsJSON() throws Exception { Assert.assertTrue(first.getString("clientAddress").length() > 0); Assert.assertTrue(first.getJsonNumber("creationTime").longValue() > 0); Assert.assertEquals(0, first.getJsonNumber("sessionCount").longValue()); - Assert.assertEquals("", first.getString("clientID")); Assert.assertTrue(second.getString("connectionID").length() > 0); Assert.assertTrue(second.getString("clientAddress").length() > 0); Assert.assertTrue(second.getJsonNumber("creationTime").longValue() > 0); Assert.assertEquals(1, second.getJsonNumber("sessionCount").longValue()); - Assert.assertEquals("", second.getString("clientID")); } @Test @@ -1366,9 +1364,6 @@ public void testListConsumersAsJSON() throws Exception { Assert.assertEquals(false, first.getBoolean("browseOnly")); Assert.assertTrue(first.getJsonNumber("creationTime").longValue() > 0); Assert.assertEquals(0, first.getJsonNumber("deliveringCount").longValue()); - Assert.assertEquals(queueName.toString(), first.getString("destinationName")); - Assert.assertEquals("queue", first.getString("destinationType")); - Assert.assertFalse(first.getBoolean("durable")); Assert.assertNotNull(second.getJsonNumber("consumerID").longValue()); Assert.assertTrue(second.getString("connectionID").length() > 0); @@ -1382,9 +1377,6 @@ public void testListConsumersAsJSON() throws Exception { Assert.assertEquals(0, second.getJsonNumber("deliveringCount").longValue()); Assert.assertTrue(second.getString("filter").length() > 0); Assert.assertEquals(filter, second.getString("filter")); - Assert.assertEquals(queueName.toString(), second.getString("destinationName")); - Assert.assertEquals("queue", second.getString("destinationType")); - Assert.assertFalse(second.getBoolean("durable")); } @Test @@ -1449,9 +1441,6 @@ public void testListAllConsumersAsJSON() throws Exception { Assert.assertEquals(queueName.toString(), first.getString("queueName")); Assert.assertEquals(false, first.getBoolean("browseOnly")); Assert.assertEquals(0, first.getJsonNumber("deliveringCount").longValue()); - Assert.assertEquals(queueName.toString(), first.getString("destinationName")); - Assert.assertEquals("queue", first.getString("destinationType")); - Assert.assertFalse(first.getBoolean("durable")); Assert.assertTrue(second.getJsonNumber("creationTime").longValue() > 0); Assert.assertNotNull(second.getJsonNumber("consumerID").longValue()); @@ -1463,9 +1452,6 @@ public void testListAllConsumersAsJSON() throws Exception { Assert.assertEquals(queueName.toString(), second.getString("queueName")); Assert.assertEquals(false, second.getBoolean("browseOnly")); Assert.assertEquals(0, second.getJsonNumber("deliveringCount").longValue()); - Assert.assertEquals(queueName.toString(), second.getString("destinationName")); - Assert.assertEquals("queue", second.getString("destinationType")); - Assert.assertFalse(second.getBoolean("durable")); } @Test From 57ed5b0530d4b5d74909deb81121f473718b352e Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Tue, 12 Jun 2018 15:27:48 -0500 Subject: [PATCH 043/207] ARTEMIS-1926 refactor SSLSupport --- .../remoting/impl/netty/NettyConnector.java | 52 +++-- .../core/remoting/impl/ssl/SSLSupport.java | 183 ++++++++++-------- .../remoting/impl/netty/NettyAcceptor.java | 20 +- .../management/ConnectorServerFactory.java | 9 +- .../cli/test/WebServerComponentTest.java | 16 +- .../jms/example/MqttCrlEnabledExample.java | 7 +- .../mqtt/imported/MQTTSecurityCRLTest.java | 7 +- .../ssl/CoreClientOverOneWaySSLTest.java | 15 +- .../NettyConnectorWithHTTPUpgradeTest.java | 5 +- .../remoting/impl/ssl/SSLSupportTest.java | 102 ++++++++-- 10 files changed, 295 insertions(+), 121 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java index b32da929115..284d0b90531 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java @@ -533,7 +533,7 @@ public void initChannel(Channel channel) throws Exception { if (sslProvider.equals(TransportConstants.OPENSSL_PROVIDER)) { engine = loadOpenSslEngine(channel.alloc(), realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword); } else { - engine = loadJdkSslEngine(useDefaultSslContext, realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword); + engine = loadJdkSslEngine(realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword); } engine.setUseClientMode(true); @@ -607,18 +607,26 @@ public void initChannel(Channel channel) throws Exception { ActiveMQClientLogger.LOGGER.startedNettyConnector(connectorType, TransportConstants.NETTY_VERSION, host, port); } - private SSLEngine loadJdkSslEngine(boolean useDefaultSslContext, - String realKeyStoreProvider, - String realKeyStorePath, - String realKeyStorePassword, - String realTrustStoreProvider, - String realTrustStorePath, - String realTrustStorePassword) throws Exception { + private SSLEngine loadJdkSslEngine(String keystoreProvider, + String keystorePath, + String keystorePassword, + String truststoreProvider, + String truststorePath, + String truststorePassword) throws Exception { SSLContext context; if (useDefaultSslContext) { context = SSLContext.getDefault(); } else { - context = SSLSupport.createContext(realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword, trustAll, crlPath); + context = new SSLSupport() + .setKeystoreProvider(keystoreProvider) + .setKeystorePath(keystorePath) + .setKeystorePassword(keystorePassword) + .setTruststoreProvider(truststoreProvider) + .setTruststorePath(truststorePath) + .setTruststorePassword(truststorePassword) + .setTrustAll(trustAll) + .setCrlPath(crlPath) + .createContext(); } Subject subject = null; if (kerb5Config != null) { @@ -642,14 +650,24 @@ public SSLEngine run() { } private SSLEngine loadOpenSslEngine(ByteBufAllocator alloc, - String realKeyStoreProvider, - String realKeyStorePath, - String realKeyStorePassword, - String realTrustStoreProvider, - String realTrustStorePath, - String realTrustStorePassword) throws Exception { - - SslContext context = SSLSupport.createNettyClientContext(realKeyStoreProvider, realKeyStorePath, realKeyStorePassword, realTrustStoreProvider, realTrustStorePath, realTrustStorePassword, sslProvider, trustAll); + String keystoreProvider, + String keystorePath, + String keystorePassword, + String truststoreProvider, + String truststorePath, + String truststorePassword) throws Exception { + + + SslContext context = new SSLSupport() + .setKeystoreProvider(keystoreProvider) + .setKeystorePath(keystorePath) + .setKeystorePassword(keystorePassword) + .setTruststoreProvider(truststoreProvider) + .setTruststorePath(truststorePath) + .setTruststorePassword(truststorePassword) + .setSslProvider(sslProvider) + .setTrustAll(trustAll) + .createNettyClientContext(); Subject subject = null; if (kerb5Config != null) { diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java index 905e19e9330..89994c29ddc 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java @@ -44,6 +44,7 @@ import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; import org.apache.activemq.artemis.utils.ClassloadingUtil; /** @@ -53,80 +54,117 @@ * null keystore path. */ public class SSLSupport { - // Public -------------------------------------------------------- + private String keystoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER; + private String keystorePath = TransportConstants.DEFAULT_KEYSTORE_PATH; + private String keystorePassword = TransportConstants.DEFAULT_KEYSTORE_PASSWORD; + private String truststoreProvider = TransportConstants.DEFAULT_TRUSTSTORE_PROVIDER; + private String truststorePath = TransportConstants.DEFAULT_TRUSTSTORE_PATH; + private String truststorePassword = TransportConstants.DEFAULT_TRUSTSTORE_PASSWORD; + private String crlPath = TransportConstants.DEFAULT_CRL_PATH; + private String sslProvider = TransportConstants.DEFAULT_SSL_PROVIDER; + private boolean trustAll = TransportConstants.DEFAULT_TRUST_ALL; + + public String getKeystoreProvider() { + return keystoreProvider; + } + + public SSLSupport setKeystoreProvider(String keystoreProvider) { + this.keystoreProvider = keystoreProvider; + return this; + } + + public String getKeystorePath() { + return keystorePath; + } + + public SSLSupport setKeystorePath(String keystorePath) { + this.keystorePath = keystorePath; + return this; + } + + public String getKeystorePassword() { + return keystorePassword; + } + + public SSLSupport setKeystorePassword(String keystorePassword) { + this.keystorePassword = keystorePassword; + return this; + } + + public String getTruststoreProvider() { + return truststoreProvider; + } - public static SSLContext createContext(final String keystoreProvider, - final String keystorePath, - final String keystorePassword, - final String trustStoreProvider, - final String trustStorePath, - final String trustStorePassword) throws Exception { + public SSLSupport setTruststoreProvider(String truststoreProvider) { + this.truststoreProvider = truststoreProvider; + return this; + } - return SSLSupport.createContext(keystoreProvider, keystorePath, keystorePassword, trustStoreProvider, trustStorePath, trustStorePassword, false, null); + public String getTruststorePath() { + return truststorePath; } - public static SSLContext createContext(final String keystoreProvider, - final String keystorePath, - final String keystorePassword, - final String trustStoreProvider, - final String trustStorePath, - final String trustStorePassword, - final String crlPath) throws Exception { + public SSLSupport setTruststorePath(String truststorePath) { + this.truststorePath = truststorePath; + return this; + } - return SSLSupport.createContext(keystoreProvider, keystorePath, keystorePassword, trustStoreProvider, trustStorePath, trustStorePassword, false, crlPath); + public String getTruststorePassword() { + return truststorePassword; } - public static SSLContext createContext(final String keystoreProvider, - final String keystorePath, - final String keystorePassword, - final String trustStoreProvider, - final String trustStorePath, - final String trustStorePassword, - final boolean trustAll) throws Exception { - return SSLSupport.createContext(keystoreProvider, keystorePath, keystorePassword, trustStoreProvider, trustStorePath, trustStorePassword, trustAll, null); + public SSLSupport setTruststorePassword(String truststorePassword) { + this.truststorePassword = truststorePassword; + return this; } - public static SSLContext createContext(final String keystoreProvider, - final String keystorePath, - final String keystorePassword, - final String trustStoreProvider, - final String trustStorePath, - final String trustStorePassword, - final boolean trustAll, - final String crlPath) throws Exception { + public String getCrlPath() { + return crlPath; + } + + public SSLSupport setCrlPath(String crlPath) { + this.crlPath = crlPath; + return this; + } + + public String getSslProvider() { + return sslProvider; + } + + public SSLSupport setSslProvider(String sslProvider) { + this.sslProvider = sslProvider; + return this; + } + + public boolean isTrustAll() { + return trustAll; + } + + public SSLSupport setTrustAll(boolean trustAll) { + this.trustAll = trustAll; + return this; + } + + public SSLContext createContext() throws Exception { SSLContext context = SSLContext.getInstance("TLS"); - KeyManager[] keyManagers = SSLSupport.loadKeyManagers(keystoreProvider, keystorePath, keystorePassword); - TrustManager[] trustManagers = SSLSupport.loadTrustManager(trustStoreProvider, trustStorePath, trustStorePassword, trustAll, crlPath); + KeyManager[] keyManagers = loadKeyManagers(); + TrustManager[] trustManagers = loadTrustManagers(); context.init(keyManagers, trustManagers, new SecureRandom()); return context; } - public static SslContext createNettyContext(final String keystoreProvider, - final String keystorePath, - final String keystorePassword, - final String trustStoreProvider, - final String trustStorePath, - final String trustStorePassword, - final String sslProvider) throws Exception { - + public SslContext createNettyContext() throws Exception { KeyStore keyStore = SSLSupport.loadKeystore(keystoreProvider, keystorePath, keystorePassword); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, keystorePassword.toCharArray()); - return SslContextBuilder.forServer(keyManagerFactory).sslProvider(SslProvider.valueOf(sslProvider)).trustManager(SSLSupport.loadTrustManagerFactory(trustStoreProvider, trustStorePath, trustStorePassword, false, null)).build(); + return SslContextBuilder.forServer(keyManagerFactory).sslProvider(SslProvider.valueOf(sslProvider)).trustManager(loadTrustManagerFactory()).build(); } - public static SslContext createNettyClientContext(final String keystoreProvider, - final String keystorePath, - final String keystorePassword, - final String trustStoreProvider, - final String trustStorePath, - final String trustStorePassword, - final String sslProvider, - final boolean trustAll ) throws Exception { + public SslContext createNettyClientContext() throws Exception { KeyStore keyStore = SSLSupport.loadKeystore(keystoreProvider, keystorePath, keystorePassword); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, keystorePassword == null ? null : keystorePassword.toCharArray()); - return SslContextBuilder.forClient().sslProvider(SslProvider.valueOf(sslProvider)).keyManager(keyManagerFactory).trustManager(SSLSupport.loadTrustManagerFactory(trustStoreProvider, trustStorePath, trustStorePassword, trustAll, null)).build(); + return SslContextBuilder.forClient().sslProvider(SslProvider.valueOf(sslProvider)).keyManager(keyManagerFactory).trustManager(loadTrustManagerFactory()).build(); } @@ -151,19 +189,15 @@ public static String parseArrayIntoCommandSeparatedList(String[] suites) { } // Private ------------------------------------------------------- - private static TrustManagerFactory loadTrustManagerFactory(final String trustStoreProvider, - final String trustStorePath, - final String trustStorePassword, - final boolean trustAll, - final String crlPath) throws Exception { + private TrustManagerFactory loadTrustManagerFactory() throws Exception { if (trustAll) { //This is useful for testing but not should be used outside of that purpose return InsecureTrustManagerFactory.INSTANCE; - } else if (trustStorePath == null && (trustStoreProvider == null || !"PKCS11".equals(trustStoreProvider.toUpperCase()))) { + } else if (truststorePath == null && (truststoreProvider == null || !"PKCS11".equals(truststoreProvider.toUpperCase()))) { return null; } else { TrustManagerFactory trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - KeyStore trustStore = SSLSupport.loadKeystore(trustStoreProvider, trustStorePath, trustStorePassword); + KeyStore trustStore = SSLSupport.loadKeystore(truststoreProvider, truststorePath, truststorePassword); boolean ocsp = Boolean.valueOf(Security.getProperty("ocsp.enable")); boolean initialized = false; @@ -171,7 +205,7 @@ private static TrustManagerFactory loadTrustManagerFactory(final String trustSto PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustStore, new X509CertSelector()); if (crlPath != null) { pkixParams.setRevocationEnabled(true); - Collection crlList = loadCRL(crlPath); + Collection crlList = loadCRL(); if (crlList != null) { pkixParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(crlList))); } @@ -187,25 +221,19 @@ private static TrustManagerFactory loadTrustManagerFactory(final String trustSto } } - private static TrustManager[] loadTrustManager(final String trustStoreProvider, - final String trustStorePath, - final String trustStorePassword, - final boolean trustAll, - final String crlPath) throws Exception { - TrustManagerFactory trustManagerFactory = loadTrustManagerFactory(trustStoreProvider, trustStorePath, trustStorePassword, trustAll, crlPath); + private TrustManager[] loadTrustManagers() throws Exception { + TrustManagerFactory trustManagerFactory = loadTrustManagerFactory(); if (trustManagerFactory == null) { return null; } return trustManagerFactory.getTrustManagers(); } - private static Collection loadCRL(String crlPath) throws Exception { + private Collection loadCRL() throws Exception { if (crlPath == null) { return null; } - - URL resource = SSLSupport.validateStoreURL(crlPath); - + URL resource = validateStoreURL(crlPath); try (InputStream is = resource.openStream()) { return CertificateFactory.getInstance("X.509").generateCRLs(is); } @@ -233,25 +261,20 @@ private static KeyStore loadKeystore(final String keystoreProvider, return ks; } - private static KeyManager[] loadKeyManagers(final String keyStoreProvider, - final String keystorePath, - final String keystorePassword) throws Exception { - - KeyManagerFactory factory = loadKeyManagerFactory(keyStoreProvider, keystorePath, keystorePassword); + private KeyManager[] loadKeyManagers() throws Exception { + KeyManagerFactory factory = loadKeyManagerFactory(); if (factory == null) { return null; } return factory.getKeyManagers(); } - private static KeyManagerFactory loadKeyManagerFactory(final String keyStoreProvider, - final String keystorePath, - final String keystorePassword) throws Exception { - if (keystorePath == null && (keyStoreProvider == null || !"PKCS11".equals(keyStoreProvider.toUpperCase()))) { + private KeyManagerFactory loadKeyManagerFactory() throws Exception { + if (keystorePath == null && (keystoreProvider == null || !"PKCS11".equals(keystoreProvider.toUpperCase()))) { return null; } else { KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - KeyStore ks = SSLSupport.loadKeystore(keyStoreProvider, keystorePath, keystorePassword); + KeyStore ks = SSLSupport.loadKeystore(keystoreProvider, keystorePath, keystorePassword); kmf.init(ks, keystorePassword == null ? null : keystorePassword.toCharArray()); return kmf; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java index ed1a9412d23..fb46ff44015 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java @@ -542,7 +542,15 @@ private SSLEngine loadJdkSslEngine() throws Exception { try { if (kerb5Config == null && keyStorePath == null && TransportConstants.DEFAULT_TRUSTSTORE_PROVIDER.equals(keyStoreProvider)) throw new IllegalArgumentException("If \"" + TransportConstants.SSL_ENABLED_PROP_NAME + "\" is true then \"" + TransportConstants.KEYSTORE_PATH_PROP_NAME + "\" must be non-null " + "unless an alternative \"" + TransportConstants.KEYSTORE_PROVIDER_PROP_NAME + "\" has been specified."); - context = SSLSupport.createContext(keyStoreProvider, keyStorePath, keyStorePassword, trustStoreProvider, trustStorePath, trustStorePassword, crlPath); + context = new SSLSupport() + .setKeystoreProvider(keyStoreProvider) + .setKeystorePath(keyStorePath) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(trustStoreProvider) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .setCrlPath(crlPath) + .createContext(); } catch (Exception e) { IllegalStateException ise = new IllegalStateException("Unable to create NettyAcceptor for " + host + ":" + port); ise.initCause(e); @@ -573,7 +581,15 @@ private SSLEngine loadOpenSslEngine(ByteBufAllocator alloc) throws Exception { try { if (kerb5Config == null && keyStorePath == null && TransportConstants.DEFAULT_TRUSTSTORE_PROVIDER.equals(keyStoreProvider)) throw new IllegalArgumentException("If \"" + TransportConstants.SSL_ENABLED_PROP_NAME + "\" is true then \"" + TransportConstants.KEYSTORE_PATH_PROP_NAME + "\" must be non-null " + "unless an alternative \"" + TransportConstants.KEYSTORE_PROVIDER_PROP_NAME + "\" has been specified."); - context = SSLSupport.createNettyContext(keyStoreProvider, keyStorePath, keyStorePassword, trustStoreProvider, trustStorePath, trustStorePassword, sslProvider); + context = new SSLSupport() + .setKeystoreProvider(keyStoreProvider) + .setKeystorePath(keyStorePath) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(trustStoreProvider) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .setSslProvider(sslProvider) + .createNettyContext(); } catch (Exception e) { IllegalStateException ise = new IllegalStateException("Unable to create NettyAcceptor for " + host + ":" + port); ise.initCause(e); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ConnectorServerFactory.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ConnectorServerFactory.java index 2c66acbb95a..4ae7e3eabd8 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ConnectorServerFactory.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ConnectorServerFactory.java @@ -232,7 +232,14 @@ protected void doUnregister(ObjectName objectName) { //todo fix private void setupSsl() throws Exception { - SSLContext context = SSLSupport.createContext(keyStoreProvider, keyStorePath, keyStorePassword, trustStoreProvider, trustStorePath, trustStorePassword); + SSLContext context = new SSLSupport() + .setKeystoreProvider(keyStoreProvider) + .setKeystorePath(keyStorePath) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(trustStoreProvider) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .createContext(); SSLServerSocketFactory sssf = context.getServerSocketFactory(); RMIServerSocketFactory rssf = new ArtemisSslRMIServerSocketFactory(sssf, this.isClientAuth(), rmiServerHost); RMIClientSocketFactory rcsf = new SslRMIClientSocketFactory(); diff --git a/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java b/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java index 1f1a946ea5d..fb6461e2cf9 100644 --- a/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java +++ b/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java @@ -177,9 +177,13 @@ public void simpleSecureServer() throws Exception { webServerComponent.start(); final int port = webServerComponent.getPort(); // Make the connection attempt. - String keyStoreProvider = "JKS"; - SSLContext context = SSLSupport.createContext(keyStoreProvider, webServerDTO.keyStorePath, webServerDTO.getKeyStorePassword(), keyStoreProvider, webServerDTO.keyStorePath, webServerDTO.getKeyStorePassword()); + SSLContext context = new SSLSupport() + .setKeystorePath(webServerDTO.keyStorePath) + .setKeystorePassword(webServerDTO.getKeyStorePassword()) + .setTruststorePath(webServerDTO.keyStorePath) + .setTruststorePassword(webServerDTO.getKeyStorePassword()) + .createContext(); SSLEngine engine = context.createSSLEngine(); engine.setUseClientMode(true); @@ -233,9 +237,13 @@ public void simpleSecureServerWithClientAuth() throws Exception { webServerComponent.start(); final int port = webServerComponent.getPort(); // Make the connection attempt. - String keyStoreProvider = "JKS"; - SSLContext context = SSLSupport.createContext(keyStoreProvider, webServerDTO.keyStorePath, webServerDTO.getKeyStorePassword(), keyStoreProvider, webServerDTO.trustStorePath, webServerDTO.getTrustStorePassword()); + SSLContext context = new SSLSupport() + .setKeystorePath(webServerDTO.keyStorePath) + .setKeystorePassword(webServerDTO.getKeyStorePassword()) + .setTruststorePath(webServerDTO.trustStorePath) + .setTruststorePassword(webServerDTO.getTrustStorePassword()) + .createContext(); SSLEngine engine = context.createSSLEngine(); engine.setUseClientMode(true); diff --git a/examples/features/standard/ssl-enabled-crl-mqtt/src/main/java/org/apache/activemq/artemis/jms/example/MqttCrlEnabledExample.java b/examples/features/standard/ssl-enabled-crl-mqtt/src/main/java/org/apache/activemq/artemis/jms/example/MqttCrlEnabledExample.java index a4ddf6a6dcb..46e0ad15054 100644 --- a/examples/features/standard/ssl-enabled-crl-mqtt/src/main/java/org/apache/activemq/artemis/jms/example/MqttCrlEnabledExample.java +++ b/examples/features/standard/ssl-enabled-crl-mqtt/src/main/java/org/apache/activemq/artemis/jms/example/MqttCrlEnabledExample.java @@ -72,7 +72,12 @@ private static BlockingConnection retrieveMQTTConnection(String host, String tru mqtt.setConnectAttemptsMax(0); mqtt.setReconnectAttemptsMax(0); mqtt.setHost(host); - mqtt.setSslContext(SSLSupport.createContext("JKS", keystorePath, keystorePass, "JKS", truststorePath, truststorePass)); + mqtt.setSslContext(new SSLSupport() + .setKeystorePath(keystorePath) + .setKeystorePassword(keystorePass) + .setTruststorePath(truststorePath) + .setTruststorePassword(truststorePass) + .createContext()); mqtt.setCleanSession(true); BlockingConnection connection = mqtt.blockingConnection(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/imported/MQTTSecurityCRLTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/imported/MQTTSecurityCRLTest.java index 4f88661ad5e..dd45f5c0c15 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/imported/MQTTSecurityCRLTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/imported/MQTTSecurityCRLTest.java @@ -235,7 +235,12 @@ private BlockingConnection retrieveMQTTConnection(String host, String truststore mqtt.setConnectAttemptsMax(1); mqtt.setReconnectAttemptsMax(0); mqtt.setHost(host); - SSLContext sslContext = SSLSupport.createContext(TransportConstants.DEFAULT_KEYSTORE_PROVIDER, keystorePath, keystorePass, TransportConstants.DEFAULT_TRUSTSTORE_PROVIDER, truststorePath, truststorePass); + SSLContext sslContext = new SSLSupport() + .setKeystorePath(keystorePath) + .setKeystorePassword(keystorePass) + .setTruststorePath(truststorePath) + .setTruststorePassword(truststorePass) + .createContext(); mqtt.setSslContext(sslContext); BlockingConnection connection = mqtt.blockingConnection(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java index b9b9bc8131c..2bc321224cb 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java @@ -239,7 +239,11 @@ public void testOneWaySSLUsingDefaultSslContext() throws Exception { tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true); tc.getParams().put(TransportConstants.USE_DEFAULT_SSL_CONTEXT_PROP_NAME, true); - SSLContext.setDefault(SSLSupport.createContext(TransportConstants.DEFAULT_KEYSTORE_PROVIDER, TransportConstants.DEFAULT_KEYSTORE_PATH, TransportConstants.DEFAULT_KEYSTORE_PASSWORD, storeType, CLIENT_SIDE_TRUSTSTORE, PASSWORD)); + SSLContext.setDefault(new SSLSupport() + .setTruststoreProvider(storeType) + .setTruststorePath(CLIENT_SIDE_TRUSTSTORE) + .setTruststorePassword(PASSWORD) + .createContext()); ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc)); ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator)); @@ -662,7 +666,14 @@ public String getSuitableCipherSuite() throws Exception { } public String[] getEnabledCipherSuites() throws Exception { - SSLContext context = SSLSupport.createContext(storeType, SERVER_SIDE_KEYSTORE, PASSWORD, storeType, CLIENT_SIDE_TRUSTSTORE, PASSWORD); + SSLContext context = new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath(SERVER_SIDE_KEYSTORE) + .setKeystorePassword(PASSWORD) + .setTruststoreProvider(storeType) + .setTruststorePath(CLIENT_SIDE_TRUSTSTORE) + .setTruststorePassword(PASSWORD) + .createContext(); SSLEngine engine = context.createSSLEngine(); return engine.getEnabledCipherSuites(); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/transports/netty/NettyConnectorWithHTTPUpgradeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/transports/netty/NettyConnectorWithHTTPUpgradeTest.java index b93dc44f918..96ecb56a90c 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/transports/netty/NettyConnectorWithHTTPUpgradeTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/transports/netty/NettyConnectorWithHTTPUpgradeTest.java @@ -210,7 +210,10 @@ private void startWebServer(int port) throws Exception { ServerBootstrap b = new ServerBootstrap(); final SSLContext context; if (useSSL) { - context = SSLSupport.createContext("JKS", SERVER_SIDE_KEYSTORE, PASSWORD, null, null, null); + context = new SSLSupport() + .setKeystorePath(SERVER_SIDE_KEYSTORE) + .setKeystorePassword(PASSWORD) + .createContext(); } else { context = null; } diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/remoting/impl/ssl/SSLSupportTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/remoting/impl/ssl/SSLSupportTest.java index 3cb6e6d433a..256be6462d5 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/remoting/impl/ssl/SSLSupportTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/remoting/impl/ssl/SSLSupportTest.java @@ -73,32 +73,60 @@ public void setUp() throws Exception { @Test public void testContextWithRightParameters() throws Exception { - SSLSupport.createContext(storeType, keyStorePath, keyStorePassword, storeType, trustStorePath, trustStorePassword); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath(keyStorePath) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(storeType) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .createContext(); } // This is valid as it will create key and trust managers with system defaults @Test public void testContextWithNullParameters() throws Exception { - SSLSupport.createContext(null, null, null, null, null, null); + new SSLSupport().createContext(); } @Test public void testContextWithKeyStorePathAsURL() throws Exception { URL url = Thread.currentThread().getContextClassLoader().getResource(keyStorePath); - SSLSupport.createContext(storeType, url.toString(), keyStorePassword, storeType, trustStorePath, trustStorePassword); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath(url.toString()) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(storeType) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .createContext(); } @Test public void testContextWithKeyStorePathAsFile() throws Exception { URL url = Thread.currentThread().getContextClassLoader().getResource(keyStorePath); File file = new File(url.toURI()); - SSLSupport.createContext(storeType, file.getAbsolutePath(), keyStorePassword, storeType, trustStorePath, trustStorePassword); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath(file.getAbsolutePath()) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(storeType) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .createContext(); } @Test public void testContextWithBadKeyStorePath() throws Exception { try { - SSLSupport.createContext(storeType, "not a keystore", keyStorePassword, storeType, trustStorePath, trustStorePassword); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath("not a keystore") + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(storeType) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .createContext(); Assert.fail(); } catch (Exception e) { } @@ -107,7 +135,14 @@ public void testContextWithBadKeyStorePath() throws Exception { @Test public void testContextWithNullKeyStorePath() throws Exception { try { - SSLSupport.createContext(storeType, null, keyStorePassword, storeType, trustStorePath, trustStorePassword); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath(null) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(storeType) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .createContext(); } catch (Exception e) { Assert.fail(); } @@ -122,13 +157,27 @@ public void testContextWithKeyStorePathAsRelativePath() throws Exception { return; } - SSLSupport.createContext(storeType, "src/test/resources/" + keyStorePath, keyStorePassword, storeType, trustStorePath, trustStorePassword); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath("src/test/resources/" + keyStorePath) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(storeType) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .createContext(); } @Test public void testContextWithBadKeyStorePassword() throws Exception { try { - SSLSupport.createContext(storeType, keyStorePath, "bad password", storeType, trustStorePath, trustStorePassword); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath(keyStorePath) + .setKeystorePassword("bad password") + .setTruststoreProvider(storeType) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .createContext(); Assert.fail(); } catch (Exception e) { } @@ -137,7 +186,14 @@ public void testContextWithBadKeyStorePassword() throws Exception { @Test public void testContextWithNullKeyStorePassword() throws Exception { try { - SSLSupport.createContext(storeType, keyStorePath, null, storeType, trustStorePath, trustStorePassword); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath(keyStorePath) + .setKeystorePassword(null) + .setTruststoreProvider(storeType) + .setTruststorePath(trustStorePath) + .setTruststorePassword(trustStorePassword) + .createContext(); Assert.fail(); } catch (Exception e) { assertFalse(e instanceof NullPointerException); @@ -147,7 +203,14 @@ public void testContextWithNullKeyStorePassword() throws Exception { @Test public void testContextWithBadTrustStorePath() throws Exception { try { - SSLSupport.createContext(storeType, keyStorePath, keyStorePassword, storeType, "not a trust store", trustStorePassword); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath(keyStorePath) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(storeType) + .setTruststorePath("not a trust store") + .setTruststorePassword(trustStorePassword) + .createContext(); Assert.fail(); } catch (Exception e) { } @@ -156,7 +219,14 @@ public void testContextWithBadTrustStorePath() throws Exception { @Test public void testContextWithBadTrustStorePassword() throws Exception { try { - SSLSupport.createContext(storeType, keyStorePath, keyStorePassword, storeType, trustStorePath, "bad passord"); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath(keyStorePath) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(storeType) + .setTruststorePath(trustStorePath) + .setTruststorePassword("bad passord") + .createContext(); Assert.fail(); } catch (Exception e) { } @@ -166,6 +236,14 @@ public void testContextWithBadTrustStorePassword() throws Exception { public void testContextWithTrustAll() throws Exception { //This is using a bad password but should not fail because the trust store should be ignored with //the trustAll flag set to true - SSLSupport.createContext(storeType, keyStorePath, keyStorePassword, storeType, trustStorePath, "bad passord", true); + new SSLSupport() + .setKeystoreProvider(storeType) + .setKeystorePath(keyStorePath) + .setKeystorePassword(keyStorePassword) + .setTruststoreProvider(storeType) + .setTruststorePath(trustStorePath) + .setTruststorePassword("bad passord") + .setTrustAll(true) + .createContext(); } } From 197661cb5800973a721789e1f828bebf4c8999bc Mon Sep 17 00:00:00 2001 From: Johan Stenberg <35264802+jostbg@users.noreply.github.com> Date: Fri, 8 Jun 2018 21:57:24 +0200 Subject: [PATCH 044/207] ARTEMIS-1918 Remove unused private field clientID --- .../core/protocol/core/impl/RemotingConnectionImpl.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/RemotingConnectionImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/RemotingConnectionImpl.java index f24fe87ecb9..db2b5000a60 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/RemotingConnectionImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/RemotingConnectionImpl.java @@ -76,9 +76,6 @@ public class RemotingConnectionImpl extends AbstractRemotingConnection implement private final SimpleString nodeID; - private String clientID; - - @Override public void scheduledFlush() { flush(); @@ -148,7 +145,7 @@ private RemotingConnectionImpl(final PacketDecoder packetDecoder, @Override public String toString() { - return "RemotingConnectionImpl [ID=" + getID() + ", clientID=" + clientID + ", nodeID=" + nodeID + ", transportConnection=" + getTransportConnection() + "]"; + return "RemotingConnectionImpl [ID=" + getID() + ", clientID=" + getClientID() + ", nodeID=" + nodeID + ", transportConnection=" + getTransportConnection() + "]"; } /** From aa1f6a9dd31f6d86fc5534a64e0899b4e165066d Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 7 Jun 2018 16:59:47 -0500 Subject: [PATCH 045/207] ARTEMIS-1917 support logging HTTP access --- .../activemq/artemis/dto/RequestLogDTO.java | 105 ++++++++++++++++++ .../activemq/artemis/dto/WebServerDTO.java | 3 + .../artemis/component/WebServerComponent.java | 71 ++++++++++++ docs/user-manual/en/SUMMARY.md | 1 + docs/user-manual/en/web-server.md | 82 ++++++++++++++ 5 files changed, 262 insertions(+) create mode 100644 artemis-dto/src/main/java/org/apache/activemq/artemis/dto/RequestLogDTO.java create mode 100644 docs/user-manual/en/web-server.md diff --git a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/RequestLogDTO.java b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/RequestLogDTO.java new file mode 100644 index 00000000000..b2522e6890a --- /dev/null +++ b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/RequestLogDTO.java @@ -0,0 +1,105 @@ +/* + * 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.artemis.dto; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "request-log") +@XmlAccessorType(XmlAccessType.FIELD) +public class RequestLogDTO { + + /** + * the output file name of the request log + */ + @XmlAttribute(required = true) + public String filename; + + /** + * append to log + */ + @XmlAttribute + public Boolean append; + + /** + * the extended request log format flag + */ + @XmlAttribute + public Boolean extended; + + /** + * logging of the request cookies + */ + @XmlAttribute + public Boolean logCookies; + + /** + * the output file name of the request log + */ + @XmlAttribute + public String logTimeZone; + + /** + * the log file name date format + */ + @XmlAttribute + public String filenameDateFormat; + + /** + * the number of days before rotated log files are deleted + */ + @XmlAttribute + public Integer retainDays; + + /** + * request paths that will not be logged + */ + @XmlAttribute + public String ignorePaths; + + /** + * the timestamp format string for request log entries + */ + @XmlAttribute + public String logDateFormat; + + /** + * the locale of the request log + */ + @XmlAttribute + public String logLocale; + + /** + * logging of request processing time + */ + @XmlAttribute + public Boolean logLatency; + + /** + * logging of the request hostname + */ + @XmlAttribute + public Boolean logServer; + + /** + * whether the actual IP address of the connection or the IP address from the X-Forwarded-For header will be logged + */ + @XmlAttribute + public Boolean preferProxiedForAddress; +} diff --git a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java index 3e1526184ff..8987b42e0d0 100644 --- a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java +++ b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java @@ -50,6 +50,9 @@ public class WebServerDTO extends ComponentDTO { @XmlElementRef public List apps; + @XmlElementRef(required = false) + public RequestLogDTO requestLog; + @XmlAttribute private String keyStorePassword; diff --git a/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java b/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java index 0a2b2316797..05a9202596c 100644 --- a/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java +++ b/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java @@ -23,6 +23,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import org.apache.activemq.artemis.ActiveMQWebLogger; import org.apache.activemq.artemis.components.ExternalComponent; @@ -35,6 +36,7 @@ import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -42,6 +44,7 @@ import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.server.handler.RequestLogHandler; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.webapp.WebAppContext; @@ -143,12 +146,80 @@ public void configure(ComponentDTO config, String artemisInstance, String artemi DefaultHandler defaultHandler = new DefaultHandler(); defaultHandler.setServeIcon(false); + if (webServerConfig.requestLog != null) { + handlers.addHandler(getLogHandler()); + } handlers.addHandler(homeContext); handlers.addHandler(instanceContext); handlers.addHandler(defaultHandler); server.setHandler(handlers); } + private RequestLogHandler getLogHandler() { + RequestLogHandler requestLogHandler = new RequestLogHandler(); + NCSARequestLog requestLog = new NCSARequestLog(); + + // required via config so no check necessary + requestLog.setFilename(webServerConfig.requestLog.filename); + + if (webServerConfig.requestLog.append != null) { + requestLog.setAppend(webServerConfig.requestLog.append); + } + + if (webServerConfig.requestLog.extended != null) { + requestLog.setExtended(webServerConfig.requestLog.extended); + } + + if (webServerConfig.requestLog.logCookies != null) { + requestLog.setLogCookies(webServerConfig.requestLog.logCookies); + } + + if (webServerConfig.requestLog.logTimeZone != null) { + requestLog.setLogTimeZone(webServerConfig.requestLog.logTimeZone); + } + + if (webServerConfig.requestLog.filenameDateFormat != null) { + requestLog.setFilenameDateFormat(webServerConfig.requestLog.filenameDateFormat); + } + + if (webServerConfig.requestLog.retainDays != null) { + requestLog.setRetainDays(webServerConfig.requestLog.retainDays); + } + + if (webServerConfig.requestLog.ignorePaths != null && webServerConfig.requestLog.ignorePaths.length() > 0) { + String[] split = webServerConfig.requestLog.ignorePaths.split(","); + String[] ignorePaths = new String[split.length]; + for (int i = 0; i < ignorePaths.length; i++) { + ignorePaths[i] = split[i].trim(); + } + requestLog.setIgnorePaths(ignorePaths); + } + + if (webServerConfig.requestLog.logDateFormat != null) { + requestLog.setLogDateFormat(webServerConfig.requestLog.logDateFormat); + } + + if (webServerConfig.requestLog.logLocale != null) { + requestLog.setLogLocale(Locale.forLanguageTag(webServerConfig.requestLog.logLocale)); + } + + if (webServerConfig.requestLog.logLatency != null) { + requestLog.setLogLatency(webServerConfig.requestLog.logLatency); + } + + if (webServerConfig.requestLog.logServer != null) { + requestLog.setLogServer(webServerConfig.requestLog.logServer); + } + + if (webServerConfig.requestLog.preferProxiedForAddress != null) { + requestLog.setPreferProxiedForAddress(webServerConfig.requestLog.preferProxiedForAddress); + } + + requestLogHandler.setRequestLog(requestLog); + + return requestLogHandler; + } + @Override public void start() throws Exception { if (isStarted()) { diff --git a/docs/user-manual/en/SUMMARY.md b/docs/user-manual/en/SUMMARY.md index 86273cc82f8..d86ed8582c2 100644 --- a/docs/user-manual/en/SUMMARY.md +++ b/docs/user-manual/en/SUMMARY.md @@ -58,6 +58,7 @@ * [Graceful Server Shutdown](graceful-shutdown.md) * [Libaio Native Libraries](libaio.md) * [Thread management](thread-pooling.md) +* [Embedded Web Server](web-server.md) * [Logging](logging.md) * [REST Interface](rest.md) * [Embedding the Broker](embedding-activemq.md) diff --git a/docs/user-manual/en/web-server.md b/docs/user-manual/en/web-server.md new file mode 100644 index 00000000000..2a403adb181 --- /dev/null +++ b/docs/user-manual/en/web-server.md @@ -0,0 +1,82 @@ +# Embedded Web Server + +Apache ActiveMQ Artemis embeds the [Jetty web +server](https://www.eclipse.org/jetty/). Its main purpose is to host the [Management +Console](management-console.md). However, it can also host other web +applications like the [REST interface](rest.md) or even Spring-based web +applications (e.g. using Camel). + +## Configuration + +The embedded Jetty instance is configured in `etc/bootstrap.xml` via the `web` +element, e.g.: + +```xml + + + + + +``` + +The `web` element has the following attributes: + +- `bind` The protocol to use (i.e. `http` or `https`) as well as the host and + port on which to listen. +- `path` The name of the subdirectory in which to find the web application + archives (i.e. WAR files). This is a subdirectory of the broker's home or + instance directory. +- `clientAuth` Whether or not clients should present an SSL certificate when + they connect. Only applicable when using `https`. +- `passwordCodec` The custom coded to use for unmasking the `keystorePassword` + and `truststorePassword`. +- `keystorePath` The location on disk of the keystore. Only applicable when + using `https`. +- `keystorePassword` The password to the keystore. Only applicable when using + `https`. Can be masked using `ENC()` syntax or by defining `passwordCodec`. + See more in the [password masking](masking-passwords.md) chapter. +- `truststorePath` The location on disk fo the truststore. Only applicable when + using `https`. +- `truststorePassword` The password to the truststore. Only applicable when + using `https`. Can be masked using `ENC()` syntax or by defining + `passwordCodec`. See more in the [password masking](masking-passwords.md) + chapter. + +Each web application should be defined in an `app` element. The `app` element +has the following attributes: + +- `url` The context to use for the web application. +- `war` The name of the web application archive on disk. + +It's also possible to configure HTTP/S request logging via the `request-log` +element which has the following attributes: + +- `filename` The full path of the request log. This attribute is required. +- `append` Whether or not to append to the existing log or truncate it. Boolean flag. +- `extended` Whether or not to use the extended request log format. Boolean flag. +- `logCookies` Logging of the request cookies. Boolean flag. +- `logTimeZone` The output file name of the request log. +- `filenameDateFormat` The log file name date format. +- `retainDays` The number of days before rotated log files are deleted. +- `ignorePaths` Request paths that will not be logged. Comma delimited list. +- `logDateFormat` The timestamp format string for request log entries. +- `logLocale` The locale of the request log. +- `logLatency` Logging of request processing time. Boolean flag. +- `logServer` Logging of the request hostname. Boolean flag. +- `preferProxiedForAddress` Whether the actual IP address of the connection or + the IP address from the `X-Forwarded-For` header will be logged. Boolean flag. + +These attributes are essentially passed straight through to the underlying +[`org.eclipse.jetty.server.NCSARequestLog`](https://www.eclipse.org/jetty/javadoc/current/org/eclipse/jetty/server/NCSARequestLog.html) +instance. Default values are based on this implementation. + +Here is an example configuration: + +```xml + + + + + + +``` \ No newline at end of file From 69d9b6094a8157bf0a6528420a33aa465b8bcec3 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 19 Jun 2018 17:42:46 -0400 Subject: [PATCH 046/207] ARTEMIS-1924 Test consumer cleanup after socket connection reset --- .../integration/amqp/AmqpNoHearbeatsTest.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java index b35ef7d34db..6e35c02756b 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java @@ -16,15 +16,23 @@ */ package org.apache.activemq.artemis.tests.integration.amqp; +import java.net.URI; import java.util.Arrays; import java.util.Collection; import java.util.Map; +import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.tests.util.SpawnedVMSupport; import org.apache.activemq.transport.amqp.client.AmqpClient; import org.apache.activemq.transport.amqp.client.AmqpConnection; +import org.apache.activemq.transport.amqp.client.AmqpMessage; +import org.apache.activemq.transport.amqp.client.AmqpReceiver; +import org.apache.activemq.transport.amqp.client.AmqpSender; +import org.apache.activemq.transport.amqp.client.AmqpSession; import org.apache.activemq.transport.amqp.client.AmqpValidator; import org.apache.qpid.proton.engine.Connection; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -80,4 +88,68 @@ public void inspectOpenedResource(Connection connection) { connection.close(); } + private static final String QUEUE_NAME = "queue://testHeartless"; + + // This test is validating a scenario where the client will leave with connection reset + // This is done by setting soLinger=0 on the socket, which will make the system to issue a connection.reset instead of sending a + // disconnect. + @Test(timeout = 60000) + public void testCloseConsumerOnConnectionReset() throws Exception { + + AmqpClient client = createAmqpClient(); + assertNotNull(client); + + client.setValidator(new AmqpValidator() { + + @Override + public void inspectOpenedResource(Connection connection) { + assertEquals("idle timeout was not disabled", 0, connection.getTransport().getRemoteIdleTimeout()); + } + }); + + AmqpConnection connection = addConnection(client.connect()); + assertNotNull(connection); + + connection.getStateInspector().assertValid(); + AmqpSession session = connection.createSession(); + AmqpReceiver receiver = session.createReceiver(QUEUE_NAME); + + // This test needs a remote process exiting without closing the socket + // with soLinger=0 on the socket so it will issue a connection.reset + Process p = SpawnedVMSupport.spawnVM(AmqpNoHearbeatsTest.class.getName(), "testConnectionReset"); + Assert.assertEquals(33, p.waitFor()); + + AmqpSender sender = session.createSender(QUEUE_NAME); + + for (int i = 0; i < 10; i++) { + AmqpMessage msg = new AmqpMessage(); + msg.setBytes(new byte[] {0}); + sender.send(msg); + } + + receiver.flow(20); + + for (int i = 0; i < 10; i++) { + AmqpMessage msg = receiver.receive(1, TimeUnit.SECONDS); + Assert.assertNotNull(msg); + msg.accept(); + } + } + + public static void main(String[] arg) { + if (arg.length > 0 && arg[0].equals("testConnectionReset")) { + try { + AmqpClient client = new AmqpClient(new URI("tcp://127.0.0.1:5672?transport.soLinger=0"), null, null); + AmqpConnection connection = client.connect(); + AmqpSession session = connection.createSession(); + AmqpReceiver receiver = session.createReceiver(QUEUE_NAME); + receiver.flow(10); + System.exit(33); + } catch (Throwable e) { + e.printStackTrace(); + System.exit(-1); + } + } + } + } From f4097fb01a489d56dde1678ab59115ce06260c51 Mon Sep 17 00:00:00 2001 From: vkamble60 Date: Wed, 20 Jun 2018 12:32:12 +0530 Subject: [PATCH 047/207] [ARTEMIS-1944] Typo mistake in broker.xml at journal-buffer-timeout explanation --- .../artemis/cli/commands/etc/journal-buffer-settings.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/journal-buffer-settings.txt b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/journal-buffer-settings.txt index fc9e2ba1fe1..f7668a29019 100644 --- a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/journal-buffer-settings.txt +++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/journal-buffer-settings.txt @@ -6,7 +6,7 @@ That translates as a sync write every ${nanoseconds} nanoseconds. Note: If you specify 0 the system will perform writes directly to the disk. - We recommend this to be 0 if you are using journalType=MAPPED and ournal-datasync=false. + We recommend this to be 0 if you are using journalType=MAPPED and journal-datasync=false. --> ${nanoseconds} From e035487a5f352e5193d10dfa7f2fdb8f68e617bf Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Tue, 19 Jun 2018 18:21:03 +0200 Subject: [PATCH 048/207] ARTEMIS-1945 InVMNodeManager shared state should be volatile State access should be atomic to be visibile between different threads. --- .../activemq/artemis/core/server/impl/InVMNodeManager.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 2719d66c21d..a3bce410e83 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 @@ -49,7 +49,7 @@ public enum State { LIVE, PAUSED, FAILING_BACK, NOT_STARTED } - public State state = NOT_STARTED; + public volatile State state = NOT_STARTED; public long failoverPause = 0L; @@ -147,9 +147,7 @@ public void interrupt() { @Override public void releaseBackup() { - if (backupLock != null) { - backupLock.release(); - } + backupLock.release(); } @Override From 6f72e0b4e53c03e468426678ed372fadf8cca0ee Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 20 Jun 2018 10:20:42 -0400 Subject: [PATCH 049/207] ARTEMIS-1924 small tweaks on HeartBeat test --- .../integration/amqp/AmqpNoHearbeatsTest.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java index 6e35c02756b..a7e82d04825 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpNoHearbeatsTest.java @@ -40,6 +40,11 @@ @RunWith(Parameterized.class) public class AmqpNoHearbeatsTest extends AmqpClientTestSupport { + private static final int OK = 0x33; + + @Parameterized.Parameter(0) + public boolean useOverride; + @Parameterized.Parameters(name = "useOverride={0}") public static Collection parameters() { return Arrays.asList(new Object[][] { @@ -47,8 +52,6 @@ public static Collection parameters() { }); } - @Parameterized.Parameter(0) - public boolean useOverride; @Override protected void addConfiguration(ActiveMQServer server) { @@ -88,8 +91,6 @@ public void inspectOpenedResource(Connection connection) { connection.close(); } - private static final String QUEUE_NAME = "queue://testHeartless"; - // This test is validating a scenario where the client will leave with connection reset // This is done by setting soLinger=0 on the socket, which will make the system to issue a connection.reset instead of sending a // disconnect. @@ -112,14 +113,14 @@ public void inspectOpenedResource(Connection connection) { connection.getStateInspector().assertValid(); AmqpSession session = connection.createSession(); - AmqpReceiver receiver = session.createReceiver(QUEUE_NAME); + AmqpReceiver receiver = session.createReceiver(getQueueName()); // This test needs a remote process exiting without closing the socket // with soLinger=0 on the socket so it will issue a connection.reset - Process p = SpawnedVMSupport.spawnVM(AmqpNoHearbeatsTest.class.getName(), "testConnectionReset"); - Assert.assertEquals(33, p.waitFor()); + Process p = SpawnedVMSupport.spawnVM(AmqpNoHearbeatsTest.class.getName(), getTestName(), getQueueName()); + Assert.assertEquals(OK, p.waitFor()); - AmqpSender sender = session.createSender(QUEUE_NAME); + AmqpSender sender = session.createSender(getQueueName()); for (int i = 0; i < 10; i++) { AmqpMessage msg = new AmqpMessage(); @@ -137,18 +138,22 @@ public void inspectOpenedResource(Connection connection) { } public static void main(String[] arg) { - if (arg.length > 0 && arg[0].equals("testConnectionReset")) { + if (arg.length == 2 && arg[0].startsWith("testCloseConsumerOnConnectionReset")) { try { + String queueName = arg[1]; AmqpClient client = new AmqpClient(new URI("tcp://127.0.0.1:5672?transport.soLinger=0"), null, null); AmqpConnection connection = client.connect(); AmqpSession session = connection.createSession(); - AmqpReceiver receiver = session.createReceiver(QUEUE_NAME); + AmqpReceiver receiver = session.createReceiver(queueName); receiver.flow(10); - System.exit(33); + System.exit(OK); } catch (Throwable e) { e.printStackTrace(); System.exit(-1); } + } else { + System.err.println("Test " + arg[0] + " unkown"); + System.exit(-2); } } From 2ed8a5764b1d1c2ae8069e36ca25fb1418358d8f Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Wed, 20 Jun 2018 11:02:30 -0500 Subject: [PATCH 050/207] ARTEMIS-1947 return metadata in JSON session mgmnt ops --- .../impl/ActiveMQServerControlImpl.java | 32 +++++++++++-------- .../artemis/core/server/ServerSession.java | 2 ++ .../core/server/impl/ServerSessionImpl.java | 5 +++ .../management/ActiveMQServerControlTest.java | 28 ++++++++++++++++ 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java index 58f26131858..cabdc1050f9 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java @@ -1849,13 +1849,7 @@ public String listSessionsAsJSON(final String connectionID) throws Exception { try { List sessions = server.getSessions(connectionID); for (ServerSession sess : sessions) { - JsonObjectBuilder obj = JsonLoader.createObjectBuilder().add("sessionID", sess.getName()).add("creationTime", sess.getCreationTime()).add("consumerCount", sess.getServerConsumers().size()); - - if (sess.getValidatedUser() != null) { - obj.add("principal", sess.getValidatedUser()); - } - - array.add(obj); + buildSessionJSON(array, sess); } } finally { blockOnIO(); @@ -1873,13 +1867,7 @@ public String listAllSessionsAsJSON() throws Exception { try { Set sessions = server.getSessions(); for (ServerSession sess : sessions) { - JsonObjectBuilder obj = JsonLoader.createObjectBuilder().add("sessionID", sess.getName()).add("creationTime", sess.getCreationTime()).add("consumerCount", sess.getServerConsumers().size()); - - if (sess.getValidatedUser() != null) { - obj.add("principal", sess.getValidatedUser()); - } - - array.add(obj); + buildSessionJSON(array, sess); } } finally { blockOnIO(); @@ -1887,6 +1875,22 @@ public String listAllSessionsAsJSON() throws Exception { return array.build().toString(); } + public void buildSessionJSON(JsonArrayBuilder array, ServerSession sess) { + JsonObjectBuilder obj = JsonLoader.createObjectBuilder().add("sessionID", sess.getName()).add("creationTime", sess.getCreationTime()).add("consumerCount", sess.getServerConsumers().size()); + + if (sess.getValidatedUser() != null) { + obj.add("principal", sess.getValidatedUser()); + } + + String metadata = sess.getMetaData() == null ? null : sess.getMetaData().toString(); + if (metadata != null) { + // remove leading and trailing curly brackets + obj.add("metadata", metadata.substring(1, metadata.length() - 1)); + } + + array.add(obj); + } + @Override public String listConsumersAsJSON(String connectionID) throws Exception { checkStarted(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java index f5cca75ac3c..8c2f74885d0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java @@ -251,6 +251,8 @@ RoutingStatus doSend(Transaction tx, String getMetaData(String key); + Map getMetaData(); + String[] getTargetAddresses(); /** diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java index f00fe1916a7..7388e568e28 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java @@ -1547,6 +1547,11 @@ public String getMetaData(String key) { return data; } + @Override + public Map getMetaData() { + return metaData; + } + @Override public String[] getTargetAddresses() { Map> copy = cloneTargetAddresses(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java index b79fceda167..048d5067999 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java @@ -71,6 +71,8 @@ import org.apache.activemq.artemis.core.settings.impl.SlowConsumerPolicy; import org.apache.activemq.artemis.core.transaction.impl.XidImpl; import org.apache.activemq.artemis.jlibaio.LibaioContext; +import org.apache.activemq.artemis.jms.client.ActiveMQConnection; +import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQSession; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule; @@ -1523,6 +1525,8 @@ public void testListAllSessionsAsJSON() throws Exception { ClientSession session1 = addClientSession(factory.createSession()); Thread.sleep(5); ClientSession session2 = addClientSession(factory2.createSession("myUser", "myPass", false, false, false, false, 0)); + session2.addMetaData("foo", "bar"); + session2.addMetaData("bar", "baz"); session2.createConsumer(queueName); String jsonString = serverControl.listAllSessionsAsJSON(); @@ -1546,6 +1550,30 @@ public void testListAllSessionsAsJSON() throws Exception { Assert.assertEquals("myUser", second.getString("principal")); Assert.assertTrue(second.getJsonNumber("creationTime").longValue() > 0); Assert.assertEquals(1, second.getJsonNumber("consumerCount").longValue()); + Assert.assertTrue(second.getJsonString("metadata").getString().contains("foo=bar")); + Assert.assertTrue(second.getJsonString("metadata").getString().contains("bar=baz")); + } + + @Test + public void testListAllSessionsAsJSONWithJMS() throws Exception { + SimpleString queueName = new SimpleString(UUID.randomUUID().toString()); + server.addAddressInfo(new AddressInfo(queueName, RoutingType.ANYCAST)); + server.createQueue(queueName, RoutingType.ANYCAST, queueName, null, false, false); + ActiveMQServerControl serverControl = createManagementControl(); + + ActiveMQConnectionFactory cf = ActiveMQJMSClient.createConnectionFactory("vm://0", "cf"); + Connection con = cf.createConnection(); + String clientID = UUID.randomUUID().toString(); + con.setClientID(clientID); + + String jsonString = serverControl.listAllSessionsAsJSON(); + IntegrationTestLogger.LOGGER.info(jsonString); + Assert.assertNotNull(jsonString); + JsonArray array = JsonUtil.readJsonArray(jsonString); + Assert.assertEquals(1 + (usingCore() ? 1 : 0), array.size()); + JsonObject obj = lookupSession(array, ((ActiveMQConnection)con).getInitialSession()); + Assert.assertTrue(obj.getString("metadata").contains(ClientSession.JMS_SESSION_CLIENT_ID_PROPERTY + "=" + clientID)); + Assert.assertTrue(obj.getString("metadata").contains(ClientSession.JMS_SESSION_IDENTIFIER_PROPERTY)); } @Test From d805074b94ce58e83c0f8950a525e44050dfa280 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 20 Jun 2018 18:40:11 -0400 Subject: [PATCH 051/207] ARTEMIS-1948 dotnet example with high performant load --- .../amqp/dotnet/HighPerformanceLoad/App.cs | 73 ++++++++++++ .../HighPerformance.csproj | 29 +++++ .../dotnet/HighPerformanceLoad/Producer.cs | 96 +++++++++++++++ .../HighPerformanceLoad/ReceiverPool.cs | 110 ++++++++++++++++++ .../HighPerformanceLoad/TokenBucketLimiter.cs | 81 +++++++++++++ .../amqp/dotnet/HighPerformanceLoad/readme.md | 62 ++++++++++ .../HighPerformanceLoad/start-server.sh | 29 +++++ 7 files changed, 480 insertions(+) create mode 100644 examples/protocols/amqp/dotnet/HighPerformanceLoad/App.cs create mode 100644 examples/protocols/amqp/dotnet/HighPerformanceLoad/HighPerformance.csproj create mode 100644 examples/protocols/amqp/dotnet/HighPerformanceLoad/Producer.cs create mode 100644 examples/protocols/amqp/dotnet/HighPerformanceLoad/ReceiverPool.cs create mode 100644 examples/protocols/amqp/dotnet/HighPerformanceLoad/TokenBucketLimiter.cs create mode 100644 examples/protocols/amqp/dotnet/HighPerformanceLoad/readme.md create mode 100755 examples/protocols/amqp/dotnet/HighPerformanceLoad/start-server.sh diff --git a/examples/protocols/amqp/dotnet/HighPerformanceLoad/App.cs b/examples/protocols/amqp/dotnet/HighPerformanceLoad/App.cs new file mode 100644 index 00000000000..c1a94b123d5 --- /dev/null +++ b/examples/protocols/amqp/dotnet/HighPerformanceLoad/App.cs @@ -0,0 +1,73 @@ +/** + * 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. + */ +using System; +using System.Threading; +using Amqp.Framing; +using Amqp; +using System.Threading.Tasks; + +namespace Artemis.Perf +{ + class App + { + static long ReceivedMessages = 0; + + static void TheCallback(int id, Session session, ReceiverLink link, Message message) + { + Interlocked.Increment(ref ReceivedMessages); + link.Accept(message); + } + + static void Main(string[] args) { + + if (args.Length == 0) { + args = new string[1]; + args[0] = "amqp://127.0.0.1:5672"; + } + + // it will start one client towards each server + for (int i = 0; i < args.Length; i++) { + string addr0 = args.Length >= 1 ? args[0] : "amqp://127.0.0.1:5672"; + processOn(addr0, "orders", 100000000, 25000, "p1"); + } + + while (true) { + long previousRead = Interlocked.Read(ref ReceivedMessages); + long previousSent = Interlocked.Read(ref Producer.totalSent); + Thread.Sleep(1000); + long currentRead = Interlocked.Read(ref ReceivedMessages); + long currentSent = Interlocked.Read(ref Producer.totalSent); + Console.WriteLine("Received: " + currentRead + " TotalSent: " + + currentSent); + Console.WriteLine("Rate reading: " + (currentRead - previousRead) + ", Rate sending: " + (currentSent - previousSent)); + } + + } + static void processOn(string addr, string queue, int totalSend, int maxRateSend, String processName) { + Address address = new Address(addr); + + Connection connection = new Connection(address); + + ReceiverPool pool = new ReceiverPool(connection, 1, queue, 200, TheCallback); + pool.start(); + + Producer Producer = new Producer(processName, addr, queue, totalSend, maxRateSend); + Producer.produce(); // this will start an asynchronous producer + + } + } +} diff --git a/examples/protocols/amqp/dotnet/HighPerformanceLoad/HighPerformance.csproj b/examples/protocols/amqp/dotnet/HighPerformanceLoad/HighPerformance.csproj new file mode 100644 index 00000000000..99a2d1a2ffc --- /dev/null +++ b/examples/protocols/amqp/dotnet/HighPerformanceLoad/HighPerformance.csproj @@ -0,0 +1,29 @@ + + + + + + Exe + netcoreapp2.0 + + + + + + + diff --git a/examples/protocols/amqp/dotnet/HighPerformanceLoad/Producer.cs b/examples/protocols/amqp/dotnet/HighPerformanceLoad/Producer.cs new file mode 100644 index 00000000000..8e9cefe51a2 --- /dev/null +++ b/examples/protocols/amqp/dotnet/HighPerformanceLoad/Producer.cs @@ -0,0 +1,96 @@ +/** + * 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. + */ + +using System; +using System.Threading; +using Amqp.Framing; +using Amqp; +using System.Threading.Tasks; + +namespace Artemis.Perf +{ + public class Producer + { + string name; + + string addr; + string queue; + int numberOfMessages; + int messagesPerSecond; + long messagesSent; + + public static long totalSent; + + public Producer(string name, string addr, string queue, int numberOfMessages, int messagesPerSecond) { + this.name = name; + this.addr = addr; + this.queue = queue; + this.numberOfMessages = numberOfMessages; + this.messagesPerSecond = messagesPerSecond; + } + + + public void produce() { + Address address = new Address(addr); + + Connection connection = new Connection(address); + + + Session session = new Session(connection); + SenderLink sender = new SenderLink(session, "sender", queue); + + OutcomeCallback callback = (l, msg, o, s) => { + Interlocked.Increment(ref messagesSent); + Interlocked.Increment(ref totalSent); + }; + + // This is just to limit the number of messages per second we are sending + TokenBucketLimiterImpl tokens = new TokenBucketLimiterImpl(messagesPerSecond); + + Task.Factory.StartNew(() => { + Console.WriteLine("Sending {0} messages...", numberOfMessages); + for (var i = 0; i < numberOfMessages; i++) + { + tokens.limit(); + Message message = new Message("a message!" + i); + message.Header = new Header(); + message.Header.Durable = true; + + // The callback here is to make the sending to happen as fast as possible + sender.Send(message, callback, null); + } + Console.WriteLine(".... Done sending"); + }, TaskCreationOptions.LongRunning); + + // Trace.TraceLevel = TraceLevel.Verbose | TraceLevel.Error | + // TraceLevel.Frame | TraceLevel.Information | TraceLevel.Warning; + // Trace.TraceListener = (l, f, o) => Console.WriteLine(DateTime.Now.ToString("[hh:mm:ss.fff]") + " " + string.Format(f, o)); + + // sender.Close(); + + // Task.Factory.StartNew(() => { + // while (true) { + // Console.WriteLine("Sent " + Interlocked.Read(ref messagesSent) + " on queue " + queue + " producer " + this.name); + // Thread.Sleep(1000); + // } + // }, TaskCreationOptions.LongRunning); + + + + } + } +} diff --git a/examples/protocols/amqp/dotnet/HighPerformanceLoad/ReceiverPool.cs b/examples/protocols/amqp/dotnet/HighPerformanceLoad/ReceiverPool.cs new file mode 100644 index 00000000000..dce841a2791 --- /dev/null +++ b/examples/protocols/amqp/dotnet/HighPerformanceLoad/ReceiverPool.cs @@ -0,0 +1,110 @@ +/* + * 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. + */ + +using System; +using System.Threading; +using Amqp.Framing; +using Amqp; +using System.Threading.Tasks; + +namespace Artemis.Perf +{ + /** + * This class will start many consumers underneath it to satisfy a pool of consumers + * While calling a single callback for when messages are received. + */ + public class ReceiverPool + { + public delegate void MessageReceived(int id, Session session, ReceiverLink link, Message msg); + + public int MessagesReceived; + + MessageReceived _callback; + int _Workers; + private Object receiverLock = new Object(); + private Boolean running = true; + + private ReceiverLink[] Receivers; + private Session[] Sessions; + + private Connection _Connection; + + private int Credits; + + public ReceiverPool(Connection Connection, int Workers, String queue, int Credits, MessageReceived callback) + { + this._Connection = Connection; + this.Receivers = new ReceiverLink[Workers]; + this.Sessions = new Session[Workers]; + + + for (int i = 0; i < Workers; i++) + { + + // I was playing with using a single session versus multiple sessions + if (i == 0) { + Sessions[i] = new Session(Connection); + } + else { + Sessions[i] = Sessions[0]; + } + Receivers[i] = new ReceiverLink(Sessions[i], "receiver " + queue + " " + i, queue); + } + this._Workers = Workers; + this._callback = callback; + this.Credits = Credits; + } + + + public void stop() { + running = false; + for (int i = 0; i < _Workers; i++) { + Receivers[i].Close(); + Sessions[i].Close(); + } + } + + + public void start() { + for (int i = 0; i < _Workers; i++) { + { + // This variable exists otherwise we would get an olderValue of i + int value = i; + Task.Factory.StartNew(() => WorkerRun(value), TaskCreationOptions.LongRunning); + } + } + } + + void WorkerRun(int i) { + try { + Receivers[i].SetCredit(Credits); + while (running) + { + Message theMessage = Receivers[i].Receive(TimeSpan.FromSeconds(1)); + + if (theMessage != null) + { + _callback(i, Sessions[i], Receivers[i], theMessage); + } + } + } catch (Exception e) { + Console.WriteLine(e); + } + } + } + +} \ No newline at end of file diff --git a/examples/protocols/amqp/dotnet/HighPerformanceLoad/TokenBucketLimiter.cs b/examples/protocols/amqp/dotnet/HighPerformanceLoad/TokenBucketLimiter.cs new file mode 100644 index 00000000000..2685e921ff5 --- /dev/null +++ b/examples/protocols/amqp/dotnet/HighPerformanceLoad/TokenBucketLimiter.cs @@ -0,0 +1,81 @@ +/* + * 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. + */ + +using System; +using System.Threading; +using Amqp.Framing; +using Amqp; +using System.Threading.Tasks; + +namespace Artemis.Perf +{ + + // this has been copied from Artemis' TokenBucketLimiter with some modifications + public class TokenBucketLimiterImpl { + + private int rate; + + /** + * Even thought we don't use TokenBucket in multiThread + * the implementation should keep this volatile for correctness + */ + private long last; + + /** + * Even thought we don't use TokenBucket in multiThread + * the implementation should keep this volatile for correctness + */ + private int tokens; + + public TokenBucketLimiterImpl(int rate) { + this.rate = rate; + } + private bool checkRate() { + + long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + + if (last == 0) { + last = now; + } + + long diff = now - last; + + if (diff >= 1000) { + last = now; + + tokens = rate; + } + + if (tokens > 0) { + tokens--; + + return true; + } else { + return false; + } + } + + public void limit() { + if (!checkRate()) { + // Console.WriteLine("Limiting messages per max rate"); + do { + Thread.Sleep(1); + } while (!checkRate()); + } + } + } +} \ No newline at end of file diff --git a/examples/protocols/amqp/dotnet/HighPerformanceLoad/readme.md b/examples/protocols/amqp/dotnet/HighPerformanceLoad/readme.md new file mode 100644 index 00000000000..65be3742d52 --- /dev/null +++ b/examples/protocols/amqp/dotnet/HighPerformanceLoad/readme.md @@ -0,0 +1,62 @@ +# Running the .NET AMQP example + + +# Pre-requisites: + +All of this can be done on Linux, Mac and... Windows + +- Install .NET + +https://www.microsoft.com/net/core + + +- Visual Studio Code is free and may be useful: + +https://code.visualstudio.com + + +- Powershell might be also useful: + +https://github.com/PowerShell/PowerShell/ + + + +# running the example + +- Create and start the broker, by running: + +```bash +./start-server.sh +``` + +This broker is created by simply using the CLI. you may do it manually if you like: + +```bash +artemis create ./server1 --user a --password a --role a --allow-anonymous --force +cd server1/bin +./artemis run +``` + +- Compile the code + +You need call restore to download AMQP Library and build it. +Restore is part of NuGET which is sort of the Maven Repo for Java devs. + +```sh +dotnet restore +dotnet build +dotnet run +``` + +Or simply use the run-example.sh script on this directory + +- Debugging + +Visual Studio Code will make it fairly easy to do it + + +# About this example + +This is sending messages, limited to 25K messages a second. +The consumer will have a pool of consumers, which will synchronously acknowledge messages. +.NET threading model is expensive, this example shows how to make most of your resources by a pool of consumers. diff --git a/examples/protocols/amqp/dotnet/HighPerformanceLoad/start-server.sh b/examples/protocols/amqp/dotnet/HighPerformanceLoad/start-server.sh new file mode 100755 index 00000000000..4b50a0cb0bb --- /dev/null +++ b/examples/protocols/amqp/dotnet/HighPerformanceLoad/start-server.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env sh +# 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. + +# Setting the script to fail if anything goes wrong +set -e + +rm -rf ./server1 +../../../../../bin/artemis create ./server1 --user a --password a --role a --allow-anonymous --force +./target/server0/bin/artemis run +#sleep 1 +#../../../../target/clustered-static-node1/bin/artemis run | tee server2.log & +#sleep 1 +#../../../../target/clustered-static-node2/bin/artemis run | tee server3.log & +#sleep 1 From cca73dc7dd45ddc34699546bd59c0588edb57db6 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 21 Jun 2018 15:53:27 -0500 Subject: [PATCH 052/207] ARTEMIS-1950 clarify STOMP durable sub header names --- docs/user-manual/en/stomp.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/user-manual/en/stomp.md b/docs/user-manual/en/stomp.md index 8ba11f2bd1f..4abe8d40117 100644 --- a/docs/user-manual/en/stomp.md +++ b/docs/user-manual/en/stomp.md @@ -220,6 +220,15 @@ To delete a durable subscription the `client-id` header must be set on the `UNSUBSCRIBE` frame. The values for these headers should match what was set on the `SUBSCRIBE` frame to delete the corresponding durable subscription. +Aside from `durable-subscription-name`, the broker also supports +`durable-subscriber-name` (a deprecated property used before +`durable-subscription-name`) as well as `activemq.subscriptionName` from ActiveMQ +5.x. This is the order of precedence if the frame contains more than one of these: + +1) `durable-subscriber-name` +2) `durable-subscription-name` +3) `activemq.subscriptionName` + It is possible to pre-configure durable subscriptions since the STOMP implementation creates the queue used for the durable subscription in a deterministic way (i.e. using the format of `client-id`.`subscription-name`). From 023feaed373816e36c519bb1963b46f7f855b436 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 21 Jun 2018 14:41:57 -0500 Subject: [PATCH 053/207] NO-JIRA add doc on recent releases --- docs/user-manual/en/versions.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/user-manual/en/versions.md b/docs/user-manual/en/versions.md index 9bb33d6387e..0ba675cde13 100644 --- a/docs/user-manual/en/versions.md +++ b/docs/user-manual/en/versions.md @@ -1,12 +1,33 @@ # Versions -This chapter provides the information for each release: +This chapter provides the following information for each release: - A link to the full release notes which includes all issues resolved in the release. -- A brief list of "highlights." +- A brief list of "highlights" when applicable. - If necessary, specific steps required when upgrading from the previous version. - **Note:** If the upgrade spans multiple versions then the steps from **each** version need to be followed in order. - **Note:** Follow the general upgrade procedure outlined in the [Upgrading the Broker](upgrading.md) - chapter in addition to any version-specific upgrade instructions outlined here. + chapter in addition to any version-specific upgrade instructions outlined here. + +## 2.6.2 + +[Full release notes](https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=12315920&version=12343404). + +This was a bug-fix release with no substantial new features or improvements. + +## 2.6.1 + +[Full release notes](https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=12315920&version=12343356). + +This was a bug-fix release with no substantial new features or improvements. + +## 2.6.0 + +[Full release notes](https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=12315920&version=12342903). + +Highlights: +- Support [regular expressions for matching client certificates](security.md#certificateloginmodule). +- Support `SASL_EXTERNAL` for AMQP clients. +- New examples showing [virtual topic mapping](examples.md#openwire) and [exclusive queue](examples.md#exclusive-queue) features. ## 2.5.0 From 754a263328fe01cd2ce95b44440067c329a80771 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 21 Jun 2018 12:50:11 -0500 Subject: [PATCH 054/207] ARTEMIS-1951 allow queue's user to be updated --- .../management/ActiveMQServerControl.java | 23 +++++++++++++++++++ .../impl/ActiveMQServerControlImpl.java | 14 ++++++++++- .../artemis/core/postoffice/PostOffice.java | 3 ++- .../core/postoffice/impl/PostOfficeImpl.java | 7 +++++- .../artemis/core/server/ActiveMQServer.java | 7 ++++++ .../activemq/artemis/core/server/Queue.java | 7 +++++- .../core/server/impl/ActiveMQServerImpl.java | 17 ++++++++++++-- .../artemis/core/server/impl/QueueImpl.java | 7 +++++- .../impl/ScheduledDeliveryHandlerTest.java | 6 +++++ .../integration/client/UpdateQueueTest.java | 12 +++++++--- .../ActiveMQServerControlUsingCoreTest.java | 11 +++++++++ .../unit/core/postoffice/impl/FakeQueue.java | 5 ++++ .../server/impl/fakes/FakePostOffice.java | 3 ++- 13 files changed, 111 insertions(+), 11 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java index 83f66b7aea9..234a2d514c8 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java @@ -605,6 +605,7 @@ String createQueue(@Parameter(name = "address", desc = "Address of the queue") S * @return a textual summary of the queue * @throws Exception */ + @Deprecated @Operation(desc = "Update a queue", impact = MBeanOperationInfo.ACTION) String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String name, @Parameter(name = "routingType", desc = "The routing type used for this address, MULTICAST or ANYCAST") String routingType, @@ -618,9 +619,11 @@ String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String * @param routingType the routing type used for this address, {@code MULTICAST} or {@code ANYCAST} * @param maxConsumers the maximum number of consumers allowed on this queue at any one time * @param purgeOnNoConsumers delete this queue when the last consumer disconnects + * @param exclusive if the queue should route exclusively to one consumer * @return a textual summary of the queue * @throws Exception */ + @Deprecated @Operation(desc = "Update a queue", impact = MBeanOperationInfo.ACTION) String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String name, @Parameter(name = "routingType", desc = "The routing type used for this address, MULTICAST or ANYCAST") String routingType, @@ -628,6 +631,26 @@ String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String @Parameter(name = "purgeOnNoConsumers", desc = "Delete this queue when the last consumer disconnects") Boolean purgeOnNoConsumers, @Parameter(name = "exclusive", desc = "If the queue should route exclusively to one consumer") Boolean exclusive) throws Exception; + /** + * Update a queue + * + * @param name name of the queue + * @param routingType the routing type used for this address, {@code MULTICAST} or {@code ANYCAST} + * @param maxConsumers the maximum number of consumers allowed on this queue at any one time + * @param purgeOnNoConsumers delete this queue when the last consumer disconnects + * @param exclusive if the queue should route exclusively to one consumer + * @param user the user associated with this queue + * @return + * @throws Exception + */ + @Operation(desc = "Update a queue", impact = MBeanOperationInfo.ACTION) + String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String name, + @Parameter(name = "routingType", desc = "The routing type used for this address, MULTICAST or ANYCAST") String routingType, + @Parameter(name = "maxConsumers", desc = "The maximum number of consumers allowed on this queue at any one time") Integer maxConsumers, + @Parameter(name = "purgeOnNoConsumers", desc = "Delete this queue when the last consumer disconnects") Boolean purgeOnNoConsumers, + @Parameter(name = "exclusive", desc = "If the queue should route exclusively to one consumer") Boolean exclusive, + @Parameter(name = "user", desc = "The user associated with this queue") String user) throws Exception; + /** * Deploy a durable queue. *
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java index cabdc1050f9..9ed7acdf3de 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java @@ -825,6 +825,7 @@ public String createQueue(String address, } } + @Deprecated @Override public String updateQueue(String name, String routingType, @@ -833,18 +834,29 @@ public String updateQueue(String name, return updateQueue(name, routingType, maxConsumers, purgeOnNoConsumers, null); } + @Deprecated @Override public String updateQueue(String name, String routingType, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive) throws Exception { + return updateQueue(name, routingType, maxConsumers, purgeOnNoConsumers, exclusive, null); + } + + @Override + public String updateQueue(String name, + String routingType, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive, + String user) throws Exception { checkStarted(); clearIO(); try { - final Queue queue = server.updateQueue(name, routingType != null ? RoutingType.valueOf(routingType) : null, maxConsumers, purgeOnNoConsumers, exclusive); + final Queue queue = server.updateQueue(name, routingType != null ? RoutingType.valueOf(routingType) : null, maxConsumers, purgeOnNoConsumers, exclusive, user); if (queue == null) { throw ActiveMQMessageBundle.BUNDLE.noSuchQueue(new SimpleString(name)); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java index 19ddd94f39c..024a80671d3 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java @@ -68,7 +68,8 @@ QueueBinding updateQueue(SimpleString name, RoutingType routingType, Integer maxConsumers, Boolean purgeOnNoConsumers, - Boolean exclusive) throws Exception; + Boolean exclusive, + String user) throws Exception; List listQueuesForAddress(SimpleString address) throws Exception; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index 5fbb2d8dc92..e3df0c40067 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -467,7 +467,8 @@ public QueueBinding updateQueue(SimpleString name, RoutingType routingType, Integer maxConsumers, Boolean purgeOnNoConsumers, - Boolean exclusive) throws Exception { + Boolean exclusive, + String user) throws Exception { synchronized (addressLock) { final QueueBinding queueBinding = (QueueBinding) addressManager.getBinding(name); if (queueBinding == null) { @@ -511,6 +512,10 @@ public QueueBinding updateQueue(SimpleString name, changed = true; queue.setExclusive(exclusive); } + if ((user != null && !user.equals(queue.getUser()) || (user == null && queue.getUser() != null))) { + changed = true; + queue.setUser(SimpleString.toSimpleString(user)); + } if (changed) { final long txID = storageManager.generateID(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java index 78ebbb710cf..c2e4cbf2b16 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java @@ -445,6 +445,13 @@ Queue updateQueue(String name, Boolean purgeOnNoConsumers, Boolean exclusive) throws Exception; + Queue updateQueue(String name, + RoutingType routingType, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive, + String user) throws Exception; + /* * add a ProtocolManagerFactory to be used. Note if @see Configuration#isResolveProtocols is tur then this factory will * replace any factories with the same protocol diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java index c549be9bf32..0e16718af64 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java @@ -352,10 +352,15 @@ int moveReferences(int flushLimit, float getRate(); /** - * @return the user who created this queue + * @return the user associated with this queue */ SimpleString getUser(); + /** + * @param user the user associated with this queue + */ + void setUser(SimpleString user); + /** This is to perform a check on the counter again */ void recheckRefCount(OperationContext context); 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 ebe0401a1d5..1e722aa00c2 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 @@ -2546,7 +2546,8 @@ private void deployQueuesFromListCoreQueueConfiguration(List Date: Fri, 22 Jun 2018 14:27:20 -0400 Subject: [PATCH 055/207] NO-JIRA Adding Redistribution & Loadbalancing test with AMQP --- .../ProtocolsMessageLoadBalancingTest.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java index 8ed685cf362..ae413924082 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java @@ -367,6 +367,62 @@ public void testRestartConnection() throws Exception { } + @Test + public void testRedistributeAfterLoadBalanced() throws Exception { + + startServers(MessageLoadBalancingType.ON_DEMAND); + + ConnectionFactory[] factory = new ConnectionFactory[NUMBER_OF_SERVERS]; + Connection[] connection = new Connection[NUMBER_OF_SERVERS]; + Session[] session = new Session[NUMBER_OF_SERVERS]; + MessageConsumer[] consumer = new MessageConsumer[NUMBER_OF_SERVERS]; + + // this will pre create consumers to make sure messages are distributed evenly without redistribution + for (int node = 0; node < NUMBER_OF_SERVERS; node++) { + factory[node] = getJmsConnectionFactory(node); + connection[node] = factory[node].createConnection(); + session[node] = connection[node].createSession(false, Session.AUTO_ACKNOWLEDGE); + consumer[node] = session[node].createConsumer(session[node].createQueue(queueName.toString())); + } + + waitForBindings(0, "queues.0", 1, 1, true); + waitForBindings(1, "queues.0", 1, 1, true); + + waitForBindings(0, "queues.0", 1, 1, false); + waitForBindings(1, "queues.0", 1, 1, false); + + + // sending Messages.. they should be load balanced + { + ConnectionFactory cf = getJmsConnectionFactory(0); + Connection cn = cf.createConnection(); + Session sn = cn.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer pd = sn.createProducer(sn.createQueue(queueName.toString())); + + for (int i = 0; i < NUMBER_OF_MESSAGES; i++) { + StringBuffer stringbuffer = new StringBuffer(); + stringbuffer.append("hello"); + if (i % 3 == 0) { + // making 1/3 of the messages to be large message + for (int j = 0; j < 300 * 1024; j++) { + stringbuffer.append(" "); + } + } + pd.send(sn.createTextMessage(stringbuffer.toString())); + } + + cn.close(); + } + + Wait.assertEquals(NUMBER_OF_MESSAGES / 2, servers[1].locateQueue(queueName)::getMessageCount); + Wait.assertEquals(NUMBER_OF_MESSAGES / 2, servers[0].locateQueue(queueName)::getMessageCount); + receiveMessages(connection[0], consumer[0], NUMBER_OF_MESSAGES / 2, true); + connection[1].close(); + // this wil be after redistribution + receiveMessages(connection[0], consumer[0], NUMBER_OF_MESSAGES / 2, true); + } + + private void receiveMessages(Connection connection, MessageConsumer messageConsumer, int messageCount, From 1ed7a616ee5e9beedb173c56a9226e314339a8e4 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Mon, 18 Jun 2018 15:19:48 -0500 Subject: [PATCH 056/207] ARTEMIS-1930 require STOMP durable sub name to unsubscribe --- .../core/protocol/stomp/StompSession.java | 16 ++++----- .../protocol/stomp/StompSubscription.java | 14 ++++---- .../tests/integration/stomp/StompTest.java | 34 +++++++++++++++++++ 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSession.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSession.java index e370c812f2f..291634f6188 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSession.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSession.java @@ -37,6 +37,7 @@ import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; import org.apache.activemq.artemis.core.server.LargeServerMessage; import org.apache.activemq.artemis.core.server.MessageReference; +import org.apache.activemq.artemis.core.server.Queue; import org.apache.activemq.artemis.core.server.ServerConsumer; import org.apache.activemq.artemis.core.server.ServerSession; import org.apache.activemq.artemis.core.server.impl.ServerSessionImpl; @@ -255,14 +256,12 @@ public StompPostReceiptFunction addSubscription(long consumerID, SimpleString address = SimpleString.toSimpleString(destination); SimpleString queueName = SimpleString.toSimpleString(destination); SimpleString selectorSimple = SimpleString.toSimpleString(selector); - boolean pubSub = false; final int receiveCredits = ack.equals(Stomp.Headers.Subscribe.AckModeValues.AUTO) ? -1 : consumerCredits; Set routingTypes = manager.getServer().getAddressInfo(getCoreSession().removePrefix(address)).getRoutingTypes(); - boolean topic = routingTypes.size() == 1 && routingTypes.contains(RoutingType.MULTICAST); - if (topic) { + boolean multicast = routingTypes.size() == 1 && routingTypes.contains(RoutingType.MULTICAST); + if (multicast) { // subscribes to a topic - pubSub = true; if (durableSubscriptionName != null) { if (clientID == null) { throw BUNDLE.missingClientID(); @@ -276,8 +275,8 @@ public StompPostReceiptFunction addSubscription(long consumerID, session.createQueue(address, queueName, selectorSimple, true, false); } } - final ServerConsumer consumer = session.createConsumer(consumerID, queueName, topic ? null : selectorSimple, false, false, 0); - StompSubscription subscription = new StompSubscription(subscriptionID, ack, queueName, pubSub); + final ServerConsumer consumer = session.createConsumer(consumerID, queueName, multicast ? null : selectorSimple, false, false, 0); + StompSubscription subscription = new StompSubscription(subscriptionID, ack, queueName, multicast); subscriptions.put(consumerID, subscription); session.start(); return () -> consumer.receiveCredits(receiveCredits); @@ -295,14 +294,15 @@ public boolean unsubscribe(String id, String durableSubscriptionName, String cli iterator.remove(); SimpleString queueName = sub.getQueueName(); session.closeConsumer(consumerID); - if (sub.isPubSub() && manager.getServer().locateQueue(queueName) != null) { + Queue queue = manager.getServer().locateQueue(queueName); + if (sub.isMulticast() && queue != null && (durableSubscriptionName == null && !queue.isDurable())) { session.deleteQueue(queueName); } result = true; } } - if (!result && durableSubscriptionName != null && clientID != null) { + if (durableSubscriptionName != null && clientID != null) { SimpleString queueName = SimpleString.toSimpleString(clientID + "." + durableSubscriptionName); if (manager.getServer().locateQueue(queueName) != null) { session.deleteQueue(queueName); diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSubscription.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSubscription.java index a1417adc9fb..de6044bfbe1 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSubscription.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompSubscription.java @@ -29,18 +29,18 @@ public class StompSubscription { private final SimpleString queueName; - // whether or not this subscription follows publish/subscribe semantics (e.g. for a JMS topic) - private final boolean pubSub; + // whether or not this subscription follows multicast semantics (e.g. for a JMS topic) + private final boolean multicast; // Static -------------------------------------------------------- // Constructors -------------------------------------------------- - public StompSubscription(String subID, String ack, SimpleString queueName, boolean pubSub) { + public StompSubscription(String subID, String ack, SimpleString queueName, boolean multicast) { this.subID = subID; this.ack = ack; this.queueName = queueName; - this.pubSub = pubSub; + this.multicast = multicast; } // Public -------------------------------------------------------- @@ -57,13 +57,13 @@ public SimpleString getQueueName() { return queueName; } - public boolean isPubSub() { - return pubSub; + public boolean isMulticast() { + return multicast; } @Override public String toString() { - return "StompSubscription[id=" + subID + ", ack=" + ack + ", queueName=" + queueName + ", pubSub=" + pubSub + "]"; + return "StompSubscription[id=" + subID + ", ack=" + ack + ", queueName=" + queueName + ", multicast=" + multicast + "]"; } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java index bc363f21ef9..5c6eefec85a 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java @@ -1343,6 +1343,40 @@ public void testDurableUnSubscribe() throws Exception { assertNull(server.getActiveMQServer().locateQueue(SimpleString.toSimpleString("myclientid." + getName()))); } + @Test + public void testDurableUnSubscribeWithoutDurableSubName() throws Exception { + server.getActiveMQServer().getConfiguration().getWildcardConfiguration().setDelimiter('/'); + server.getActiveMQServer().getAddressSettingsRepository().addMatch("/topic/#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.MULTICAST).setDefaultQueueRoutingType(RoutingType.MULTICAST)); + conn.connect(defUser, defPass, "myclientid"); + String subId = UUID.randomUUID().toString(); + String durableSubName = UUID.randomUUID().toString(); + String receipt = UUID.randomUUID().toString(); + ClientStompFrame frame = conn.createFrame(Stomp.Commands.SUBSCRIBE) + .addHeader(Stomp.Headers.Subscribe.DESTINATION, "/topic/test.foo") + .addHeader(Stomp.Headers.Unsubscribe.ID, subId) + .addHeader(Stomp.Headers.Subscribe.ACK_MODE, Stomp.Headers.Subscribe.AckModeValues.CLIENT_INDIVIDUAL) + .addHeader(Stomp.Headers.Subscribe.DURABLE_SUBSCRIPTION_NAME, durableSubName) + .addHeader(Stomp.Headers.RECEIPT_REQUESTED, receipt); + + frame = conn.sendFrame(frame); + assertEquals(receipt, frame.getHeader(Stomp.Headers.Response.RECEIPT_ID)); + + assertTrue(Wait.waitFor(() -> server.getActiveMQServer().locateQueue(SimpleString.toSimpleString("myclientid." + durableSubName)) != null, 2000, 100)); + + receipt = UUID.randomUUID().toString(); + frame = conn.createFrame(Stomp.Commands.UNSUBSCRIBE) + .addHeader(Stomp.Headers.Unsubscribe.ID, subId) + .addHeader(Stomp.Headers.RECEIPT_REQUESTED, receipt); + + frame = conn.sendFrame(frame); + assertEquals(receipt, frame.getHeader(Stomp.Headers.Response.RECEIPT_ID)); + + conn.disconnect(); + + // make sure the durable subscription queue is still there + assertTrue(Wait.waitFor(() -> server.getActiveMQServer().locateQueue(SimpleString.toSimpleString("myclientid." + durableSubName)) != null, 2000, 100)); + } + @Test public void testDurableUnSubscribeLegacySubscriptionHeader() throws Exception { conn.connect(defUser, defPass, "myclientid"); From c0d28432ad113190ba51777c7e92f38be87e933d Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Mon, 18 Jun 2018 19:14:15 -0500 Subject: [PATCH 057/207] NO-JIRA STOMP frame logging --- .../core/protocol/stomp/StompConnection.java | 24 +++++++++++++++++++ .../core/protocol/stomp/StompFrame.java | 8 +++++-- .../protocol/stomp/StompProtocolManager.java | 6 ++--- docs/user-manual/en/stomp.md | 8 +++++++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java index f7accb1ea6b..92b6edc46ff 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java @@ -54,11 +54,14 @@ import org.apache.activemq.artemis.utils.ConfigurationHelper; import org.apache.activemq.artemis.utils.ExecutorFactory; import org.apache.activemq.artemis.utils.VersionLoader; +import org.jboss.logging.Logger; import static org.apache.activemq.artemis.core.protocol.stomp.ActiveMQStompProtocolMessageBundle.BUNDLE; public final class StompConnection implements RemotingConnection { + private static final Logger logger = Logger.getLogger(StompConnection.class); + protected static final String CONNECTION_ID_PROP = "__AMQ_CID"; private static final String SERVER_NAME = "ActiveMQ-Artemis/" + VersionLoader.getVersion().getFullVersion() + " ActiveMQ Artemis Messaging Engine"; @@ -582,6 +585,27 @@ public void handleFrame(StompFrame request) { } } + public void logFrame(StompFrame request, boolean in) { + if (logger.isDebugEnabled()) { + StringBuilder message = new StringBuilder() + .append("STOMP(") + .append(getRemoteAddress()) + .append(", ") + .append(this.getID()) + .append("):"); + + if (in) { + message.append(" IN << "); + } else { + message.append("OUT >> "); + } + + message.append(request); + + logger.debug(message.toString()); + } + } + public void sendFrame(StompFrame frame, StompPostReceiptFunction function) { manager.sendReply(this, frame, function); } diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompFrame.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompFrame.java index 439eba28237..1ba5d382d83 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompFrame.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompFrame.java @@ -77,8 +77,12 @@ public int getEncodedSize() throws Exception { @Override public String toString() { - return "StompFrame[command=" + command + ", headers=" + headers + ", content= " + this.body + " bytes " + - Arrays.toString(bytesBody); + return new StringBuilder() + .append("StompFrame[command=").append(command) + .append(", headers=").append(headers) + .append(", content= ").append(this.body) + .append(", bytes= ").append(Arrays.toString(bytesBody)) + .toString(); } public boolean isPing() { diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java index 62df6e5a4e7..19a00626db9 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java @@ -155,6 +155,7 @@ public void handleBuffer(final RemotingConnection connection, final ActiveMQBuff try { invokeInterceptors(this.incomingInterceptors, request, conn); + conn.logFrame(request, true); conn.handleFrame(request); } finally { server.getStorageManager().clearContext(); @@ -186,11 +187,8 @@ public List websocketSubprotocolIdentifiers() { // Public -------------------------------------------------------- public boolean send(final StompConnection connection, final StompFrame frame) { - if (ActiveMQStompProtocolLogger.LOGGER.isTraceEnabled()) { - ActiveMQStompProtocolLogger.LOGGER.trace("sent " + frame); - } - invokeInterceptors(this.outgoingInterceptors, frame, connection); + connection.logFrame(frame, false); synchronized (connection) { if (connection.isDestroyed()) { diff --git a/docs/user-manual/en/stomp.md b/docs/user-manual/en/stomp.md index 4abe8d40117..27db086b07c 100644 --- a/docs/user-manual/en/stomp.md +++ b/docs/user-manual/en/stomp.md @@ -38,6 +38,14 @@ In Apache ActiveMQ Artemis, these destinations are mapped to *addresses* and *queues* depending on the operation being done and the desired semantics (e.g. anycast or multicast). +## Logging + +Incoming and outgoing STOMP frames can be logged by enabling `DEBUG` for +`org.apache.activemq.artemis.core.protocol.stomp.StompConnection`. This can be +extremely useful for debugging or simply monitoring client activity. Along with +the STOMP frame itself the remote IP address of the client is logged as well as +the internal connection ID so that frames from the same client can be correlated. + ## Sending When a STOMP client sends a message (using a `SEND` frame), the protocol From efd966d88d1a3ff4612c29729c8c717f075de943 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Fri, 22 Jun 2018 15:37:42 -0400 Subject: [PATCH 058/207] ARTEMIS-1928 Fixing body conversion of LargeMessages to AMQP --- .../amqp/converter/CoreAmqpConverter.java | 9 +++-- .../ProtocolsMessageLoadBalancingTest.java | 37 +++++++++++++++++-- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java index 66c75a8fe7d..49372dbc1e9 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java @@ -60,6 +60,7 @@ import javax.jms.TextMessage; import javax.jms.Topic; +import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ICoreMessage; import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.protocol.amqp.broker.AMQPMessage; @@ -381,17 +382,17 @@ private static Section convertBody(ServerJMSMessage message, Map // will be unknown so we check for special cases of messages with special data // encoded into the server message body. ICoreMessage internalMessage = message.getInnerMessage(); - int readerIndex = internalMessage.getBodyBuffer().readerIndex(); + + // this will represent a readOnly buffer for the message + ActiveMQBuffer buffer = internalMessage.getDataBuffer(); try { - Object s = internalMessage.getBodyBuffer().readNullableSimpleString(); + Object s = buffer.readNullableSimpleString(); if (s != null) { body = new AmqpValue(s.toString()); } } catch (Throwable ignored) { logger.debug("Exception ignored during conversion", ignored.getMessage(), ignored); body = new AmqpValue("Conversion to AMQP error!"); - } finally { - internalMessage.getBodyBuffer().readerIndex(readerIndex); } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java index ae413924082..ae4f013a9a0 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/crossprotocol/ProtocolsMessageLoadBalancingTest.java @@ -29,6 +29,12 @@ import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientProducer; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; +import org.apache.activemq.artemis.api.core.client.ServerLocator; import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; import org.apache.activemq.artemis.core.protocol.openwire.OpenWireProtocolManagerFactory; import org.apache.activemq.artemis.core.server.ActiveMQServer; @@ -392,9 +398,34 @@ public void testRedistributeAfterLoadBalanced() throws Exception { waitForBindings(1, "queues.0", 1, 1, false); - // sending Messages.. they should be load balanced - { - ConnectionFactory cf = getJmsConnectionFactory(0); + + if (protocol.equals("AMQP")) { + + + ServerLocator locator = ActiveMQClient.createServerLocator("tcp://localhost:61616"); + locator.setMinLargeMessageSize(1024); + ClientSessionFactory coreFactory = locator.createSessionFactory(); + ClientSession clientSession = coreFactory.createSession(); + ClientProducer producer = clientSession.createProducer(queueName); + for (int i = 0; i < NUMBER_OF_MESSAGES; i++) { + ClientMessage message = clientSession.createMessage((byte)0, true); + StringBuffer stringbuffer = new StringBuffer(); + stringbuffer.append("hello"); + if (i % 3 == 0) { + // making 1/3 of the messages to be large message + for (int j = 0; j < 10 * 1024; j++) { + stringbuffer.append(" "); + } + } + message.getBodyBuffer().writeUTF(stringbuffer.toString()); + producer.send(message); + } + coreFactory.close(); + + } else { + + // sending Messages.. they should be load balanced + ConnectionFactory cf = getJmsConnectionFactory(0); Connection cn = cf.createConnection(); Session sn = cn.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer pd = sn.createProducer(sn.createQueue(queueName.toString())); From 901e5c4a98c9b1ecb5f82f129c889023a1077374 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Sat, 23 Jun 2018 10:33:33 -0400 Subject: [PATCH 059/207] NO-JIRA Improving ExpiryMessageTest --- .../jms/client/ExpiryMessageTest.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ExpiryMessageTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ExpiryMessageTest.java index b5091dc042a..53a3637fc1e 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ExpiryMessageTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ExpiryMessageTest.java @@ -25,6 +25,7 @@ import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.management.AddressControl; import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.junit.Wait; import org.apache.activemq.artemis.tests.integration.management.ManagementControlHelper; import org.apache.activemq.artemis.tests.util.JMSTestBase; import org.junit.Test; @@ -42,7 +43,7 @@ public class ExpiryMessageTest extends JMSTestBase { @Override protected Configuration createDefaultConfig(boolean netty) throws Exception { - return super.createDefaultConfig(netty).setMessageExpiryScanPeriod(1000); + return super.createDefaultConfig(netty).setMessageExpiryScanPeriod(50); } @Test @@ -64,7 +65,7 @@ public void testSendTopicNoSubscription() throws Exception { conn = cf.createConnection(); Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); MessageProducer prod = sess.createProducer(topic); - prod.setTimeToLive(1000); + prod.setTimeToLive(100); for (int i = 0; i < 100; i++) { TextMessage txt = sess.createTextMessage("txt"); @@ -75,17 +76,7 @@ public void testSendTopicNoSubscription() throws Exception { conn.close(); - // minimal time needed - Thread.sleep(2000); - - long timeout = System.currentTimeMillis() + 10000; - - // We will wait some time, but we will wait as minimal as possible - while (control.getMessageCount() != 0 && System.currentTimeMillis() > timeout) { - Thread.sleep(100); - } - - assertEquals(0, control.getMessageCount()); + Wait.assertEquals(0, control::getMessageCount); } From f9af366f07368bfaed197cb1eddfde17506c9d84 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Sat, 23 Jun 2018 11:42:41 -0500 Subject: [PATCH 060/207] ARTEMIS-1951 fix comparison bug --- .../apache/activemq/artemis/core/postoffice/PostOffice.java | 2 +- .../activemq/artemis/core/postoffice/impl/PostOfficeImpl.java | 4 ++-- .../activemq/artemis/core/server/impl/ActiveMQServerImpl.java | 2 +- .../tests/unit/core/server/impl/fakes/FakePostOffice.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java index 024a80671d3..d95526dcb2a 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java @@ -69,7 +69,7 @@ QueueBinding updateQueue(SimpleString name, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, - String user) throws Exception; + SimpleString user) throws Exception; List listQueuesForAddress(SimpleString address) throws Exception; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index e3df0c40067..fb973ecfc6f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -468,7 +468,7 @@ public QueueBinding updateQueue(SimpleString name, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, - String user) throws Exception { + SimpleString user) throws Exception { synchronized (addressLock) { final QueueBinding queueBinding = (QueueBinding) addressManager.getBinding(name); if (queueBinding == null) { @@ -514,7 +514,7 @@ public QueueBinding updateQueue(SimpleString name, } if ((user != null && !user.equals(queue.getUser()) || (user == null && queue.getUser() != null))) { changed = true; - queue.setUser(SimpleString.toSimpleString(user)); + queue.setUser(user); } if (changed) { 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 1e722aa00c2..882c66d4b90 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 @@ -2970,7 +2970,7 @@ public Queue updateQueue(String name, Boolean purgeOnNoConsumers, Boolean exclusive, String user) throws Exception { - final QueueBinding queueBinding = this.postOffice.updateQueue(new SimpleString(name), routingType, maxConsumers, purgeOnNoConsumers, exclusive, user); + final QueueBinding queueBinding = this.postOffice.updateQueue(new SimpleString(name), routingType, maxConsumers, purgeOnNoConsumers, exclusive, new SimpleString(user)); if (queueBinding != null) { final Queue queue = queueBinding.getQueue(); return queue; diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java index b0560e39897..3f350847d85 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java @@ -49,7 +49,7 @@ public QueueBinding updateQueue(SimpleString name, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, - String user) throws Exception { + SimpleString user) throws Exception { return null; } From c7c23454e810c6d32593dd4f18feb05eebfdaad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Mon, 25 Jun 2018 13:55:16 +0100 Subject: [PATCH 061/207] ARTEMIS-1951 allow queue's user to be updated Fix so that it only updates if not null, to avoid user being unset from existing api's, This is similar to other values, that only change the value when not null. --- .../activemq/artemis/core/postoffice/impl/PostOfficeImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index fb973ecfc6f..2b5ba602fdf 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -512,7 +512,7 @@ public QueueBinding updateQueue(SimpleString name, changed = true; queue.setExclusive(exclusive); } - if ((user != null && !user.equals(queue.getUser()) || (user == null && queue.getUser() != null))) { + if (user != null && !user.equals(queue.getUser())) { changed = true; queue.setUser(user); } From af7c6c811183d9f8c8478bf54e998aebaea179bf Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Wed, 27 Jun 2018 15:04:23 -0500 Subject: [PATCH 062/207] ARTEMIS-1957 add missing notification type docs --- docs/user-manual/en/management.md | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/user-manual/en/management.md b/docs/user-manual/en/management.md index ca523b7a20e..e27794cd165 100644 --- a/docs/user-manual/en/management.md +++ b/docs/user-manual/en/management.md @@ -804,6 +804,40 @@ un-formatted result of a call to `java.lang.System.currentTimeMillis()`. `_AMQ_Address`, `_AMQ_ConsumerCount`, `_AMQ_RemoteAddress`, `_AMQ_ConnectionName`, `_AMQ_ConsumerName`, `_AMQ_SessionName` +- `ADDRESS_ADDED` (22) + + `_AMQ_Address`, `_AMQ_Routing_Type` + +- `ADDRESS_REMOVED` (23) + + `_AMQ_Address`, `_AMQ_Routing_Type` + +- `CONNECTION_CREATED` (24) + + `_AMQ_ConnectionName`, `_AMQ_RemoteAddress` + +- `CONNECTION_DESTROYED` (25) + + `_AMQ_ConnectionName`, `_AMQ_RemoteAddress` + +- `SESSION_CREATED` (26) + + `_AMQ_ConnectionName`, `_AMQ_User`, `_AMQ_SessionName` + +- `SESSION_CLOSED` (27) + + `_AMQ_ConnectionName`, `_AMQ_User`, `_AMQ_SessionName` + +- `MESSAGE_DELIVERED` (28) + + `_AMQ_Address`, `_AMQ_Routing_Type`, `_AMQ_RoutingName`, + `_AMQ_ConsumerName`, `_AMQ_Message_ID` + +- `MESSAGE_EXPIRED` (29) + + `_AMQ_Address`, `_AMQ_Routing_Type`, `_AMQ_RoutingName`, + `_AMQ_ConsumerName`, `_AMQ_Message_ID` + ## Message Counters Message counters can be used to obtain information on queues *over time* as From f288cfc9cae4feecfcba2bce335fcddbf36e0092 Mon Sep 17 00:00:00 2001 From: andytaylor Date: Mon, 2 Jul 2018 11:42:31 +0100 Subject: [PATCH 063/207] ARTEMIS-1866 - Make wait time for reply configurable once vote goes out to acquire a quorum Im refactoring a lot of this original fix as it has broken API compatibilty by removing a default constructor. Also it should be set via a setter. https://issues.apache.org/jira/projects/ARTEMIS/issues/ARTEMIS-1866 --- .../config/ha/ReplicaPolicyConfiguration.java | 10 ++++++--- .../ha/ReplicatedPolicyConfiguration.java | 10 ++++++--- .../impl/FileConfigurationParser.java | 9 ++++++-- .../jms/bridge/ClusteredBridgeTestBase.java | 6 ++++-- .../cluster/distribution/ClusterTestBase.java | 9 ++++---- .../AutomaticColocatedQuorumVoteTest.java | 5 ++--- ...tipleLivesMultipleBackupsFailoverTest.java | 5 ++--- .../MultipleServerFailoverTestBase.java | 5 ++--- .../failover/QuorumResultWaitTest.java | 6 +++--- ...SingleLiveMultipleBackupsFailoverTest.java | 5 ++--- .../cluster/util/MultiServerTestBase.java | 5 ++--- .../jms/cluster/JMSFailoverTest.java | 5 ++--- .../SharedNothingReplicationTest.java | 5 ++--- .../artemis/tests/util/HAConfigUtils.java | 21 ------------------- .../tests/util/ReplicatedBackupUtils.java | 4 ++-- 15 files changed, 48 insertions(+), 62 deletions(-) delete mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/HAConfigUtils.java diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java index 1760aa33aff..e02bb5603a3 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java @@ -47,12 +47,12 @@ public class ReplicaPolicyConfiguration implements HAPolicyConfiguration { private long voteRetryWait = ActiveMQDefaultConfiguration.getDefaultVoteRetryWait(); - private final int quorumVoteWait; + private int quorumVoteWait = ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(); - public ReplicaPolicyConfiguration(int quorumVoteWait) { - this.quorumVoteWait = quorumVoteWait; + public ReplicaPolicyConfiguration() { } + @Override public TYPE getType() { return TYPE.REPLICA; @@ -166,4 +166,8 @@ public long getVoteRetryWait() { public int getQuorumVoteWait() { return quorumVoteWait; } + + public void setQuorumVoteWait(int quorumVoteWait) { + this.quorumVoteWait = quorumVoteWait; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java index fe12168571e..e27bb4009fb 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java @@ -37,12 +37,12 @@ public class ReplicatedPolicyConfiguration implements HAPolicyConfiguration { private long voteRetryWait = ActiveMQDefaultConfiguration.getDefaultVoteRetryWait(); - private final int quorumVoteWait; + private int quorumVoteWait = ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(); - public ReplicatedPolicyConfiguration(int quorumVoteWait) { - this.quorumVoteWait = quorumVoteWait; + public ReplicatedPolicyConfiguration() { } + @Override public TYPE getType() { return TYPE.REPLICATED; @@ -119,4 +119,8 @@ public long getVoteRetryWait() { public int getQuorumVoteWait() { return quorumVoteWait; } + + public void setQuorumVoteWait(int quorumVoteWait) { + this.quorumVoteWait = quorumVoteWait; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java index d2d17633273..27badac4d02 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java @@ -1303,7 +1303,9 @@ private LiveOnlyPolicyConfiguration createLiveOnlyHaPolicy(Element policyNode) { } private ReplicatedPolicyConfiguration createReplicatedHaPolicy(Element policyNode) { - ReplicatedPolicyConfiguration configuration = new ReplicatedPolicyConfiguration(getInteger(policyNode, "quorum-vote-wait", ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(), Validators.GT_ZERO)); + ReplicatedPolicyConfiguration configuration = new ReplicatedPolicyConfiguration(); + + configuration.setQuorumVoteWait(getInteger(policyNode, "quorum-vote-wait", ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(), Validators.GT_ZERO)); configuration.setCheckForLiveServer(getBoolean(policyNode, "check-for-live-server", configuration.isCheckForLiveServer())); @@ -1325,7 +1327,10 @@ private ReplicatedPolicyConfiguration createReplicatedHaPolicy(Element policyNod } private ReplicaPolicyConfiguration createReplicaHaPolicy(Element policyNode) { - ReplicaPolicyConfiguration configuration = new ReplicaPolicyConfiguration(getInteger(policyNode, "quorum-vote-wait", ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(), Validators.GT_ZERO)); + + ReplicaPolicyConfiguration configuration = new ReplicaPolicyConfiguration(); + + configuration.setQuorumVoteWait(getInteger(policyNode, "quorum-vote-wait", ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(), Validators.GT_ZERO)); configuration.setRestartBackup(getBoolean(policyNode, "restart-backup", configuration.isRestartBackup())); diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java index 9d99a90e9f4..165601a18de 100644 --- a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java @@ -147,7 +147,7 @@ public void create() throws Exception { backupConnector = new TransportConfiguration(INVM_CONNECTOR_FACTORY, params, "in-vm-backup"); //live - Configuration conf0 = createBasicConfig().setJournalDirectory(getJournalDir(id, false)).setBindingsDirectory(getBindingsDir(id, false)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params0)).addConnectorConfiguration(liveConnector.getName(), liveConnector).setHAPolicyConfiguration(new ReplicatedPolicyConfiguration(QUORUM_VOTE_WAIT_TIME_SEC)).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())); + Configuration conf0 = createBasicConfig().setJournalDirectory(getJournalDir(id, false)).setBindingsDirectory(getBindingsDir(id, false)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params0)).addConnectorConfiguration(liveConnector.getName(), liveConnector).setHAPolicyConfiguration(new ReplicatedPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())); ActiveMQServer server0 = addServer(ActiveMQServers.newActiveMQServer(conf0, true)); @@ -156,7 +156,9 @@ public void create() throws Exception { liveNode.setRegistry(new JndiBindingRegistry(liveContext)); //backup - Configuration config = createBasicConfig().setJournalDirectory(getJournalDir(id, true)).setBindingsDirectory(getBindingsDir(id, true)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params)).addConnectorConfiguration(backupConnector.getName(), backupConnector).addConnectorConfiguration(liveConnector.getName(), liveConnector).setHAPolicyConfiguration(new ReplicaPolicyConfiguration(QUORUM_VOTE_WAIT_TIME_SEC)).addClusterConfiguration(basicClusterConnectionConfig(backupConnector.getName(), liveConnector.getName())); + ReplicaPolicyConfiguration replicaPolicyConfiguration = new ReplicaPolicyConfiguration(); + replicaPolicyConfiguration.setQuorumVoteWait(QUORUM_VOTE_WAIT_TIME_SEC); + Configuration config = createBasicConfig().setJournalDirectory(getJournalDir(id, true)).setBindingsDirectory(getBindingsDir(id, true)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params)).addConnectorConfiguration(backupConnector.getName(), backupConnector).addConnectorConfiguration(liveConnector.getName(), liveConnector).setHAPolicyConfiguration(replicaPolicyConfiguration).addClusterConfiguration(basicClusterConnectionConfig(backupConnector.getName(), liveConnector.getName())); ActiveMQServer backup = addServer(ActiveMQServers.newActiveMQServer(config, true)); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java index edb4fbbd92b..89d5175dbbb 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java @@ -84,7 +84,6 @@ import org.apache.activemq.artemis.core.server.impl.InVMNodeManager; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; -import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -1484,7 +1483,7 @@ protected void setupLiveServer(final int node, if (sharedStorage) haPolicyConfiguration = new SharedStoreMasterPolicyConfiguration(); else - haPolicyConfiguration = new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); + haPolicyConfiguration = new ReplicatedPolicyConfiguration(); } Configuration configuration = createBasicConfig(node).setJournalMaxIO_AIO(1000).setThreadPoolMaxSize(10).clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(netty, true, generateParams(node, netty))).setHAPolicyConfiguration(haPolicyConfiguration).setResolveProtocols(isResolveProtocols()); @@ -1539,7 +1538,7 @@ protected void setupBackupServer(final int node, TransportConfiguration backupConfig = createTransportConfiguration(netty, false, generateParams(node, netty)); TransportConfiguration acceptorConfig = createTransportConfiguration(netty, true, generateParams(node, netty)); - Configuration configuration = createBasicConfig(sharedStorage ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(acceptorConfig).addConnectorConfiguration(liveConfig.getName(), liveConfig).addConnectorConfiguration(backupConfig.getName(), backupConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); + Configuration configuration = createBasicConfig(sharedStorage ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(acceptorConfig).addConnectorConfiguration(liveConfig.getName(), liveConfig).addConnectorConfiguration(backupConfig.getName(), backupConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration()); ActiveMQServer server; @@ -1576,7 +1575,7 @@ protected void setupLiveServerWithDiscovery(final int node, DiscoveryGroupConfiguration dcConfig = new DiscoveryGroupConfiguration().setName("dg1").setRefreshTimeout(1000).setDiscoveryInitialWaitTimeout(1000).setBroadcastEndpointFactory(endpoint); - Configuration configuration = createBasicConfig(node).setJournalMaxIO_AIO(1000).clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(netty, true, params)).addConnectorConfiguration(connector.getName(), connector).addBroadcastGroupConfiguration(bcConfig).addDiscoveryGroupConfiguration(dcConfig.getName(), dcConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); + Configuration configuration = createBasicConfig(node).setJournalMaxIO_AIO(1000).clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(netty, true, params)).addConnectorConfiguration(connector.getName(), connector).addBroadcastGroupConfiguration(bcConfig).addDiscoveryGroupConfiguration(dcConfig.getName(), dcConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration()); ActiveMQServer server; if (fileStorage) { @@ -1621,7 +1620,7 @@ protected void setupBackupServerWithDiscovery(final int node, DiscoveryGroupConfiguration dcConfig = new DiscoveryGroupConfiguration().setName("dg1").setRefreshTimeout(5000).setDiscoveryInitialWaitTimeout(5000).setBroadcastEndpointFactory(endpoint); - Configuration configuration = createBasicConfig(sharedStorage ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(netty, true, params)).addConnectorConfiguration(connector.getName(), connector).addBroadcastGroupConfiguration(bcConfig).addDiscoveryGroupConfiguration(dcConfig.getName(), dcConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreSlavePolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); + Configuration configuration = createBasicConfig(sharedStorage ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(netty, true, params)).addConnectorConfiguration(connector.getName(), connector).addBroadcastGroupConfiguration(bcConfig).addDiscoveryGroupConfiguration(dcConfig.getName(), dcConfig).setHAPolicyConfiguration(sharedStorage ? new SharedStoreSlavePolicyConfiguration() : new ReplicatedPolicyConfiguration()); ActiveMQServer server; if (sharedStorage) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/AutomaticColocatedQuorumVoteTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/AutomaticColocatedQuorumVoteTest.java index 7cc59041118..0f792b52326 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/AutomaticColocatedQuorumVoteTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/AutomaticColocatedQuorumVoteTest.java @@ -41,7 +41,6 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; -import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -312,8 +311,8 @@ private Configuration getConfiguration(String identity, sssc.setScaleDownConfiguration(new ScaleDownConfiguration()); } } else { - ReplicatedPolicyConfiguration rpc = new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); - ReplicaPolicyConfiguration rpc2 = new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); + ReplicatedPolicyConfiguration rpc = new ReplicatedPolicyConfiguration(); + ReplicaPolicyConfiguration rpc2 = new ReplicaPolicyConfiguration(); haPolicy.setLiveConfig(rpc); haPolicy.setBackupConfig(rpc2); if (scaleDown) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleLivesMultipleBackupsFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleLivesMultipleBackupsFailoverTest.java index bfd85a7bd4e..30bd24c090d 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleLivesMultipleBackupsFailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleLivesMultipleBackupsFailoverTest.java @@ -33,7 +33,6 @@ import org.apache.activemq.artemis.core.server.impl.InVMNodeManager; import org.apache.activemq.artemis.tests.integration.cluster.util.SameProcessActiveMQServer; import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer; -import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.Test; /** @@ -126,7 +125,7 @@ protected void createBackupConfig(NodeManager nodeManager, boolean createClusterConnections, int[] otherBackupNodes, int... otherClusterNodes) throws Exception { - Configuration config1 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(nodeid, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); + Configuration config1 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(nodeid, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration()).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); for (int node : otherBackupNodes) { TransportConfiguration liveConnector = createTransportConfiguration(isNetty(), false, generateParams(node, isNetty())); @@ -150,7 +149,7 @@ protected void createBackupConfig(NodeManager nodeManager, protected void createLiveConfig(NodeManager nodeManager, int liveNode, int... otherLiveNodes) throws Exception { TransportConfiguration liveConnector = createTransportConfiguration(isNetty(), false, generateParams(liveNode, isNetty())); - Configuration config0 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(liveNode, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode).addConnectorConfiguration(liveConnector.getName(), liveConnector); + Configuration config0 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(liveNode, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration()).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode).addConnectorConfiguration(liveConnector.getName(), liveConnector); String[] pairs = new String[otherLiveNodes.length]; for (int i = 0; i < otherLiveNodes.length; i++) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleServerFailoverTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleServerFailoverTestBase.java index c7ddefea317..0775e085238 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleServerFailoverTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/MultipleServerFailoverTestBase.java @@ -39,7 +39,6 @@ import org.apache.activemq.artemis.tests.integration.cluster.util.SameProcessActiveMQServer; import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; -import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.apache.activemq.artemis.tests.util.TransportConfigurationUtils; import org.junit.Before; @@ -87,7 +86,7 @@ public void setUp() throws Exception { if (isSharedStore()) { haPolicyConfiguration = new SharedStoreMasterPolicyConfiguration(); } else { - haPolicyConfiguration = new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); + haPolicyConfiguration = new ReplicatedPolicyConfiguration(); if (getNodeGroupName() != null) { ((ReplicatedPolicyConfiguration) haPolicyConfiguration).setGroupName(getNodeGroupName() + "-" + i); } @@ -130,7 +129,7 @@ public void setUp() throws Exception { if (isSharedStore()) { haPolicyConfiguration = new SharedStoreSlavePolicyConfiguration(); } else { - haPolicyConfiguration = new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); + haPolicyConfiguration = new ReplicaPolicyConfiguration(); if (getNodeGroupName() != null) { ((ReplicaPolicyConfiguration) haPolicyConfiguration).setGroupName(getNodeGroupName() + "-" + i); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java index ada45e6bab4..f481dbc0dcd 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java @@ -16,10 +16,10 @@ */ package org.apache.activemq.artemis.tests.integration.cluster.failover; +import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.core.config.ha.ReplicaPolicyConfiguration; import org.apache.activemq.artemis.core.config.ha.ReplicatedPolicyConfiguration; import org.apache.activemq.artemis.core.server.cluster.ha.ReplicatedPolicy; -import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.Test; public class QuorumResultWaitTest extends StaticClusterWithBackupFailoverTest { @@ -33,7 +33,7 @@ protected void setupServers() throws Exception { ((ReplicatedPolicyConfiguration) servers[2].getConfiguration().getHAPolicyConfiguration()).setGroupName("group2"); ((ReplicaPolicyConfiguration) servers[4].getConfiguration().getHAPolicyConfiguration()).setGroupName("group1"); ((ReplicaPolicyConfiguration) servers[5].getConfiguration().getHAPolicyConfiguration()).setGroupName("group2"); - ReplicatedPolicyConfiguration replicatedPolicyConf = new ReplicatedPolicyConfiguration(QUORUM_VOTE_WAIT_CONFIGURED_TIME_SEC); + ReplicatedPolicyConfiguration replicatedPolicyConf = new ReplicatedPolicyConfiguration(); replicatedPolicyConf.setGroupName("group0"); replicatedPolicyConf.setVoteRetries(5); replicatedPolicyConf.setVoteRetryWait(100); @@ -46,7 +46,7 @@ public void testQuorumVotingResultWait() throws Exception { startServers(0, 1, 2); startServers(3, 4, 5); //Assert if the default time 30 sec is used - assertEquals(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC, ((ReplicatedPolicy)(servers[0].getHAPolicy())).getQuorumVoteWait()); + assertEquals(ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(), ((ReplicatedPolicy)(servers[0].getHAPolicy())).getQuorumVoteWait()); //Assert if the configured time is used. assertEquals(QUORUM_VOTE_WAIT_CONFIGURED_TIME_SEC, ((ReplicatedPolicy)(servers[3].getHAPolicy())).getQuorumVoteWait()); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SingleLiveMultipleBackupsFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SingleLiveMultipleBackupsFailoverTest.java index 6853faaeb9c..a3174d7cc9c 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SingleLiveMultipleBackupsFailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SingleLiveMultipleBackupsFailoverTest.java @@ -33,7 +33,6 @@ import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.integration.cluster.util.SameProcessActiveMQServer; import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer; -import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.Test; /** @@ -126,7 +125,7 @@ public void testMultipleFailovers() throws Exception { protected void createBackupConfig(int liveNode, int nodeid, int... nodes) throws Exception { TransportConfiguration backupConnector = createTransportConfiguration(isNetty(), false, generateParams(nodeid, isNetty())); - Configuration config1 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(nodeid, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).addConnectorConfiguration(backupConnector.getName(), backupConnector).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); + Configuration config1 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(nodeid, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicatedPolicyConfiguration()).addConnectorConfiguration(backupConnector.getName(), backupConnector).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); String[] staticConnectors = new String[nodes.length]; for (int i = 0; i < nodes.length; i++) { @@ -142,7 +141,7 @@ protected void createBackupConfig(int liveNode, int nodeid, int... nodes) throws protected void createLiveConfig(int liveNode) throws Exception { TransportConfiguration liveConnector = createTransportConfiguration(isNetty(), false, generateParams(liveNode, isNetty())); - Configuration config0 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(liveNode, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())).addConnectorConfiguration(liveConnector.getName(), liveConnector).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); + Configuration config0 = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(createTransportConfiguration(isNetty(), true, generateParams(liveNode, isNetty()))).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())).addConnectorConfiguration(liveConnector.getName(), liveConnector).setBindingsDirectory(getBindingsDir() + "_" + liveNode).setJournalDirectory(getJournalDir() + "_" + liveNode).setPagingDirectory(getPageDir() + "_" + liveNode).setLargeMessagesDirectory(getLargeMessagesDir() + "_" + liveNode); SameProcessActiveMQServer server = new SameProcessActiveMQServer(createInVMFailoverServer(true, config0, nodeManager, liveNode)); addActiveMQComponent(server); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/util/MultiServerTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/util/MultiServerTestBase.java index 39fcf098ae2..8ceff8829a5 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/util/MultiServerTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/util/MultiServerTestBase.java @@ -35,7 +35,6 @@ import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; import org.apache.activemq.artemis.core.server.impl.InVMNodeManager; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; -import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.junit.Before; public class MultiServerTestBase extends ActiveMQTestBase { @@ -157,7 +156,7 @@ protected Pair setupLiveServer(final int node, nodeManager = new InVMNodeManager(false); } - Configuration configuration = createBasicConfig(node).setJournalMaxIO_AIO(1000).setThreadPoolMaxSize(10).clearAcceptorConfigurations().addAcceptorConfiguration(serverConfigAcceptor).addConnectorConfiguration("thisConnector", thisConnector).setHAPolicyConfiguration(sharedStorage ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); + Configuration configuration = createBasicConfig(node).setJournalMaxIO_AIO(1000).setThreadPoolMaxSize(10).clearAcceptorConfigurations().addAcceptorConfiguration(serverConfigAcceptor).addConnectorConfiguration("thisConnector", thisConnector).setHAPolicyConfiguration(sharedStorage ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration()); List targetServersOnConnection = new ArrayList<>(); @@ -203,7 +202,7 @@ protected ActiveMQServer setupBackupServer(final int node, TransportConfiguration serverConfigAcceptor = createTransportConfiguration(useNetty(), true, generateParams(node, useNetty())); TransportConfiguration thisConnector = createTransportConfiguration(useNetty(), false, generateParams(node, useNetty())); - Configuration configuration = createBasicConfig(useSharedStorage() ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(serverConfigAcceptor).addConnectorConfiguration("thisConnector", thisConnector).setHAPolicyConfiguration(useSharedStorage() ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); + Configuration configuration = createBasicConfig(useSharedStorage() ? liveNode : node).clearAcceptorConfigurations().addAcceptorConfiguration(serverConfigAcceptor).addConnectorConfiguration("thisConnector", thisConnector).setHAPolicyConfiguration(useSharedStorage() ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration()); List targetServersOnConnection = new ArrayList<>(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverTest.java index 800f8a306d9..4b4e89b1be3 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverTest.java @@ -62,7 +62,6 @@ import org.apache.activemq.artemis.tests.integration.jms.server.management.JMSUtil; import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; -import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.apache.activemq.artemis.tests.util.InVMNodeManagerServer; import org.apache.activemq.artemis.utils.RandomUtil; import org.junit.Assert; @@ -466,7 +465,7 @@ protected void startServers() throws Exception { backupParams.put(TransportConstants.SERVER_ID_PROP_NAME, 1); - backupConf = createBasicConfig().addAcceptorConfiguration(backupAcceptortc).addConnectorConfiguration(livetc.getName(), livetc).addConnectorConfiguration(backuptc.getName(), backuptc).setSecurityEnabled(false).setJournalType(getDefaultJournalType()).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, backupParams)).setBindingsDirectory(getBindingsDir()).setJournalMinFiles(2).setJournalDirectory(getJournalDir()).setPagingDirectory(getPageDir()).setLargeMessagesDirectory(getLargeMessagesDir()).setPersistenceEnabled(true).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).addClusterConfiguration(basicClusterConnectionConfig(backuptc.getName(), livetc.getName())); + backupConf = createBasicConfig().addAcceptorConfiguration(backupAcceptortc).addConnectorConfiguration(livetc.getName(), livetc).addConnectorConfiguration(backuptc.getName(), backuptc).setSecurityEnabled(false).setJournalType(getDefaultJournalType()).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, backupParams)).setBindingsDirectory(getBindingsDir()).setJournalMinFiles(2).setJournalDirectory(getJournalDir()).setPagingDirectory(getPageDir()).setLargeMessagesDirectory(getLargeMessagesDir()).setPersistenceEnabled(true).setHAPolicyConfiguration(sharedStore ? new SharedStoreSlavePolicyConfiguration() : new ReplicaPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(backuptc.getName(), livetc.getName())); backupServer = addServer(new InVMNodeManagerServer(backupConf, nodeManager)); @@ -478,7 +477,7 @@ protected void startServers() throws Exception { log.info("Starting backup"); backupJMSServer.start(); - liveConf = createBasicConfig().setJournalDirectory(getJournalDir()).setBindingsDirectory(getBindingsDir()).setSecurityEnabled(false).addAcceptorConfiguration(liveAcceptortc).setJournalType(getDefaultJournalType()).setBindingsDirectory(getBindingsDir()).setJournalMinFiles(2).setJournalDirectory(getJournalDir()).setPagingDirectory(getPageDir()).setLargeMessagesDirectory(getLargeMessagesDir()).addConnectorConfiguration(livetc.getName(), livetc).setPersistenceEnabled(true).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)).addClusterConfiguration(basicClusterConnectionConfig(livetc.getName())); + liveConf = createBasicConfig().setJournalDirectory(getJournalDir()).setBindingsDirectory(getBindingsDir()).setSecurityEnabled(false).addAcceptorConfiguration(liveAcceptortc).setJournalType(getDefaultJournalType()).setBindingsDirectory(getBindingsDir()).setJournalMinFiles(2).setJournalDirectory(getJournalDir()).setPagingDirectory(getPageDir()).setLargeMessagesDirectory(getLargeMessagesDir()).addConnectorConfiguration(livetc.getName(), livetc).setPersistenceEnabled(true).setHAPolicyConfiguration(sharedStore ? new SharedStoreMasterPolicyConfiguration() : new ReplicatedPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(livetc.getName())); liveServer = addServer(new InVMNodeManagerServer(liveConf, nodeManager)); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationTest.java index 869ec6c7dcf..3997c1dad3f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationTest.java @@ -46,7 +46,6 @@ import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.core.server.JournalType; import org.apache.activemq.artemis.junit.Wait; -import org.apache.activemq.artemis.tests.util.HAConfigUtils; import org.jboss.logging.Logger; import org.junit.After; import org.junit.Assert; @@ -248,7 +247,7 @@ private Configuration createLiveConfiguration() throws Exception { conf.setClusterUser("mycluster"); conf.setClusterPassword("mypassword"); - ReplicatedPolicyConfiguration haPolicy = new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); + ReplicatedPolicyConfiguration haPolicy = new ReplicatedPolicyConfiguration(); haPolicy.setVoteOnReplicationFailure(false); haPolicy.setCheckForLiveServer(false); conf.setHAPolicyConfiguration(haPolicy); @@ -271,7 +270,7 @@ private Configuration createBackupConfiguration() throws Exception { File backupDir = brokersFolder.newFolder("backup"); conf.setBrokerInstance(backupDir); - ReplicaPolicyConfiguration haPolicy = new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC); + ReplicaPolicyConfiguration haPolicy = new ReplicaPolicyConfiguration(); haPolicy.setClusterName("cluster"); conf.setHAPolicyConfiguration(haPolicy); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/HAConfigUtils.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/HAConfigUtils.java deleted file mode 100644 index 096c41bb6ef..00000000000 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/HAConfigUtils.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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.artemis.tests.util; - -public final class HAConfigUtils { - public static final int QUORUM_VOTE_WAIT_TIME_SEC = 30; -} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/ReplicatedBackupUtils.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/ReplicatedBackupUtils.java index 7d313e3cf93..1a38a6ac702 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/ReplicatedBackupUtils.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/ReplicatedBackupUtils.java @@ -44,8 +44,8 @@ public static void configureReplicationPair(Configuration backupConfig, liveConfig.clearAcceptorConfigurations().addAcceptorConfiguration(liveAcceptor); } - backupConfig.addConnectorConfiguration(BACKUP_NODE_NAME, backupConnector).addConnectorConfiguration(LIVE_NODE_NAME, liveConnector).addClusterConfiguration(ActiveMQTestBase.basicClusterConnectionConfig(BACKUP_NODE_NAME, LIVE_NODE_NAME)).setHAPolicyConfiguration(new ReplicaPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); + backupConfig.addConnectorConfiguration(BACKUP_NODE_NAME, backupConnector).addConnectorConfiguration(LIVE_NODE_NAME, liveConnector).addClusterConfiguration(ActiveMQTestBase.basicClusterConnectionConfig(BACKUP_NODE_NAME, LIVE_NODE_NAME)).setHAPolicyConfiguration(new ReplicaPolicyConfiguration()); - liveConfig.setName(LIVE_NODE_NAME).addConnectorConfiguration(LIVE_NODE_NAME, liveConnector).addConnectorConfiguration(BACKUP_NODE_NAME, backupConnector).setSecurityEnabled(false).addClusterConfiguration(ActiveMQTestBase.basicClusterConnectionConfig(LIVE_NODE_NAME, BACKUP_NODE_NAME)).setHAPolicyConfiguration(new ReplicatedPolicyConfiguration(HAConfigUtils.QUORUM_VOTE_WAIT_TIME_SEC)); + liveConfig.setName(LIVE_NODE_NAME).addConnectorConfiguration(LIVE_NODE_NAME, liveConnector).addConnectorConfiguration(BACKUP_NODE_NAME, backupConnector).setSecurityEnabled(false).addClusterConfiguration(ActiveMQTestBase.basicClusterConnectionConfig(LIVE_NODE_NAME, BACKUP_NODE_NAME)).setHAPolicyConfiguration(new ReplicatedPolicyConfiguration()); } } From 60c586a64c760414509c8990554e4bfbc5b0845f Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Mon, 2 Jul 2018 09:18:39 +0200 Subject: [PATCH 064/207] ARTEMIS-1951 Fix NPE on updateQueue with NULL user --- .../core/postoffice/impl/PostOfficeImpl.java | 5 ++ .../core/server/impl/ActiveMQServerImpl.java | 2 +- .../integration/client/UpdateQueueTest.java | 71 ++++++++++++++++++- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index 2b5ba602fdf..21a75048c56 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -512,6 +512,11 @@ public QueueBinding updateQueue(SimpleString name, changed = true; queue.setExclusive(exclusive); } + if (logger.isDebugEnabled()) { + if (user == null && queue.getUser() != null) { + logger.debug("Ignoring updating Queue to a NULL user"); + } + } if (user != null && !user.equals(queue.getUser())) { changed = true; queue.setUser(user); 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 882c66d4b90..9359eccd2f6 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 @@ -2970,7 +2970,7 @@ public Queue updateQueue(String name, Boolean purgeOnNoConsumers, Boolean exclusive, String user) throws Exception { - final QueueBinding queueBinding = this.postOffice.updateQueue(new SimpleString(name), routingType, maxConsumers, purgeOnNoConsumers, exclusive, new SimpleString(user)); + final QueueBinding queueBinding = this.postOffice.updateQueue(new SimpleString(name), routingType, maxConsumers, purgeOnNoConsumers, exclusive, SimpleString.toSimpleString(user)); if (queueBinding != null) { final Queue queue = queueBinding.getQueue(); return queue; diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/UpdateQueueTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/UpdateQueueTest.java index 9c8da674787..60a84a4c27b 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/UpdateQueueTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/UpdateQueueTest.java @@ -21,7 +21,6 @@ import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Session; - import java.util.EnumSet; import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -39,6 +38,76 @@ public class UpdateQueueTest extends ActiveMQTestBase { + @Test + public void testUpdateQueueWithNullUser() throws Exception { + ActiveMQServer server = createServer(true, true); + + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); + + server.start(); + + SimpleString ADDRESS = SimpleString.toSimpleString("queue.0"); + + final SimpleString user = new SimpleString("newUser"); + + Queue queue = server.createQueue(ADDRESS, RoutingType.ANYCAST, ADDRESS, user, null, true, false); + + long originalID = queue.getID(); + + Assert.assertEquals(user, queue.getUser()); + + Connection conn = factory.createConnection(); + Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer prod = session.createProducer(session.createQueue(ADDRESS.toString())); + + for (int i = 0; i < 100; i++) { + prod.send(session.createTextMessage("message " + i)); + } + + server.updateQueue(ADDRESS.toString(), RoutingType.ANYCAST, 1, false, false, null); + + conn.close(); + factory.close(); + + server.stop(); + server.start(); + + validateBindingRecords(server, JournalRecordIds.QUEUE_BINDING_RECORD, 2); + + queue = server.locateQueue(ADDRESS); + + Assert.assertNotNull("queue not found", queue); + + Assert.assertEquals("newUser", user, queue.getUser()); + + factory = new ActiveMQConnectionFactory(); + + conn = factory.createConnection(); + session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + + MessageConsumer consumer = session.createConsumer(session.createQueue(ADDRESS.toString())); + + conn.start(); + for (int i = 0; i < 100; i++) { + Assert.assertNotNull(consumer.receive(5000)); + } + + Assert.assertNull(consumer.receiveNoWait()); + + Assert.assertEquals(1, queue.getMaxConsumers()); + + conn.close(); + + Assert.assertEquals(originalID, server.locateQueue(ADDRESS).getID()); + + // stopping, restarting to make sure the system will not create an extra record without an udpate + server.stop(); + server.start(); + validateBindingRecords(server, JournalRecordIds.QUEUE_BINDING_RECORD, 2); + server.stop(); + + } + @Test public void testUpdateQueue() throws Exception { ActiveMQServer server = createServer(true, true); From 7a76d95e60f029e8ca1e1b9c05c53146c11bbb8f Mon Sep 17 00:00:00 2001 From: 17103355 <17103355@cnsuning.com> Date: Thu, 28 Jun 2018 19:53:28 +0800 Subject: [PATCH 065/207] ARTEMIS-1959 server startup failure caused by './artemis data print' --- .../impl/journal/DescribeJournal.java | 47 +++++++++- .../journal/JournalDataPrintTest.java | 90 +++++++++++++++++++ .../test/resources/dataprint/etc/broker.xml | 84 +++++++++++++++++ 3 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/journal/JournalDataPrintTest.java create mode 100644 tests/integration-tests/src/test/resources/dataprint/etc/broker.xml diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java index 20210124b9b..1842a5848ed 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java @@ -40,7 +40,10 @@ import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.UPDATE_DELIVERY_COUNT; import java.io.File; +import java.io.InputStreamReader; import java.io.PrintStream; +import java.io.Reader; +import java.net.URL; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -51,7 +54,9 @@ import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.Message; +import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.config.impl.FileConfiguration; import org.apache.activemq.artemis.core.io.SequentialFileFactory; import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory; import org.apache.activemq.artemis.core.journal.EncodingSupport; @@ -82,8 +87,12 @@ import org.apache.activemq.artemis.core.server.LargeServerMessage; import org.apache.activemq.artemis.spi.core.protocol.MessagePersister; import org.apache.activemq.artemis.utils.Base64; +import org.apache.activemq.artemis.utils.XMLUtil; import org.apache.activemq.artemis.utils.XidCodecSupport; - +import org.jboss.logging.Logger; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; /** * Outputs a String description of the Journals contents. @@ -92,8 +101,40 @@ */ public final class DescribeJournal { + private static final Logger logger = Logger.getLogger(DescribeJournal.class); + private final List records; private final List preparedTransactions; + private static final Configuration CONFIGURATION; + + static { + String instanceFolder = System.getProperty("artemis.instance"); + if (instanceFolder != null) { + CONFIGURATION = new FileConfiguration(); + File configFile = new File(instanceFolder + "/etc/broker.xml"); + URL url; + + try { + url = configFile.toURI().toURL(); + Reader reader = new InputStreamReader(url.openStream()); + String xml = XMLUtil.readerToString(reader); + xml = XMLUtil.replaceSystemProps(xml); + Element e = XMLUtil.stringToElement(xml); + + String root = ((FileConfiguration) CONFIGURATION).getRootElement(); + NodeList children = e.getElementsByTagName(root); + if (root != null && children.getLength() > 0) { + Node item = children.item(0); + XMLUtil.validate(item, ((FileConfiguration) CONFIGURATION).getSchema()); + ((FileConfiguration) CONFIGURATION).parse((Element) item, url); + } + } catch (Exception e) { + logger.error("failed to load broker.xml", e); + } + } else { + CONFIGURATION = new ConfigurationImpl(); + } + } public DescribeJournal(List records, List preparedTransactions) { this.records = records; @@ -128,9 +169,7 @@ public static DescribeJournal describeMessagesJournal(final File messagesDir, Pr SequentialFileFactory messagesFF = new NIOSequentialFileFactory(messagesDir, null, 1); // Will use only default values. The load function should adapt to anything different - ConfigurationImpl defaultValues = new ConfigurationImpl(); - - JournalImpl messagesJournal = new JournalImpl(defaultValues.getJournalFileSize(), defaultValues.getJournalMinFiles(), defaultValues.getJournalPoolFiles(), 0, 0, messagesFF, "activemq-data", "amq", 1); + JournalImpl messagesJournal = new JournalImpl(CONFIGURATION.getJournalFileSize(), CONFIGURATION.getJournalMinFiles(), CONFIGURATION.getJournalPoolFiles(), 0, 0, messagesFF, "activemq-data", "amq", 1); return describeJournal(messagesFF, messagesJournal, messagesDir, out, safe); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/journal/JournalDataPrintTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/journal/JournalDataPrintTest.java new file mode 100644 index 00000000000..8e8b0d3b066 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/journal/JournalDataPrintTest.java @@ -0,0 +1,90 @@ +/* + * 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.artemis.tests.integration.journal; + +import org.apache.activemq.artemis.cli.commands.tools.PrintData; +import org.apache.activemq.artemis.core.config.FileDeploymentManager; +import org.apache.activemq.artemis.core.config.impl.FileConfiguration; +import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; +import org.apache.activemq.artemis.jms.server.config.impl.FileJMSConfiguration; +import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; +import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +public class JournalDataPrintTest extends ActiveMQTestBase { + + protected ActiveMQServer server; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + } + + @Test + public void testJournalDataPrint() throws Exception { + ActiveMQServer server = getActiveMQServer("dataprint/etc/broker.xml"); + try { + server.start(); + server.stop(); + System.setProperty("artemis.instance", + this.getClass().getClassLoader().getResource("dataprint").getFile()); + PrintData.printData(server.getConfiguration().getBindingsLocation().getAbsoluteFile(), server.getConfiguration().getJournalLocation().getAbsoluteFile(), server.getConfiguration().getPagingLocation().getAbsoluteFile()); + + // list journal file + File dirFile = server.getConfiguration().getJournalLocation().getAbsoluteFile(); + File[] files = dirFile.listFiles(); + for (int i = 0; i < files.length; i++) { + File journalFile = files[i]; + Assert.assertEquals(30 * 1024 * 1024L, journalFile.length()); + } + + server.start(); + } finally { + try { + server.stop(); + } catch (Exception e) { + } + } + } + + protected ActiveMQServer getActiveMQServer(String brokerConfig) throws Exception { + FileConfiguration fc = new FileConfiguration(); + FileJMSConfiguration fileConfiguration = new FileJMSConfiguration(); + FileDeploymentManager deploymentManager = new FileDeploymentManager(brokerConfig); + deploymentManager.addDeployable(fc); + deploymentManager.addDeployable(fileConfiguration); + deploymentManager.readConfiguration(); + + ActiveMQJAASSecurityManager sm = new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), new SecurityConfiguration()); + + recreateDirectory(fc.getBindingsDirectory()); + recreateDirectory(fc.getJournalDirectory()); + recreateDirectory(fc.getPagingDirectory()); + recreateDirectory(fc.getLargeMessagesDirectory()); + + return addServer(new ActiveMQServerImpl(fc, sm)); + } +} diff --git a/tests/integration-tests/src/test/resources/dataprint/etc/broker.xml b/tests/integration-tests/src/test/resources/dataprint/etc/broker.xml new file mode 100644 index 00000000000..01b5591c50f --- /dev/null +++ b/tests/integration-tests/src/test/resources/dataprint/etc/broker.xml @@ -0,0 +1,84 @@ + + + + + + + + + 0.0.0.0 + + 100 + + false + + false + + + NIO + + ./target/tmp/printdata/paging + + ./target/tmp/printdata/bindings + + ./target/tmp/printdata/journal + + ./target/tmp/printdata/large-messages + + 2 + + -1 + + 30Mb + + +
+ + + + + +
+
+ + + + + +
+
+ + + + + false + DLQ + ExpiryQueue + 0 + 10Mb + 10 + BLOCK + + +
+
From a63b0315c415f8a800c185a965e523d93861cd5a Mon Sep 17 00:00:00 2001 From: Timothy Bish Date: Fri, 29 Jun 2018 15:22:05 -0400 Subject: [PATCH 066/207] ARTEMIS-1941 Preserve AMQP body section type on "large" messages When "large" messages are converted to / from core in order to be stored in the large message store the type of the AMQP body section is being lost and reconstituted incorrectly in some cases. The message needs to be annotated with the original AMQP type for the body and that used to manage the conversion back to AMQP from Core. --- .../amqp/converter/AMQPMessageSupport.java | 21 +- .../amqp/converter/AmqpCoreConverter.java | 17 + .../amqp/converter/CoreAmqpConverter.java | 75 ++++- .../JMSMappingOutboundTransformerTest.java | 307 +++++++----------- .../amqp/AmqpLargeMessageTest.java | 226 ++++++++++++- 5 files changed, 441 insertions(+), 205 deletions(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AMQPMessageSupport.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AMQPMessageSupport.java index 1bac1e55441..7c4f425c1c1 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AMQPMessageSupport.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AMQPMessageSupport.java @@ -16,13 +16,21 @@ */ package org.apache.activemq.artemis.protocol.amqp.converter; -import javax.jms.Destination; -import javax.jms.JMSException; +import static org.apache.activemq.artemis.api.core.Message.BYTES_TYPE; +import static org.apache.activemq.artemis.api.core.Message.DEFAULT_TYPE; +import static org.apache.activemq.artemis.api.core.Message.MAP_TYPE; +import static org.apache.activemq.artemis.api.core.Message.OBJECT_TYPE; +import static org.apache.activemq.artemis.api.core.Message.STREAM_TYPE; +import static org.apache.activemq.artemis.api.core.Message.TEXT_TYPE; + import java.nio.charset.Charset; import java.util.Arrays; import java.util.Map; import java.util.Set; +import javax.jms.Destination; +import javax.jms.JMSException; + import org.apache.activemq.artemis.core.message.impl.CoreMessage; import org.apache.activemq.artemis.core.message.impl.CoreMessageObjectPools; import org.apache.activemq.artemis.jms.client.ActiveMQDestination; @@ -38,13 +46,6 @@ import org.apache.qpid.proton.amqp.messaging.Data; import org.apache.qpid.proton.message.Message; -import static org.apache.activemq.artemis.api.core.Message.BYTES_TYPE; -import static org.apache.activemq.artemis.api.core.Message.DEFAULT_TYPE; -import static org.apache.activemq.artemis.api.core.Message.MAP_TYPE; -import static org.apache.activemq.artemis.api.core.Message.OBJECT_TYPE; -import static org.apache.activemq.artemis.api.core.Message.STREAM_TYPE; -import static org.apache.activemq.artemis.api.core.Message.TEXT_TYPE; - /** * Support class containing constant values and static methods that are used to map to / from * AMQP Message types being sent or received. @@ -115,6 +116,7 @@ public final class AMQPMessageSupport { public static final String JMS_AMQP_PREFIX = "JMS_AMQP_"; public static final int JMS_AMQP_PREFIX_LENGTH = JMS_AMQP_PREFIX.length(); + public static final String ORIGINAL_ENCODING = "ORIGINAL_ENCODING"; public static final String NATIVE = "NATIVE"; public static final String HEADER = "HEADER"; public static final String PROPERTIES = "PROPERTIES"; @@ -142,6 +144,7 @@ public final class AMQPMessageSupport { public static final String JMS_AMQP_DELIVERY_ANNOTATION_PREFIX = JMS_AMQP_PREFIX + DELIVERY_ANNOTATION_PREFIX; public static final String JMS_AMQP_MESSAGE_ANNOTATION_PREFIX = JMS_AMQP_PREFIX + MESSAGE_ANNOTATION_PREFIX; public static final String JMS_AMQP_FOOTER_PREFIX = JMS_AMQP_PREFIX + FOOTER_PREFIX; + public static final String JMS_AMQP_ORIGINAL_ENCODING = JMS_AMQP_PREFIX + ORIGINAL_ENCODING; // Message body type definitions public static final Binary EMPTY_BINARY = new Binary(new byte[0]); diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java index d0705795bd1..e199d1f6692 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java @@ -18,6 +18,14 @@ package org.apache.activemq.artemis.protocol.amqp.converter; import static org.apache.activemq.artemis.api.core.Message.HDR_SCHEDULED_DELIVERY_TIME; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_DATA; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_NULL; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_SEQUENCE; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_VALUE_BINARY; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_VALUE_LIST; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_VALUE_MAP; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_VALUE_NULL; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_VALUE_STRING; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_CONTENT_ENCODING; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_CONTENT_TYPE; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_FIRST_ACQUIRER; @@ -26,6 +34,7 @@ import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_HEADER_DURABLE; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_HEADER_PRIORITY; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_MESSAGE_ANNOTATION_PREFIX; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_ORIGINAL_ENCODING; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_REPLYTO_GROUP_ID; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.OCTET_STREAM_CONTENT_TYPE; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.SERIALIZED_JAVA_OBJECT_CONTENT_TYPE; @@ -108,6 +117,8 @@ public static ICoreMessage toCore(AMQPMessage message, CoreMessageObjectPools co result = createMessage(message.getMessageID(), coreMessageObjectPools); } } + + result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_NULL); } else if (body instanceof Data) { Binary payload = ((Data) body).getValue(); @@ -131,6 +142,7 @@ public static ICoreMessage toCore(AMQPMessage message, CoreMessageObjectPools co } } + result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_DATA); } else if (body instanceof AmqpSequence) { AmqpSequence sequence = (AmqpSequence) body; ServerJMSStreamMessage m = createStreamMessage(message.getMessageID(), coreMessageObjectPools); @@ -139,11 +151,13 @@ public static ICoreMessage toCore(AMQPMessage message, CoreMessageObjectPools co } result = m; + result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_SEQUENCE); } else if (body instanceof AmqpValue) { Object value = ((AmqpValue) body).getValue(); if (value == null || value instanceof String) { result = createTextMessage(message.getMessageID(), (String) value, coreMessageObjectPools); + result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, value == null ? AMQP_VALUE_NULL : AMQP_VALUE_STRING); } else if (value instanceof Binary) { Binary payload = (Binary) value; @@ -153,14 +167,17 @@ public static ICoreMessage toCore(AMQPMessage message, CoreMessageObjectPools co result = createBytesMessage(message.getMessageID(), payload.getArray(), payload.getArrayOffset(), payload.getLength(), coreMessageObjectPools); } + result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY); } else if (value instanceof List) { ServerJMSStreamMessage m = createStreamMessage(message.getMessageID(), coreMessageObjectPools); for (Object item : (List) value) { m.writeObject(item); } result = m; + result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_LIST); } else if (value instanceof Map) { result = createMapMessage(message.getMessageID(), (Map) value, coreMessageObjectPools); + result.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_MAP); } else { ByteBuf buf = PooledByteBufAllocator.DEFAULT.heapBuffer(1024); try { diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java index 49372dbc1e9..eb20219641e 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java @@ -19,6 +19,13 @@ import static org.apache.activemq.artemis.api.core.FilterConstants.NATIVE_MESSAGE_ID; import static org.apache.activemq.artemis.api.core.Message.HDR_SCHEDULED_DELIVERY_TIME; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_DATA; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_NULL; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_SEQUENCE; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_UNKNOWN; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_VALUE_BINARY; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_VALUE_LIST; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_VALUE_STRING; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.EMPTY_BINARY; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_CONTENT_ENCODING; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_CONTENT_TYPE; @@ -30,6 +37,7 @@ import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_HEADER_PRIORITY; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_MESSAGE_ANNOTATION_PREFIX; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_NATIVE; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_ORIGINAL_ENCODING; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_PREFIX; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_PROPERTIES; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_REPLYTO_GROUP_ID; @@ -254,6 +262,9 @@ public static AMQPMessage fromCore(ICoreMessage coreMessage) throws Exception { } else if (key.equals(JMS_AMQP_REPLYTO_GROUP_ID)) { properties.setReplyToGroupId(message.getStringProperty(key)); continue; + } else if (key.equals(JMS_AMQP_ORIGINAL_ENCODING)) { + // skip..remove annotation from previous inbound transformation + continue; } else if (key.startsWith(JMS_AMQP_FOOTER_PREFIX)) { if (footerMap == null) { footerMap = new HashMap<>(); @@ -331,6 +342,13 @@ public static AMQPMessage fromCore(ICoreMessage coreMessage) throws Exception { private static Section convertBody(ServerJMSMessage message, Map maMap, Properties properties) throws JMSException { Section body = null; + short orignalEncoding = AMQP_UNKNOWN; + + try { + orignalEncoding = message.getShortProperty(JMS_AMQP_ORIGINAL_ENCODING); + } catch (Exception ex) { + // Ignore and stick with UNKNOWN + } if (message instanceof ServerJMSBytesMessage) { Binary payload = getBinaryFromMessageBody((ServerJMSBytesMessage) message); @@ -338,11 +356,40 @@ private static Section convertBody(ServerJMSMessage message, Map maMap.put(AMQPMessageSupport.JMS_MSG_TYPE, AMQPMessageSupport.JMS_BYTES_MESSAGE); if (payload == null) { payload = EMPTY_BINARY; - } else { - body = new AmqpValue(payload); + } + + switch (orignalEncoding) { + case AMQP_NULL: + break; + case AMQP_VALUE_BINARY: + body = new AmqpValue(payload); + break; + case AMQP_DATA: + case AMQP_UNKNOWN: + default: + body = new Data(payload); + break; } } else if (message instanceof ServerJMSTextMessage) { - body = new AmqpValue(((TextMessage) message).getText()); + String text = (((TextMessage) message).getText()); + + switch (orignalEncoding) { + case AMQP_NULL: + break; + case AMQP_DATA: + if (text == null) { + body = new Data(EMPTY_BINARY); + } else { + body = new Data(new Binary(text.getBytes(StandardCharsets.UTF_8))); + } + break; + case AMQP_VALUE_STRING: + case AMQP_UNKNOWN: + default: + body = new AmqpValue(text); + break; + } + maMap.put(AMQPMessageSupport.JMS_MSG_TYPE, AMQPMessageSupport.JMS_TEXT_MESSAGE); } else if (message instanceof ServerJMSMapMessage) { body = new AmqpValue(getMapFromMessageBody((ServerJMSMapMessage) message)); @@ -358,7 +405,16 @@ private static Section convertBody(ServerJMSMessage message, Map } catch (MessageEOFException e) { } - body = new AmqpSequence(list); + switch (orignalEncoding) { + case AMQP_SEQUENCE: + body = new AmqpSequence(list); + break; + case AMQP_VALUE_LIST: + case AMQP_UNKNOWN: + default: + body = new AmqpValue(list); + break; + } } else if (message instanceof ServerJMSObjectMessage) { properties.setContentType(AMQPMessageSupport.SERIALIZED_JAVA_OBJECT_CONTENT_TYPE); maMap.put(AMQPMessageSupport.JMS_MSG_TYPE, AMQPMessageSupport.JMS_OBJECT_MESSAGE); @@ -368,7 +424,16 @@ private static Section convertBody(ServerJMSMessage message, Map payload = EMPTY_BINARY; } - body = new Data(payload); + switch (orignalEncoding) { + case AMQP_VALUE_BINARY: + body = new AmqpValue(payload); + break; + case AMQP_DATA: + case AMQP_UNKNOWN: + default: + body = new Data(payload); + break; + } // For a non-AMQP message we tag the outbound content type as containing // a serialized Java object so that an AMQP client has a hint as to what diff --git a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/message/JMSMappingOutboundTransformerTest.java b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/message/JMSMappingOutboundTransformerTest.java index ccafd37b044..f1cd588cb01 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/message/JMSMappingOutboundTransformerTest.java +++ b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/message/JMSMappingOutboundTransformerTest.java @@ -16,16 +16,32 @@ */ package org.apache.activemq.artemis.protocol.amqp.converter.message; -import javax.jms.JMSException; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_DATA; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_NULL; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_SEQUENCE; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_UNKNOWN; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_VALUE_BINARY; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_VALUE_LIST; +import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.JMS_AMQP_ORIGINAL_ENCODING; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.UUID; +import javax.jms.JMSException; + import org.apache.activemq.artemis.core.buffers.impl.ResetLimitWrappedActiveMQBuffer; import org.apache.activemq.artemis.core.message.impl.CoreMessage; import org.apache.activemq.artemis.protocol.amqp.converter.AMQPConverter; @@ -44,39 +60,47 @@ import org.apache.qpid.proton.amqp.messaging.Data; import org.apache.qpid.proton.amqp.messaging.MessageAnnotations; import org.apache.qpid.proton.message.Message; -import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - public class JMSMappingOutboundTransformerTest { private final UUID TEST_OBJECT_VALUE = UUID.fromString("fee14b62-09e0-4ac6-a4c3-4206c630d844"); private final String TEST_ADDRESS = "queue://testAddress"; - public static final byte QUEUE_TYPE = 0x00; public static final byte TOPIC_TYPE = 0x01; public static final byte TEMP_QUEUE_TYPE = 0x02; public static final byte TEMP_TOPIC_TYPE = 0x03; - @Before - public void setUp() { + // ----- no-body Message type tests ---------------------------------------// + + @Test + public void testConvertMessageToAmqpMessageWithNoBody() throws Exception { + ServerJMSMessage outbound = createMessage(); + outbound.encode(); + + Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); + + assertNull(amqp.getBody()); } - // ----- no-body Message type tests ---------------------------------------// + @Test + public void testConvertTextMessageToAmqpMessageWithNoBodyOriginalEncodingWasNull() throws Exception { + ServerJMSMessage outbound = createMessage(); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_NULL); + outbound.encode(); + + Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); + + assertNull(amqp.getBody()); + } + + // ----- BytesMessage type tests ---------------------------------------// - @Ignore("Compressed message body support not yet implemented.") @Test - public void testConvertCompressedBytesMessageToAmqpMessageWithDataBody() throws Exception { - byte[] expectedPayload = new byte[] {8, 16, 24, 32}; - ServerJMSBytesMessage outbound = createBytesMessage(true); + public void testConvertBytesMessageToAmqpMessageWithDataBody() throws Exception { + byte[] expectedPayload = new byte[]{8, 16, 24, 32}; + ServerJMSBytesMessage outbound = createBytesMessage(); outbound.writeBytes(expectedPayload); outbound.encode(); @@ -96,6 +120,7 @@ public void testConvertCompressedBytesMessageToAmqpMessageWithDataBody() throws @Test public void testConvertEmptyBytesMessageToAmqpMessageWithAmqpValueBody() throws Exception { ServerJMSBytesMessage outbound = createBytesMessage(); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY); outbound.encode(); Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); @@ -107,30 +132,10 @@ public void testConvertEmptyBytesMessageToAmqpMessageWithAmqpValueBody() throws } @Test - public void testConvertUncompressedBytesMessageToAmqpMessageWithAmqpValueBody() throws Exception { - byte[] expectedPayload = new byte[] {8, 16, 24, 32}; + public void testConvertBytesMessageToAmqpMessageWithAmqpValueBody() throws Exception { + byte[] expectedPayload = new byte[]{8, 16, 24, 32}; ServerJMSBytesMessage outbound = createBytesMessage(); - outbound.writeBytes(expectedPayload); - outbound.encode(); - - Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); - - assertNotNull(amqp.getBody()); - assertTrue(amqp.getBody() instanceof AmqpValue); - assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Binary); - assertEquals(4, ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength()); - - Binary amqpData = (Binary) ((AmqpValue) amqp.getBody()).getValue(); - Binary inputData = new Binary(expectedPayload); - - assertTrue(inputData.equals(amqpData)); - } - - @Ignore("Compressed message body support not yet implemented.") - @Test - public void testConvertCompressedBytesMessageToAmqpMessageWithAmqpValueBody() throws Exception { - byte[] expectedPayload = new byte[] {8, 16, 24, 32}; - ServerJMSBytesMessage outbound = createBytesMessage(true); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY); outbound.writeBytes(expectedPayload); outbound.encode(); @@ -163,7 +168,7 @@ public void testConvertMapMessageToAmqpMessageWithNoBody() throws Exception { @Test public void testConvertMapMessageToAmqpMessageWithByteArrayValueInBody() throws Exception { - final byte[] byteArray = new byte[] {1, 2, 3, 4, 5}; + final byte[] byteArray = new byte[]{1, 2, 3, 4, 5}; ServerJMSMapMessage outbound = createMapMessage(); outbound.setBytes("bytes", byteArray); @@ -204,42 +209,32 @@ public void testConvertMapMessageToAmqpMessage() throws Exception { assertTrue("string".equals(amqpMap.get("property-1"))); } + //----- StreamMessage type tests -----------------------------------------// + @Test - public void testConvertCompressedMapMessageToAmqpMessage() throws Exception { - ServerJMSMapMessage outbound = createMapMessage(true); - outbound.setString("property-1", "string"); - outbound.setInt("property-2", 1); - outbound.setBoolean("property-3", true); + public void testConvertStreamMessageToAmqpMessageWithAmqpValueBodyNoPropertySet() throws Exception { + ServerJMSStreamMessage outbound = createStreamMessage(); + outbound.writeBoolean(false); + outbound.writeString("test"); outbound.encode(); Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); assertNotNull(amqp.getBody()); assertTrue(amqp.getBody() instanceof AmqpValue); - assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Map); + + AmqpValue list = (AmqpValue) amqp.getBody(); @SuppressWarnings("unchecked") - Map amqpMap = (Map) ((AmqpValue) amqp.getBody()).getValue(); + List amqpList = (List) list.getValue(); - assertEquals(3, amqpMap.size()); - assertTrue("string".equals(amqpMap.get("property-1"))); + assertEquals(2, amqpList.size()); } @Test - public void testConvertStreamMessageToAmqpMessageWithAmqpSequencey() throws Exception { + public void testConvertStreamMessageToAmqpMessageWithAmqpValueBody() throws Exception { ServerJMSStreamMessage outbound = createStreamMessage(); - outbound.encode(); - - Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); - - assertNotNull(amqp.getBody()); - assertTrue(amqp.getBody() instanceof AmqpSequence); - assertTrue(((AmqpSequence) amqp.getBody()).getValue() instanceof List); - } - - @Test - public void testConvertCompressedStreamMessageToAmqpMessageWithAmqpValueBody() throws Exception { - ServerJMSStreamMessage outbound = createStreamMessage(true); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_LIST); outbound.writeBoolean(false); outbound.writeString("test"); outbound.encode(); @@ -247,19 +242,20 @@ public void testConvertCompressedStreamMessageToAmqpMessageWithAmqpValueBody() t Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); assertNotNull(amqp.getBody()); - assertTrue(amqp.getBody() instanceof AmqpSequence); + assertTrue(amqp.getBody() instanceof AmqpValue); - AmqpSequence list = (AmqpSequence)amqp.getBody(); + AmqpValue list = (AmqpValue) amqp.getBody(); @SuppressWarnings("unchecked") - List amqpList = list.getValue(); + List amqpList = (List) list.getValue(); assertEquals(2, amqpList.size()); } @Test - public void testConvertCompressedStreamMessageToAmqpMessageWithAmqpSequencey() throws Exception { - ServerJMSStreamMessage outbound = createStreamMessage(true); + public void testConvertStreamMessageToAmqpMessageWithAmqpSequencey() throws Exception { + ServerJMSStreamMessage outbound = createStreamMessage(); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_SEQUENCE); outbound.writeBoolean(false); outbound.writeString("test"); outbound.encode(); @@ -293,6 +289,7 @@ public void testConvertEmptyObjectMessageToAmqpMessageWithDataBody() throws Exce @Test public void testConvertEmptyObjectMessageToAmqpMessageUnknownEncodingGetsDataSection() throws Exception { ServerJMSObjectMessage outbound = createObjectMessage(); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_UNKNOWN); outbound.encode(); Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); @@ -301,6 +298,7 @@ public void testConvertEmptyObjectMessageToAmqpMessageUnknownEncodingGetsDataSec assertTrue(amqp.getBody() instanceof Data); assertEquals(5, ((Data) amqp.getBody()).getValue().getLength()); } + @Test public void testConvertObjectMessageToAmqpMessageWithDataBody() throws Exception { ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE); @@ -320,6 +318,7 @@ public void testConvertObjectMessageToAmqpMessageWithDataBody() throws Exception @Test public void testConvertObjectMessageToAmqpMessageUnknownEncodingGetsDataSection() throws Exception { ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_UNKNOWN); outbound.encode(); Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); @@ -334,52 +333,35 @@ public void testConvertObjectMessageToAmqpMessageUnknownEncodingGetsDataSection( } @Test - public void testConvertCompressedObjectMessageToAmqpMessageWithDataBody() throws Exception { - ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE, true); - outbound.encode(); - - Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); - - assertNotNull(amqp.getBody()); - assertTrue(amqp.getBody() instanceof Data); - assertFalse(0 == ((Data) amqp.getBody()).getValue().getLength()); - - Object value = deserialize(((Data) amqp.getBody()).getValue().getArray()); - assertNotNull(value); - assertTrue(value instanceof UUID); - } - - @Test - public void testConvertCompressedObjectMessageToAmqpMessageUnknownEncodingGetsDataSection() throws Exception { - ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE, true); + public void testConvertObjectMessageToAmqpMessageWithAmqpValueBody() throws Exception { + ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY); outbound.encode(); Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); assertNotNull(amqp.getBody()); assertTrue(amqp.getBody() instanceof Data); - assertFalse(0 == ((Data) amqp.getBody()).getValue().getLength()); + assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary); + assertFalse(0 == ((Binary) ((Data) amqp.getBody()).getValue()).getLength()); - Object value = deserialize(((Data) amqp.getBody()).getValue().getArray()); + Object value = deserialize((((Data) amqp.getBody()).getValue()).getArray()); assertNotNull(value); assertTrue(value instanceof UUID); } @Test - public void testConvertCompressedObjectMessageToAmqpMessageWithAmqpValueBody() throws Exception { - ServerJMSObjectMessage outbound = createObjectMessage(TEST_OBJECT_VALUE, true); + public void testConvertEmptyObjectMessageToAmqpMessageWithAmqpValueBody() throws Exception { + ServerJMSObjectMessage outbound = createObjectMessage(); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_VALUE_BINARY); outbound.encode(); Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); assertNotNull(amqp.getBody()); - assertTrue(amqp.getBody() instanceof Data); - assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary); - assertFalse(0 == ((Binary) ((Data) amqp.getBody()).getValue()).getLength()); - - Object value = deserialize((((Data) amqp.getBody()).getValue()).getArray()); - assertNotNull(value); - assertTrue(value instanceof UUID); + assertTrue(amqp.getBody() instanceof AmqpValue); + assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Binary); + assertEquals(0, ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength()); } // ----- TextMessage type tests -------------------------------------------// @@ -423,9 +405,9 @@ public void testConvertTextMessageContentNotStoredCreatesAmqpValueStringBody() t } @Test - public void testConvertCompressedTextMessageCreatesDataSectionBody() throws Exception { + public void testConvertTextMessageCreatesDataSectionBody() throws Exception { String contentString = "myTextMessageContent"; - ServerJMSTextMessage outbound = createTextMessage(contentString, true); + ServerJMSTextMessage outbound = createTextMessage(contentString); outbound.encode(); Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); @@ -433,39 +415,57 @@ public void testConvertCompressedTextMessageCreatesDataSectionBody() throws Exce assertNotNull(amqp.getBody()); assertTrue(amqp.getBody() instanceof AmqpValue); - AmqpValue value = (AmqpValue)amqp.getBody(); + AmqpValue value = (AmqpValue) amqp.getBody(); assertEquals(contentString, value.getValue()); } - // ----- Test JMSDestination Handling -------------------------------------// - @Test - public void testConvertMessageWithJMSDestinationNull() throws Exception { - doTestConvertMessageWithJMSDestination(null, null); - } + public void testConvertTextMessageCreatesBodyUsingOriginalEncodingWithDataSection() throws Exception { + String contentString = "myTextMessageContent"; + ServerJMSTextMessage outbound = createTextMessage(contentString); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_DATA); + outbound.encode(); - @Test - public void testConvertMessageWithJMSDestinationQueue() throws Exception { - doTestConvertMessageWithJMSDestination(createDestination(QUEUE_TYPE), QUEUE_TYPE); + Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); + + assertNotNull(amqp.getBody()); + assertTrue(amqp.getBody() instanceof Data); + assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary); + + Binary data = ((Data) amqp.getBody()).getValue(); + String contents = new String(data.getArray(), data.getArrayOffset(), data.getLength(), StandardCharsets.UTF_8); + assertEquals(contentString, contents); } - @Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP") @Test - public void testConvertMessageWithJMSDestinationTemporaryQueue() throws Exception { - doTestConvertMessageWithJMSDestination(createDestination(TEMP_QUEUE_TYPE), TEMP_QUEUE_TYPE); + public void testConvertTextMessageContentNotStoredCreatesBodyUsingOriginalEncodingWithDataSection() throws Exception { + String contentString = "myTextMessageContent"; + ServerJMSTextMessage outbound = createTextMessage(contentString); + outbound.setShortProperty(JMS_AMQP_ORIGINAL_ENCODING, AMQP_DATA); + outbound.encode(); + + Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); + + assertNotNull(amqp.getBody()); + assertTrue(amqp.getBody() instanceof Data); + assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary); + + Binary data = ((Data) amqp.getBody()).getValue(); + String contents = new String(data.getArray(), data.getArrayOffset(), data.getLength(), StandardCharsets.UTF_8); + assertEquals(contentString, contents); } - @Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP") + // ----- Test JMSDestination Handling -------------------------------------// + @Test - public void testConvertMessageWithJMSDestinationTopic() throws Exception { - doTestConvertMessageWithJMSDestination(createDestination(TOPIC_TYPE), TOPIC_TYPE); + public void testConvertMessageWithJMSDestinationNull() throws Exception { + doTestConvertMessageWithJMSDestination(null, null); } - @Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP") @Test - public void testConvertMessageWithJMSDestinationTemporaryTopic() throws Exception { - doTestConvertMessageWithJMSDestination(createDestination(TEMP_TOPIC_TYPE), TEMP_TOPIC_TYPE); + public void testConvertMessageWithJMSDestinationQueue() throws Exception { + doTestConvertMessageWithJMSDestination(createDestination(QUEUE_TYPE), QUEUE_TYPE); } private void doTestConvertMessageWithJMSDestination(ServerDestination jmsDestination, Object expectedAnnotationValue) throws Exception { @@ -501,24 +501,6 @@ public void testConvertMessageWithJMSReplyToQueue() throws Exception { doTestConvertMessageWithJMSReplyTo(createDestination(QUEUE_TYPE), QUEUE_TYPE); } - @Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP") - @Test - public void testConvertMessageWithJMSReplyToTemporaryQueue() throws Exception { - doTestConvertMessageWithJMSReplyTo(createDestination(TEMP_QUEUE_TYPE), TEMP_QUEUE_TYPE); - } - - @Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP") - @Test - public void testConvertMessageWithJMSReplyToTopic() throws Exception { - doTestConvertMessageWithJMSReplyTo(createDestination(TOPIC_TYPE), TOPIC_TYPE); - } - - @Ignore("Artemis code doesn't provide a means of supplying a typed destination to AMQP") - @Test - public void testConvertMessageWithJMSReplyToTemporaryTopic() throws Exception { - doTestConvertMessageWithJMSReplyTo(createDestination(TEMP_TOPIC_TYPE), TEMP_TOPIC_TYPE); - } - private void doTestConvertMessageWithJMSReplyTo(ServerDestination jmsReplyTo, Object expectedAnnotationValue) throws Exception { ServerJMSTextMessage textMessage = createTextMessage(); textMessage.setText("myTextMessageContent"); @@ -542,7 +524,6 @@ private void doTestConvertMessageWithJMSReplyTo(ServerDestination jmsReplyTo, Ob // ----- Utility Methods used for this Test -------------------------------// - private ServerDestination createDestination(byte destType) { ServerDestination destination = null; switch (destType) { @@ -570,45 +551,15 @@ private ServerJMSMessage createMessage() { } private ServerJMSBytesMessage createBytesMessage() { - return createBytesMessage(false); - } - - private ServerJMSBytesMessage createBytesMessage(boolean compression) { - ServerJMSBytesMessage message = new ServerJMSBytesMessage(newMessage(org.apache.activemq.artemis.api.core.Message.BYTES_TYPE)); - - if (compression) { - // TODO - } - - return message; + return new ServerJMSBytesMessage(newMessage(org.apache.activemq.artemis.api.core.Message.BYTES_TYPE)); } private ServerJMSMapMessage createMapMessage() { - return createMapMessage(false); - } - - private ServerJMSMapMessage createMapMessage(boolean compression) { - ServerJMSMapMessage message = new ServerJMSMapMessage(newMessage(org.apache.activemq.artemis.api.core.Message.MAP_TYPE)); - - if (compression) { - // TODO - } - - return message; + return new ServerJMSMapMessage(newMessage(org.apache.activemq.artemis.api.core.Message.MAP_TYPE)); } private ServerJMSStreamMessage createStreamMessage() { - return createStreamMessage(false); - } - - private ServerJMSStreamMessage createStreamMessage(boolean compression) { - ServerJMSStreamMessage message = new ServerJMSStreamMessage(newMessage(org.apache.activemq.artemis.api.core.Message.STREAM_TYPE)); - - if (compression) { - // TODO - } - - return message; + return new ServerJMSStreamMessage(newMessage(org.apache.activemq.artemis.api.core.Message.STREAM_TYPE)); } private ServerJMSObjectMessage createObjectMessage() { @@ -616,16 +567,8 @@ private ServerJMSObjectMessage createObjectMessage() { } private ServerJMSObjectMessage createObjectMessage(Serializable payload) { - return createObjectMessage(payload, false); - } - - private ServerJMSObjectMessage createObjectMessage(Serializable payload, boolean compression) { ServerJMSObjectMessage result = AMQPMessageSupport.createObjectMessage(0, null); - if (compression) { - // TODO - } - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos);) { oos.writeObject(payload); @@ -643,16 +586,8 @@ private ServerJMSTextMessage createTextMessage() { } private ServerJMSTextMessage createTextMessage(String text) { - return createTextMessage(text, false); - } - - private ServerJMSTextMessage createTextMessage(String text, boolean compression) { ServerJMSTextMessage result = AMQPMessageSupport.createTextMessage(0, null); - if (compression) { - // TODO - } - try { result.setText(text); } catch (JMSException e) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java index 6bd550a27ee..8465c611283 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java @@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.TimeUnit; @@ -49,6 +50,7 @@ import org.apache.activemq.transport.amqp.client.AmqpSession; import org.apache.qpid.jms.JmsConnectionFactory; import org.apache.qpid.proton.amqp.Binary; +import org.apache.qpid.proton.amqp.messaging.AmqpSequence; import org.apache.qpid.proton.amqp.messaging.AmqpValue; import org.apache.qpid.proton.amqp.messaging.Data; import org.apache.qpid.proton.amqp.messaging.Section; @@ -149,8 +151,7 @@ private void sendMessages(int nMsgs, AmqpConnection connection) throws Exception session.close(); } - private void receiveJMS(int nMsgs, - ConnectionFactory factory) throws JMSException { + private void receiveJMS(int nMsgs, ConnectionFactory factory) throws JMSException { Connection connection2 = factory.createConnection(); Session session2 = connection2.createSession(false, Session.AUTO_ACKNOWLEDGE); connection2.start(); @@ -397,9 +398,8 @@ public void testReceiveRedeliveredLargeMessagesWithSessionFlowControl() throws E Section body = message.getWrappedMessage().getBody(); assertNotNull("No message body for msg " + i, body); - //TODO: ARTEMIS-1941 raised. This is wrong, test sent a Data section, it got converted in transit. - assertTrue("Unexpected message body type for msg " + body.getClass(), body instanceof AmqpValue); - assertEquals("Unexpected body content for msg", new Binary(payload, 0, payload.length), ((AmqpValue) body).getValue()); + assertTrue("Unexpected message body type for msg " + body.getClass(), body instanceof Data); + assertEquals("Unexpected body content for msg", new Binary(payload, 0, payload.length), ((Data) body).getValue()); message.accept(); } @@ -411,6 +411,209 @@ public void testReceiveRedeliveredLargeMessagesWithSessionFlowControl() throws E } } + @Test(timeout = 60000) + public void testMessageWithAmqpValueAndEmptyBinaryPreservesBody() throws Exception { + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST)); + + AmqpClient client = createAmqpClient(); + AmqpConnection connection = addConnection(client.connect()); + try { + AmqpSession session = connection.createSession(); + AmqpSender sender = session.createSender(getTestName()); + + AmqpMessage message = createAmqpLargeMessageWithNoBody(); + + message.getWrappedMessage().setBody(new AmqpValue(new Binary(new byte[0]))); + + sender.send(message); + sender.close(); + + AmqpReceiver receiver = session.createReceiver(getTestName()); + receiver.flow(1); + + AmqpMessage received = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull("failed to read large AMQP message", received); + MessageImpl wrapped = (MessageImpl) received.getWrappedMessage(); + + assertTrue(wrapped.getBody() instanceof AmqpValue); + AmqpValue body = (AmqpValue) wrapped.getBody(); + assertTrue(body.getValue() instanceof Binary); + Binary payload = (Binary) body.getValue(); + assertEquals(0, payload.getLength()); + + received.accept(); + session.close(); + } finally { + connection.close(); + } + } + + @Test(timeout = 60000) + public void testMessageWithDataAndEmptyBinaryPreservesBody() throws Exception { + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST)); + + AmqpClient client = createAmqpClient(); + AmqpConnection connection = addConnection(client.connect()); + try { + AmqpSession session = connection.createSession(); + AmqpSender sender = session.createSender(getTestName()); + + AmqpMessage message = createAmqpLargeMessageWithNoBody(); + + message.getWrappedMessage().setBody(new Data(new Binary(new byte[0]))); + + sender.send(message); + sender.close(); + + AmqpReceiver receiver = session.createReceiver(getTestName()); + receiver.flow(1); + + AmqpMessage received = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull("failed to read large AMQP message", received); + MessageImpl wrapped = (MessageImpl) received.getWrappedMessage(); + + assertTrue(wrapped.getBody() instanceof Data); + Data body = (Data) wrapped.getBody(); + assertTrue(body.getValue() instanceof Binary); + Binary payload = (Binary) body.getValue(); + assertEquals(0, payload.getLength()); + + received.accept(); + session.close(); + } finally { + connection.close(); + } + } + + @Test(timeout = 60000) + public void testMessageWithDataAndContentTypeOfTextPreservesBodyType() throws Exception { + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST)); + + AmqpClient client = createAmqpClient(); + AmqpConnection connection = addConnection(client.connect()); + try { + AmqpSession session = connection.createSession(); + AmqpSender sender = session.createSender(getTestName()); + + AmqpMessage message = createAmqpLargeMessageWithNoBody(); + + String messageText = "This text will be in a Data Section"; + + message.getWrappedMessage().setContentType("text/plain"); + message.getWrappedMessage().setBody(new Data(new Binary(messageText.getBytes(StandardCharsets.UTF_8)))); + + sender.send(message); + sender.close(); + + AmqpReceiver receiver = session.createReceiver(getTestName()); + receiver.flow(1); + + AmqpMessage received = receiver.receive(10, TimeUnit.SECONDS); + assertNotNull("failed to read large AMQP message", received); + MessageImpl wrapped = (MessageImpl) received.getWrappedMessage(); + + assertTrue(wrapped.getBody() instanceof Data); + Data body = (Data) wrapped.getBody(); + assertTrue(body.getValue() instanceof Binary); + Binary payload = (Binary) body.getValue(); + String reconstitutedString = new String( + payload.getArray(), payload.getArrayOffset(), payload.getLength(), StandardCharsets.UTF_8); + + assertEquals(messageText, reconstitutedString); + + received.accept(); + session.close(); + } finally { + connection.close(); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test(timeout = 60000) + public void testMessageWithAmqpValueListPreservesBodyType() throws Exception { + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST)); + + AmqpClient client = createAmqpClient(); + AmqpConnection connection = addConnection(client.connect()); + try { + AmqpSession session = connection.createSession(); + AmqpSender sender = session.createSender(getTestName()); + + AmqpMessage message = createAmqpLargeMessageWithNoBody(); + + List values = new ArrayList<>(); + values.add("1"); + values.add("2"); + values.add("3"); + + message.getWrappedMessage().setBody(new AmqpValue(values)); + + sender.send(message); + sender.close(); + + AmqpReceiver receiver = session.createReceiver(getTestName()); + receiver.flow(1); + + AmqpMessage received = receiver.receive(10, TimeUnit.SECONDS); + assertNotNull("failed to read large AMQP message", received); + MessageImpl wrapped = (MessageImpl) received.getWrappedMessage(); + + assertTrue(wrapped.getBody() instanceof AmqpValue); + AmqpValue body = (AmqpValue) wrapped.getBody(); + assertTrue(body.getValue() instanceof List); + List payload = (List) body.getValue(); + assertEquals(3, payload.size()); + + received.accept(); + session.close(); + } finally { + connection.close(); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test(timeout = 60000) + public void testMessageWithAmqpSequencePreservesBodyType() throws Exception { + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST)); + + AmqpClient client = createAmqpClient(); + AmqpConnection connection = addConnection(client.connect()); + try { + AmqpSession session = connection.createSession(); + AmqpSender sender = session.createSender(getTestName()); + + AmqpMessage message = createAmqpLargeMessageWithNoBody(); + + List values = new ArrayList<>(); + values.add("1"); + values.add("2"); + values.add("3"); + + message.getWrappedMessage().setBody(new AmqpSequence(values)); + + sender.send(message); + sender.close(); + + AmqpReceiver receiver = session.createReceiver(getTestName()); + receiver.flow(1); + + AmqpMessage received = receiver.receive(10, TimeUnit.SECONDS); + assertNotNull("failed to read large AMQP message", received); + MessageImpl wrapped = (MessageImpl) received.getWrappedMessage(); + + assertTrue(wrapped.getBody() instanceof AmqpSequence); + AmqpSequence body = (AmqpSequence) wrapped.getBody(); + assertTrue(body.getValue() instanceof List); + List payload = (List) body.getValue(); + assertEquals(3, payload.size()); + + received.accept(); + session.close(); + } finally { + connection.close(); + } + } + private void sendObjectMessages(int nMsgs, ConnectionFactory factory) throws Exception { try (Connection connection = factory.createConnection()) { Session session = connection.createSession(); @@ -483,4 +686,17 @@ private AmqpMessage createAmqpMessage(byte value, int payloadSize) { message.setBytes(payload); return message; } + + private AmqpMessage createAmqpLargeMessageWithNoBody() { + AmqpMessage message = new AmqpMessage(); + + byte[] payload = new byte[512 * 1024]; + for (int i = 0; i < payload.length; i++) { + payload[i] = (byte) 65; + } + + message.setMessageAnnotation("x-opt-big-blob", new String(payload, StandardCharsets.UTF_8)); + + return message; + } } From 2ff4faab058c330ee9df1bcaa5e3a37ab60cc714 Mon Sep 17 00:00:00 2001 From: gtully Date: Fri, 6 Jul 2018 12:38:20 -0500 Subject: [PATCH 067/207] ARTEMIS-1970 Clean up LDAP connection in JAAS login module --- .../core/security/jaas/LDAPLoginModule.java | 1 + .../security/jaas/LDAPLoginModuleTest.java | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java index cc3c824d02e..f8d7db5f0a0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java @@ -220,6 +220,7 @@ public boolean commit() throws LoginException { private void clear() { username = null; userAuthenticated = false; + closeContext(); } @Override diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java index 4fbd2c8057f..97be299d6bf 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java @@ -45,6 +45,7 @@ import org.apache.directory.server.core.annotations.ApplyLdifFiles; import org.apache.directory.server.core.integ.AbstractLdapTestUnit; import org.apache.directory.server.core.integ.FrameworkRunner; +import org.jboss.logging.Logger; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -60,6 +61,8 @@ @ApplyLdifFiles("test.ldif") public class LDAPLoginModuleTest extends AbstractLdapTestUnit { + private static final Logger logger = Logger.getLogger(LDAPLoginModuleTest.class); + private static final String PRINCIPAL = "uid=admin,ou=system"; private static final String CREDENTIALS = "secret"; @@ -109,6 +112,8 @@ public void testRunning() throws Exception { @Test public void testLogin() throws LoginException { + logger.info("num session: " + ldapServer.getLdapSessionManager().getSessions().length); + LoginContext context = new LoginContext("LDAPLogin", new CallbackHandler() { @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { @@ -125,6 +130,24 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback }); context.login(); context.logout(); + + assertTrue("no sessions after logout", waitForSessions(0)); + } + + private boolean waitForSessions(int expected) { + final long expiry = System.currentTimeMillis() + 5000; + int numSession = ldapServer.getLdapSessionManager().getSessions().length; + while (numSession != expected && System.currentTimeMillis() < expiry) { + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException ok) { + break; + } + numSession = ldapServer.getLdapSessionManager().getSessions().length; + logger.info("num session " + numSession); + + } + return numSession == expected; } @Test @@ -150,6 +173,7 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback return; } fail("Should have failed authenticating"); + assertTrue("no sessions after logout", waitForSessions(0)); } @Test From d54e5a786875f7756c61e33092a0cca018426adb Mon Sep 17 00:00:00 2001 From: gtully Date: Fri, 6 Jul 2018 16:48:13 +0100 Subject: [PATCH 068/207] ARTEMIS-1971 Support connection pooling in LDAPLoginModule --- .../core/security/jaas/LDAPLoginModule.java | 12 ++- .../security/jaas/LDAPLoginModuleTest.java | 77 +++++++++++++++++++ .../src/test/resources/login.config | 21 +++++ docs/user-manual/en/security.md | 7 ++ 4 files changed, 116 insertions(+), 1 deletion(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java index f8d7db5f0a0..19194fab172 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java @@ -85,6 +85,8 @@ public class LDAPLoginModule implements LoginModule { private static final String AUTHENTICATE_USER = "authenticateUser"; private static final String REFERRAL = "referral"; private static final String PASSWORD_CODEC = "passwordCodec"; + private static final String CONNECTION_POOL = "connectionPool"; + private static final String CONNECTION_TIMEOUT = "connectionTimeout"; protected DirContext context; @@ -128,7 +130,9 @@ public void initialize(Subject subject, new LDAPLoginProperty(PASSWORD_CODEC, (String) options.get(PASSWORD_CODEC)), new LDAPLoginProperty(SASL_LOGIN_CONFIG_SCOPE, (String) options.get(SASL_LOGIN_CONFIG_SCOPE)), new LDAPLoginProperty(AUTHENTICATE_USER, (String) options.get(AUTHENTICATE_USER)), - new LDAPLoginProperty(REFERRAL, (String) options.get(REFERRAL))}; + new LDAPLoginProperty(REFERRAL, (String) options.get(REFERRAL)), + new LDAPLoginProperty(CONNECTION_POOL, (String) options.get(CONNECTION_POOL)), + new LDAPLoginProperty(CONNECTION_TIMEOUT, (String) options.get(CONNECTION_TIMEOUT))}; if (isLoginPropertySet(AUTHENTICATE_USER)) { authenticateUser = Boolean.valueOf(getLDAPPropertyValue(AUTHENTICATE_USER)); @@ -580,6 +584,12 @@ protected void openContext() throws Exception { env.put(Context.SECURITY_PROTOCOL, getLDAPPropertyValue(CONNECTION_PROTOCOL)); env.put(Context.PROVIDER_URL, getLDAPPropertyValue(CONNECTION_URL)); env.put(Context.SECURITY_AUTHENTICATION, getLDAPPropertyValue(AUTHENTICATION)); + if (isLoginPropertySet(CONNECTION_POOL)) { + env.put("com.sun.jndi.ldap.connect.pool", getLDAPPropertyValue(CONNECTION_POOL)); + } + if (isLoginPropertySet(CONNECTION_TIMEOUT)) { + env.put("com.sun.jndi.ldap.connect.timeout", getLDAPPropertyValue(CONNECTION_TIMEOUT)); + } // handle LDAP referrals // valid values are "throw", "ignore" and "follow" diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java index 97be299d6bf..d28bd4c1f8c 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java @@ -36,6 +36,10 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler; import org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule; @@ -134,6 +138,79 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback assertTrue("no sessions after logout", waitForSessions(0)); } + @Test + public void testLoginPooled() throws LoginException { + + LoginContext context = new LoginContext("LDAPLoginPooled", new CallbackHandler() { + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + context.login(); + context.logout(); + + // again + + context.login(); + context.logout(); + + // new context + context = new LoginContext("LDAPLoginPooled", new CallbackHandler() { + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + context.login(); + context.logout(); + + Executor pool = Executors.newCachedThreadPool(); + for (int i = 0; i < 10; i++) { + ((ExecutorService) pool).execute(new Runnable() { + @Override + public void run() { + try { + LoginContext context = new LoginContext("LDAPLoginPooled", new CallbackHandler() { + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + context.login(); + context.logout(); + } catch (Exception ignored) { + } + } + }); + } + assertTrue("no sessions after logout", waitForSessions(10)); + } + private boolean waitForSessions(int expected) { final long expiry = System.currentTimeMillis() + 5000; int numSession = ldapServer.getLdapSessionManager().getSessions().length; diff --git a/artemis-server/src/test/resources/login.config b/artemis-server/src/test/resources/login.config index 8e531caf06d..26791d9d0b5 100644 --- a/artemis-server/src/test/resources/login.config +++ b/artemis-server/src/test/resources/login.config @@ -49,6 +49,27 @@ LDAPLogin { ; }; +LDAPLoginPooled { + org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required + debug=true + initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory + connectionURL="ldap://localhost:1024" + connectionUsername="uid=admin,ou=system" + connectionPassword=secret + connectionProtocol=s + authentication=simple + userBase="ou=system" + userSearchMatching="(uid={0})" + userSearchSubtree=false + roleBase="ou=system" + roleName=cn + roleSearchMatching="(member=uid={1},ou=system)" + roleSearchSubtree=false + connectionPool=true + connectionTimeout="2000" + ; +}; + UnAuthenticatedLDAPLogin { org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required debug=true diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md index dfd1a41b2b0..47fb22897c3 100644 --- a/docs/user-manual/en/security.md +++ b/docs/user-manual/en/security.md @@ -587,6 +587,13 @@ system. It is implemented by for the connection to the directory server. This option must be set explicitly to an empty string, because it has no default value. +- `connectionPool`. boolean, enable the ldap connection pool property + 'com.sun.jndi.ldap.connect.pool'. Note that the pool is [configured at the jvm level with system properties](https://docs.oracle.com/javase/jndi/tutorial/ldap/connect/config.html). + + +- `connectionTimeout`. String milliseconds, that can time limit a ldap connection + attempt. The default is infinite. + - `userBase` - selects a particular subtree of the DIT to search for user entries. The subtree is specified by a DN, which specifes the base node of the subtree. For example, by setting this option to From e2bae7856d414a6c8012eee20197451f378fd389 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 6 Jul 2018 10:14:48 -0500 Subject: [PATCH 069/207] ARTEMIS-1941 fix failing tests --- .../protocol/amqp/converter/CoreAmqpConverter.java | 9 ++++++--- .../message/JMSMappingOutboundTransformerTest.java | 10 +++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java index eb20219641e..b287d0c137a 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java @@ -451,9 +451,12 @@ private static Section convertBody(ServerJMSMessage message, Map // this will represent a readOnly buffer for the message ActiveMQBuffer buffer = internalMessage.getDataBuffer(); try { - Object s = buffer.readNullableSimpleString(); - if (s != null) { - body = new AmqpValue(s.toString()); + // the buffer may be completely empty (e.g. if the original AMQP message had a null body) + if (buffer.readableBytes() > 0) { + Object s = buffer.readNullableSimpleString(); + if (s != null) { + body = new AmqpValue(s.toString()); + } } } catch (Throwable ignored) { logger.debug("Exception ignored during conversion", ignored.getMessage(), ignored); diff --git a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/message/JMSMappingOutboundTransformerTest.java b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/message/JMSMappingOutboundTransformerTest.java index f1cd588cb01..565f67b1472 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/message/JMSMappingOutboundTransformerTest.java +++ b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/message/JMSMappingOutboundTransformerTest.java @@ -341,11 +341,11 @@ public void testConvertObjectMessageToAmqpMessageWithAmqpValueBody() throws Exce Message amqp = AMQPConverter.getInstance().fromCore(outbound.getInnerMessage()).getProtonMessage(); assertNotNull(amqp.getBody()); - assertTrue(amqp.getBody() instanceof Data); - assertTrue(((Data) amqp.getBody()).getValue() instanceof Binary); - assertFalse(0 == ((Binary) ((Data) amqp.getBody()).getValue()).getLength()); + assertTrue(amqp.getBody() instanceof AmqpValue); + assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Binary); + assertFalse(0 == ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength()); - Object value = deserialize((((Data) amqp.getBody()).getValue()).getArray()); + Object value = deserialize(((Binary) ((AmqpValue) amqp.getBody()).getValue()).getArray()); assertNotNull(value); assertTrue(value instanceof UUID); } @@ -361,7 +361,7 @@ public void testConvertEmptyObjectMessageToAmqpMessageWithAmqpValueBody() throws assertNotNull(amqp.getBody()); assertTrue(amqp.getBody() instanceof AmqpValue); assertTrue(((AmqpValue) amqp.getBody()).getValue() instanceof Binary); - assertEquals(0, ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength()); + assertEquals(5, ((Binary) ((AmqpValue) amqp.getBody()).getValue()).getLength()); } // ----- TextMessage type tests -------------------------------------------// From 048f46bd4f4b24109dd186f585730f6b14d05187 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 10 Jul 2018 21:59:30 -0400 Subject: [PATCH 070/207] ARTEMIS-1866 Fixing QuorumResultWaitTest --- .../ha/ReplicatedPolicyConfiguration.java | 19 +++++++++++++------ .../failover/QuorumResultWaitTest.java | 19 ++++++++++++------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java index e27bb4009fb..de1de4f17ea 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java @@ -79,24 +79,27 @@ public long getInitialReplicationSyncTimeout() { return initialReplicationSyncTimeout; } - public void setInitialReplicationSyncTimeout(long initialReplicationSyncTimeout) { + public ReplicatedPolicyConfiguration setInitialReplicationSyncTimeout(long initialReplicationSyncTimeout) { this.initialReplicationSyncTimeout = initialReplicationSyncTimeout; + return this; } public boolean getVoteOnReplicationFailure() { return voteOnReplicationFailure; } - public void setVoteOnReplicationFailure(boolean voteOnReplicationFailure) { + public ReplicatedPolicyConfiguration setVoteOnReplicationFailure(boolean voteOnReplicationFailure) { this.voteOnReplicationFailure = voteOnReplicationFailure; + return this; } public int getQuorumSize() { return quorumSize; } - public void setQuorumSize(int quorumSize) { + public ReplicatedPolicyConfiguration setQuorumSize(int quorumSize) { this.quorumSize = quorumSize; + return this; } @@ -104,12 +107,14 @@ public int getVoteRetries() { return voteRetries; } - public void setVoteRetries(int voteRetries) { + public ReplicatedPolicyConfiguration setVoteRetries(int voteRetries) { this.voteRetries = voteRetries; + return this; } - public void setVoteRetryWait(long voteRetryWait) { + public ReplicatedPolicyConfiguration setVoteRetryWait(long voteRetryWait) { this.voteRetryWait = voteRetryWait; + return this; } public long getVoteRetryWait() { @@ -120,7 +125,9 @@ public int getQuorumVoteWait() { return quorumVoteWait; } - public void setQuorumVoteWait(int quorumVoteWait) { + public ReplicatedPolicyConfiguration setQuorumVoteWait(int quorumVoteWait) { + new Exception("here").printStackTrace(); this.quorumVoteWait = quorumVoteWait; + return this; } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java index f481dbc0dcd..db3aed383f7 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/QuorumResultWaitTest.java @@ -21,6 +21,7 @@ import org.apache.activemq.artemis.core.config.ha.ReplicatedPolicyConfiguration; import org.apache.activemq.artemis.core.server.cluster.ha.ReplicatedPolicy; import org.junit.Test; + public class QuorumResultWaitTest extends StaticClusterWithBackupFailoverTest { public static final int QUORUM_VOTE_WAIT_CONFIGURED_TIME_SEC = 12; @@ -33,7 +34,7 @@ protected void setupServers() throws Exception { ((ReplicatedPolicyConfiguration) servers[2].getConfiguration().getHAPolicyConfiguration()).setGroupName("group2"); ((ReplicaPolicyConfiguration) servers[4].getConfiguration().getHAPolicyConfiguration()).setGroupName("group1"); ((ReplicaPolicyConfiguration) servers[5].getConfiguration().getHAPolicyConfiguration()).setGroupName("group2"); - ReplicatedPolicyConfiguration replicatedPolicyConf = new ReplicatedPolicyConfiguration(); + ReplicatedPolicyConfiguration replicatedPolicyConf = new ReplicatedPolicyConfiguration().setQuorumVoteWait(QUORUM_VOTE_WAIT_CONFIGURED_TIME_SEC); replicatedPolicyConf.setGroupName("group0"); replicatedPolicyConf.setVoteRetries(5); replicatedPolicyConf.setVoteRetryWait(100); @@ -43,12 +44,16 @@ protected void setupServers() throws Exception { @Test public void testQuorumVotingResultWait() throws Exception { setupCluster(); - startServers(0, 1, 2); - startServers(3, 4, 5); - //Assert if the default time 30 sec is used - assertEquals(ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(), ((ReplicatedPolicy)(servers[0].getHAPolicy())).getQuorumVoteWait()); - //Assert if the configured time is used. - assertEquals(QUORUM_VOTE_WAIT_CONFIGURED_TIME_SEC, ((ReplicatedPolicy)(servers[3].getHAPolicy())).getQuorumVoteWait()); + try { + startServers(0, 1, 2); + startServers(3, 4, 5); + //Assert if the default time 30 sec is used + assertEquals(ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(), ((ReplicatedPolicy) (servers[0].getHAPolicy())).getQuorumVoteWait()); + //Assert if the configured time is used. + assertEquals(QUORUM_VOTE_WAIT_CONFIGURED_TIME_SEC, ((ReplicatedPolicy) (servers[3].getHAPolicy())).getQuorumVoteWait()); + } finally { + stopServers(0, 1, 2, 3, 4, 5); + } } @Override From 7c0f6633f1e6f4fbcfc754dd2be723e8eefbdca6 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 10 Jul 2018 22:07:36 -0400 Subject: [PATCH 071/207] ARTEMIS-1959 Fixing JournalDataPrintTest --- .../impl/journal/DescribeJournal.java | 19 +++++++++++-------- .../journal/JournalDataPrintTest.java | 14 +++++++++++++- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java index 1842a5848ed..d05e23d4f4b 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java @@ -105,12 +105,12 @@ public final class DescribeJournal { private final List records; private final List preparedTransactions; - private static final Configuration CONFIGURATION; - static { + private static Configuration getConfiguration() { + Configuration configuration; String instanceFolder = System.getProperty("artemis.instance"); if (instanceFolder != null) { - CONFIGURATION = new FileConfiguration(); + configuration = new FileConfiguration(); File configFile = new File(instanceFolder + "/etc/broker.xml"); URL url; @@ -121,19 +121,21 @@ public final class DescribeJournal { xml = XMLUtil.replaceSystemProps(xml); Element e = XMLUtil.stringToElement(xml); - String root = ((FileConfiguration) CONFIGURATION).getRootElement(); + String root = ((FileConfiguration) configuration).getRootElement(); NodeList children = e.getElementsByTagName(root); if (root != null && children.getLength() > 0) { Node item = children.item(0); - XMLUtil.validate(item, ((FileConfiguration) CONFIGURATION).getSchema()); - ((FileConfiguration) CONFIGURATION).parse((Element) item, url); + XMLUtil.validate(item, ((FileConfiguration) configuration).getSchema()); + ((FileConfiguration) configuration).parse((Element) item, url); } } catch (Exception e) { logger.error("failed to load broker.xml", e); } } else { - CONFIGURATION = new ConfigurationImpl(); + configuration = new ConfigurationImpl(); } + + return configuration; } public DescribeJournal(List records, List preparedTransactions) { @@ -166,10 +168,11 @@ public static DescribeJournal describeMessagesJournal(final File messagesDir) th } public static DescribeJournal describeMessagesJournal(final File messagesDir, PrintStream out, boolean safe) throws Exception { + Configuration configuration = getConfiguration(); SequentialFileFactory messagesFF = new NIOSequentialFileFactory(messagesDir, null, 1); // Will use only default values. The load function should adapt to anything different - JournalImpl messagesJournal = new JournalImpl(CONFIGURATION.getJournalFileSize(), CONFIGURATION.getJournalMinFiles(), CONFIGURATION.getJournalPoolFiles(), 0, 0, messagesFF, "activemq-data", "amq", 1); + JournalImpl messagesJournal = new JournalImpl(configuration.getJournalFileSize(), configuration.getJournalMinFiles(), configuration.getJournalPoolFiles(), 0, 0, messagesFF, "activemq-data", "amq", 1); return describeJournal(messagesFF, messagesJournal, messagesDir, out, safe); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/journal/JournalDataPrintTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/journal/JournalDataPrintTest.java index 8e8b0d3b066..dd8fe38ca52 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/journal/JournalDataPrintTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/journal/JournalDataPrintTest.java @@ -21,6 +21,7 @@ import org.apache.activemq.artemis.core.config.FileDeploymentManager; import org.apache.activemq.artemis.core.config.impl.FileConfiguration; import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration; +import org.apache.activemq.artemis.core.persistence.impl.journal.DescribeJournal; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; import org.apache.activemq.artemis.jms.server.config.impl.FileJMSConfiguration; @@ -32,6 +33,9 @@ import org.junit.Test; import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; public class JournalDataPrintTest extends ActiveMQTestBase { @@ -49,9 +53,17 @@ public void testJournalDataPrint() throws Exception { try { server.start(); server.stop(); + // This will force some static load that used to be on the class, which would make this test to fail. + DescribeJournal journal = new DescribeJournal(null, null); System.setProperty("artemis.instance", this.getClass().getClassLoader().getResource("dataprint").getFile()); - PrintData.printData(server.getConfiguration().getBindingsLocation().getAbsoluteFile(), server.getConfiguration().getJournalLocation().getAbsoluteFile(), server.getConfiguration().getPagingLocation().getAbsoluteFile()); + PrintData.printData(server.getConfiguration().getBindingsLocation().getAbsoluteFile(), server.getConfiguration().getJournalLocation().getAbsoluteFile(), server.getConfiguration().getPagingLocation().getAbsoluteFile(), + new PrintStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + // dev/null + } + }), false); // list journal file File dirFile = server.getConfiguration().getJournalLocation().getAbsoluteFile(); From 79ff3432d599ec4cd10f6613bdb2beb428aac960 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Thu, 12 Jul 2018 14:48:39 +0200 Subject: [PATCH 072/207] ARTEMIS-1979 Table names with DB2 should be upper-cases DB2 JDBC driver fail to retrieve metadata information if table names are lower-cases: similarly to Oracle, better force any table name to be upper-cases to avoid broker being unable to restart when lower-cases table names are used --- artemis-jdbc-store/src/main/resources/journal-sql.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artemis-jdbc-store/src/main/resources/journal-sql.properties b/artemis-jdbc-store/src/main/resources/journal-sql.properties index 86e6a3d7103..3d76d5dd183 100644 --- a/artemis-jdbc-store/src/main/resources/journal-sql.properties +++ b/artemis-jdbc-store/src/main/resources/journal-sql.properties @@ -88,4 +88,5 @@ table-names-case.oracle=upper # DB2 SQL statements max-blob-size.db2=2147483647 create-file-table.db2=CREATE TABLE %s (ID BIGINT GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), FILENAME VARCHAR(255), EXTENSION VARCHAR(10), DATA BLOB(2G), PRIMARY KEY(ID)) -append-to-file.db2=UPDATE %s SET DATA = (DATA || ?) WHERE ID=? \ No newline at end of file +append-to-file.db2=UPDATE %s SET DATA = (DATA || ?) WHERE ID=? +table-names-case.db2=upper \ No newline at end of file From 9a52766e51da1c1499cafe1b50caed3f03b5ab55 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 11 Jul 2018 14:34:34 -0400 Subject: [PATCH 073/207] NO-JIRA Removing System.err style debug left by accident This is non critical. The message is only used by Queue.pause ATM. --- .../activemq/artemis/core/server/impl/QueueImpl.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java index fdb0ddd84bb..bc5c0c98fe9 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java @@ -248,8 +248,6 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { private final ReusableLatch deliveriesInTransit = new ReusableLatch(0); - private volatile boolean caused = false; - private final AtomicLong queueRateCheckTime = new AtomicLong(System.currentTimeMillis()); private final AtomicLong messagesAddedSnapshot = new AtomicLong(0); @@ -766,11 +764,6 @@ protected boolean scheduleIfPossible(MessageReference ref) { */ private boolean flushDeliveriesInTransit() { try { - - if (!deliveriesInTransit.await(100, TimeUnit.MILLISECONDS)) { - caused = true; - System.err.println("There are currently " + deliveriesInTransit.getCount() + " credits"); - } if (deliveriesInTransit.await(DELIVERY_TIMEOUT)) { return true; } else { From a1d34f56311f5cc519511eb99e01313ba3f36c35 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 11 Jul 2018 14:34:13 -0400 Subject: [PATCH 074/207] NO-JIRA Adding missing check on ClientCrashTest --- .../tests/integration/clientcrash/ClientCrashTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/clientcrash/ClientCrashTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/clientcrash/ClientCrashTest.java index e9f282e75fd..04377b13492 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/clientcrash/ClientCrashTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/clientcrash/ClientCrashTest.java @@ -29,10 +29,12 @@ import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.jms.client.ActiveMQTextMessage; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; +import org.apache.activemq.artemis.tests.util.SpawnedVMCheck; import org.apache.activemq.artemis.tests.util.SpawnedVMSupport; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; /** @@ -41,6 +43,9 @@ */ public class ClientCrashTest extends ClientTestBase { + @Rule + public SpawnedVMCheck spawnedVMCheck = new SpawnedVMCheck(); + // using short values so this test can run fast static final int PING_PERIOD = 100; From be8c29d4e1b81665aec56b82eee914683eb93bfa Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 11 Jul 2018 16:25:22 -0400 Subject: [PATCH 075/207] NO-JIRA Improve SpawnVMCheck --- .../artemis/tests/util/SpawnedVMCheck.java | 6 ++++ .../artemis/tests/util/SpawnedVMSupport.java | 34 ++++++++++++------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/util/SpawnedVMCheck.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/util/SpawnedVMCheck.java index cc1c0430d30..c4832ab36fd 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/util/SpawnedVMCheck.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/util/SpawnedVMCheck.java @@ -26,4 +26,10 @@ protected void after() { super.after(); SpawnedVMSupport.checkProcess(); } + + @Override + public void before() throws Throwable { + super.before(); + SpawnedVMSupport.enableCheck(); + } } diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/util/SpawnedVMSupport.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/util/SpawnedVMSupport.java index 544b04ffeba..b8e86e965d5 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/util/SpawnedVMSupport.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/util/SpawnedVMSupport.java @@ -40,7 +40,7 @@ public final class SpawnedVMSupport { - static ConcurrentHashMap startedProcesses = new ConcurrentHashMap(); + static ConcurrentHashMap startedProcesses = null; private static final UnitTestLogger log = UnitTestLogger.LOGGER; @@ -203,7 +203,9 @@ public static Process spawnVM(String classPath, ProcessLogger errorLogger = new ProcessLogger(logErrorOutput, process.getErrorStream(), className, wordMatch, wordRunning); errorLogger.start(); - startedProcesses.put(process, className); + if (startedProcesses != null) { + startedProcesses.put(process, className); + } return process; } @@ -216,18 +218,20 @@ private static HashSet getAliveProcesses() { HashSet aliveProcess = new HashSet<>(); - for (;;) { - try { - aliveProcess.clear(); - for (Process process : startedProcesses.keySet()) { - if (process.isAlive()) { - aliveProcess.add(process); - process.destroyForcibly(); + if (startedProcesses != null) { + for (;;) { + try { + aliveProcess.clear(); + for (Process process : startedProcesses.keySet()) { + if (process.isAlive()) { + aliveProcess.add(process); + process.destroyForcibly(); + } } + break; + } catch (Throwable e) { + e.printStackTrace(); } - break; - } catch (Throwable e) { - e.printStackTrace(); } } @@ -251,6 +255,10 @@ public static void forceKill() { } + public static void enableCheck() { + startedProcesses = new ConcurrentHashMap<>(); + } + public static void checkProcess() { HashSet aliveProcess = getAliveProcesses(); @@ -265,7 +273,7 @@ public static void checkProcess() { Assert.fail("There are " + aliveProcess.size() + " processes alive :: " + buffer.toString()); } } finally { - startedProcesses.clear(); + startedProcesses = null; } } From bdcef5a68f2fc8b341fca0f8446d1d2ffcba8165 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Thu, 12 Jul 2018 15:24:32 +0200 Subject: [PATCH 076/207] ARTEMIS-1981 JDBCJournalImpl constructor isn't using tableName parameter Removed the unused/redundant constructor parameter tableName --- .../activemq/artemis/jdbc/store/journal/JDBCJournalImpl.java | 1 - .../persistence/impl/journal/JDBCJournalStorageManager.java | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/journal/JDBCJournalImpl.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/journal/JDBCJournalImpl.java index e7b45ffb488..f997b3cf3d1 100644 --- a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/journal/JDBCJournalImpl.java +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/journal/JDBCJournalImpl.java @@ -94,7 +94,6 @@ public class JDBCJournalImpl extends AbstractJDBCDriver implements Journal { public JDBCJournalImpl(DataSource dataSource, SQLProvider provider, - String tableName, ScheduledExecutorService scheduledExecutorService, Executor completeExecutor, IOCriticalErrorListener criticalIOErrorListener, diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JDBCJournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JDBCJournalStorageManager.java index e2792027a84..3c68f98ae8c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JDBCJournalStorageManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JDBCJournalStorageManager.java @@ -67,8 +67,8 @@ protected synchronized void init(Configuration config, IOCriticalErrorListener c if (sqlProviderFactory == null) { sqlProviderFactory = new PropertySQLProvider.Factory(dbConf.getDataSource()); } - bindingsJournal = new JDBCJournalImpl(dbConf.getDataSource(), sqlProviderFactory.create(dbConf.getBindingsTableName(), SQLProvider.DatabaseStoreType.BINDINGS_JOURNAL), dbConf.getBindingsTableName(), scheduledExecutorService, executorFactory.getExecutor(), criticalErrorListener,dbConf.getJdbcJournalSyncPeriodMillis()); - messageJournal = new JDBCJournalImpl(dbConf.getDataSource(), sqlProviderFactory.create(dbConf.getMessageTableName(), SQLProvider.DatabaseStoreType.MESSAGE_JOURNAL), dbConf.getMessageTableName(), scheduledExecutorService, executorFactory.getExecutor(), criticalErrorListener, dbConf.getJdbcJournalSyncPeriodMillis()); + bindingsJournal = new JDBCJournalImpl(dbConf.getDataSource(), sqlProviderFactory.create(dbConf.getBindingsTableName(), SQLProvider.DatabaseStoreType.BINDINGS_JOURNAL), scheduledExecutorService, executorFactory.getExecutor(), criticalErrorListener,dbConf.getJdbcJournalSyncPeriodMillis()); + messageJournal = new JDBCJournalImpl(dbConf.getDataSource(), sqlProviderFactory.create(dbConf.getMessageTableName(), SQLProvider.DatabaseStoreType.MESSAGE_JOURNAL), scheduledExecutorService, executorFactory.getExecutor(), criticalErrorListener, dbConf.getJdbcJournalSyncPeriodMillis()); largeMessagesFactory = new JDBCSequentialFileFactory(dbConf.getDataSource(), sqlProviderFactory.create(dbConf.getLargeMessageTableName(), SQLProvider.DatabaseStoreType.LARGE_MESSAGE), executorFactory.getExecutor(), criticalErrorListener); } else { String driverClassName = dbConf.getJdbcDriverClassName(); From 7b4be5008dfaca122d5a277b4010807a176a2992 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Mon, 9 Jul 2018 09:36:33 -0500 Subject: [PATCH 077/207] ARTEMIS-1974 document LDAP role expansion --- .../spi/core/security/jaas/LDAPLoginModule.java | 6 ++++++ docs/user-manual/en/security.md | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java index 19194fab172..7d58a0b97be 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java @@ -480,6 +480,12 @@ protected void addRoles(DirContext context, while (!pendingNameExpansion.isEmpty()) { String name = pendingNameExpansion.remove(); final String expandFilter = expandRolesMatchingFormat.format(new String[]{name}); + if (logger.isDebugEnabled()) { + logger.debug("Get 'expanded' user roles."); + logger.debug("Looking for the 'expanded' user roles in LDAP with "); + logger.debug(" base DN: " + getLDAPPropertyValue(ROLE_BASE)); + logger.debug(" filter: " + expandFilter); + } try { results = Subject.doAs(brokerGssapiIdentity, (PrivilegedExceptionAction< NamingEnumeration>) () -> context.search(getLDAPPropertyValue(ROLE_BASE), expandFilter, constraints)); } catch (PrivilegedActionException e) { diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md index 47fb22897c3..63e49a6c0a3 100644 --- a/docs/user-manual/en/security.md +++ b/docs/user-manual/en/security.md @@ -690,6 +690,19 @@ system. It is implemented by - `referral` - specify how to handle referrals; valid values: `ignore`, `follow`, `throw`; default is `ignore`. +- `expandRoles` - boolean indicating whether to enable the role expansion + functionality or not; default false. If enabled, then roles within roles will + be found. For example, role `A` is in role `B`. User `X` is in role `A`, + which means user `X` is in role `B` by virtue of being in role `A`. + +- `expandRolesMatching` - specifies an LDAP search filter which is applied to + the subtree selected by `roleBase`. Before passing to the LDAP search operation, + the string value you provide here is subjected to string substitution, as + implemented by the `java.text.MessageFormat` class. Essentially, this means that + the special string, `{0}`, is substituted by the role name as extracted from the + previous role search. This option must always be set to enable role expansion + because it has no default value. Example value: `(member={0})`. + - `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it should be set to `false`, or omitted; default is `false` From 9f8288c0156072b3ae02efd04e1adbcd0abca2c2 Mon Sep 17 00:00:00 2001 From: yang wei Date: Thu, 5 Jul 2018 17:48:11 +0800 Subject: [PATCH 078/207] ARTEMIS-1966 Replication channel closed but not connection if flow controlled during replication --- .../core/replication/ReplicationManager.java | 1 + ...aredNothingReplicationFlowControlTest.java | 295 ++++++++++++++++++ 2 files changed, 296 insertions(+) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationFlowControlTest.java diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java index be5963a3739..fbf7c6c539b 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java @@ -302,6 +302,7 @@ public void stop() throws Exception { RemotingConnection toStop = remotingConnection; if (toStop != null) { toStop.removeFailureListener(failureListener); + toStop.destroy(); } remotingConnection = null; started = false; diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationFlowControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationFlowControlTest.java new file mode 100644 index 00000000000..381b617ac74 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationFlowControlTest.java @@ -0,0 +1,295 @@ +/** + * 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.artemis.tests.integration.replication; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.Interceptor; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientProducer; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; +import org.apache.activemq.artemis.api.core.client.ServerLocator; +import org.apache.activemq.artemis.core.client.impl.ServerLocatorImpl; +import org.apache.activemq.artemis.core.config.ClusterConnectionConfiguration; +import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.core.config.ha.ReplicaPolicyConfiguration; +import org.apache.activemq.artemis.core.config.ha.ReplicatedPolicyConfiguration; +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.io.SequentialFileFactory; +import org.apache.activemq.artemis.core.io.mapped.MappedSequentialFileFactory; +import org.apache.activemq.artemis.core.journal.LoaderCallback; +import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo; +import org.apache.activemq.artemis.core.journal.RecordInfo; +import org.apache.activemq.artemis.core.journal.impl.JournalImpl; +import org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds; +import org.apache.activemq.artemis.core.protocol.core.Packet; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.core.server.ActiveMQServers; +import org.apache.activemq.artemis.core.server.JournalType; +import org.apache.activemq.artemis.junit.Wait; +import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; +import org.jboss.logging.Logger; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class SharedNothingReplicationFlowControlTest { + + private static final Logger logger = Logger.getLogger(SharedNothingReplicationFlowControlTest.class); + + @Rule + public TemporaryFolder brokersFolder = new TemporaryFolder(); + + @Test + public void testReplicationIfFlowControlled() throws Exception { + // start live + Configuration liveConfiguration = createLiveConfiguration(); + ActiveMQServer liveServer = ActiveMQServers.newActiveMQServer(liveConfiguration); + liveServer.start(); + + Wait.waitFor(() -> liveServer.isStarted()); + + ServerLocator locator = ServerLocatorImpl.newLocator("tcp://localhost:61616"); + locator.setCallTimeout(60_000L); + locator.setConnectionTTL(60_000L); + + final ClientSessionFactory csf = locator.createSessionFactory(); + ClientSession sess = csf.createSession(); + sess.createQueue("flowcontrol", RoutingType.ANYCAST, "flowcontrol", true); + sess.close(); + Executor sendMessageExecutor = Executors.newCachedThreadPool(); + + int i = 0; + final int j = 100; + final CountDownLatch allMessageSent = new CountDownLatch(j); + + // start backup + Configuration backupConfiguration = createBackupConfiguration(); + ActiveMQServer backupServer = ActiveMQServers.newActiveMQServer(backupConfiguration); + backupServer.start(); + + Wait.waitFor(() -> backupServer.isStarted()); + + Wait.waitFor(backupServer::isReplicaSync, 30000); + + TestInterceptor interceptor = new TestInterceptor(30000); + // Increase latency of handling packet on backup side and flow control would work + backupServer.getClusterManager().getClusterController().addIncomingInterceptorForReplication(interceptor); + + byte[] body = new byte[32 * 1024]; + while (i < j) { + sendMessageExecutor.execute(() -> { + try { + ClientSession session = csf.createSession(true, true); + ClientProducer producer = session.createProducer("flowcontrol"); + ClientMessage message = session.createMessage(true); + message.writeBodyBufferBytes(body); + logger.infof("try to send a message after replicated"); + producer.send(message); + logger.info("send message done"); + producer.close(); + session.close(); + + allMessageSent.countDown(); + } catch (ActiveMQException e) { + logger.error("send message", e); + } + }); + i++; + } + + Assert.assertTrue("all message sent", allMessageSent.await(30, TimeUnit.SECONDS)); + interceptor.setSleepTime(0); + + csf.close(); + locator.close(); + Assert.assertTrue("Waiting for replica sync timeout", Wait.waitFor(liveServer::isReplicaSync, 30000)); + backupServer.stop(true); + liveServer.stop(true); + + SequentialFileFactory fileFactory; + + File liveJournalDir = brokersFolder.getRoot().toPath().resolve("live").resolve("data").resolve("journal").toFile(); + fileFactory = new MappedSequentialFileFactory(liveConfiguration.getJournalLocation(), liveConfiguration.getJournalFileSize(), false, liveConfiguration.getJournalBufferSize_NIO(), liveConfiguration.getJournalBufferTimeout_NIO(), null); + + JournalImpl liveMessageJournal = new JournalImpl(liveConfiguration.getJournalFileSize(), liveConfiguration.getJournalMinFiles(), liveConfiguration.getJournalPoolFiles(), liveConfiguration.getJournalCompactMinFiles(), liveConfiguration.getJournalCompactPercentage(), fileFactory, "activemq-data", "amq", fileFactory.getMaxIO()); + + liveMessageJournal.start(); + final AtomicInteger liveJournalCounter = new AtomicInteger(); + liveMessageJournal.load(new SharedNothingReplicationFlowControlTest.AddRecordLoaderCallback() { + @Override + public void addRecord(RecordInfo info) { + if (!(info.userRecordType == JournalRecordIds.ADD_MESSAGE_PROTOCOL)) { + // ignore + } + logger.infof("got live message %d %d", info.id, info.userRecordType); + liveJournalCounter.incrementAndGet(); + } + }); + + // read backup's journal + File backupJournalDir = brokersFolder.getRoot().toPath().resolve("backup").resolve("data").resolve("journal").toFile(); + fileFactory = new MappedSequentialFileFactory(backupConfiguration.getJournalLocation(), backupConfiguration.getJournalFileSize(), false, backupConfiguration.getJournalBufferSize_NIO(), backupConfiguration.getJournalBufferTimeout_NIO(), null); + + JournalImpl backupMessageJournal = new JournalImpl(backupConfiguration.getJournalFileSize(), backupConfiguration.getJournalMinFiles(), backupConfiguration.getJournalPoolFiles(), backupConfiguration.getJournalCompactMinFiles(), backupConfiguration.getJournalCompactPercentage(), fileFactory, "activemq-data", "amq", fileFactory.getMaxIO()); + + backupMessageJournal.start(); + + final AtomicInteger replicationCounter = new AtomicInteger(); + backupMessageJournal.load(new SharedNothingReplicationFlowControlTest.AddRecordLoaderCallback() { + @Override + public void addRecord(RecordInfo info) { + if (!(info.userRecordType == JournalRecordIds.ADD_MESSAGE_PROTOCOL)) { + // ignore + } + logger.infof("replicated message %d", info.id); + replicationCounter.incrementAndGet(); + } + }); + + logger.infof("expected %d messages, live=%d, backup=%d", j, liveJournalCounter.get(), replicationCounter.get()); + Assert.assertEquals("Live lost journal record", j, liveJournalCounter.get()); + Assert.assertEquals("Backup did not replicated all journal", j, replicationCounter.get()); + } + + // Set a small call timeout and write buffer high water mark value to trigger replication flow control + private Configuration createLiveConfiguration() throws Exception { + Configuration conf = new ConfigurationImpl(); + conf.setName("localhost::live"); + + File liveDir = brokersFolder.newFolder("live"); + conf.setBrokerInstance(liveDir); + + conf.addAcceptorConfiguration("live", "tcp://localhost:61616?writeBufferHighWaterMark=2048&writeBufferLowWaterMark=2048"); + conf.addConnectorConfiguration("backup", "tcp://localhost:61617"); + conf.addConnectorConfiguration("live", "tcp://localhost:61616"); + + conf.setClusterUser("mycluster"); + conf.setClusterPassword("mypassword"); + + ReplicatedPolicyConfiguration haPolicy = new ReplicatedPolicyConfiguration(); + haPolicy.setVoteOnReplicationFailure(false); + haPolicy.setCheckForLiveServer(false); + conf.setHAPolicyConfiguration(haPolicy); + + ClusterConnectionConfiguration ccconf = new ClusterConnectionConfiguration(); + ccconf.setStaticConnectors(new ArrayList<>()).getStaticConnectors().add("backup"); + ccconf.setName("cluster"); + ccconf.setConnectorName("live"); + ccconf.setCallTimeout(4000); + conf.addClusterConfiguration(ccconf); + + conf.setSecurityEnabled(false).setJMXManagementEnabled(false).setJournalType(JournalType.MAPPED).setJournalFileSize(1024 * 512).setConnectionTTLOverride(60_000L); + + return conf; + } + + private Configuration createBackupConfiguration() throws Exception { + Configuration conf = new ConfigurationImpl(); + conf.setName("localhost::backup"); + + File backupDir = brokersFolder.newFolder("backup"); + conf.setBrokerInstance(backupDir); + + ReplicaPolicyConfiguration haPolicy = new ReplicaPolicyConfiguration(); + haPolicy.setClusterName("cluster"); + conf.setHAPolicyConfiguration(haPolicy); + + conf.addAcceptorConfiguration("backup", "tcp://localhost:61617"); + conf.addConnectorConfiguration("live", "tcp://localhost:61616"); + conf.addConnectorConfiguration("backup", "tcp://localhost:61617"); + + conf.setClusterUser("mycluster"); + conf.setClusterPassword("mypassword"); + + ClusterConnectionConfiguration ccconf = new ClusterConnectionConfiguration(); + ccconf.setStaticConnectors(new ArrayList<>()).getStaticConnectors().add("live"); + ccconf.setName("cluster"); + ccconf.setConnectorName("backup"); + conf.addClusterConfiguration(ccconf); + + /** + * Set a bad url then, as a result the backup node would make a decision + * of replicating from live node in the case of connection failure. + * Set big check period to not schedule checking which would stop server. + */ + conf.setNetworkCheckPeriod(1000000).setNetworkCheckURLList("http://localhost:28787").setSecurityEnabled(false).setJMXManagementEnabled(false).setJournalType(JournalType.MAPPED).setJournalFileSize(1024 * 512).setConnectionTTLOverride(60_000L); + + return conf; + } + + abstract class AddRecordLoaderCallback implements LoaderCallback { + + @Override + public void addPreparedTransaction(PreparedTransactionInfo preparedTransaction) { + + } + + @Override + public void deleteRecord(long id) { + + } + + @Override + public void updateRecord(RecordInfo info) { + + } + + @Override + public void failedTransaction(long transactionID, List records, List recordsToDelete) { + + } + } + + public static final class TestInterceptor implements Interceptor { + + private long sleepTime; + + public TestInterceptor(long sleepTime) { + this.sleepTime = sleepTime; + } + + public void setSleepTime(long sleepTime) { + this.sleepTime = sleepTime; + } + + @Override + public boolean intercept(Packet packet, RemotingConnection connection) throws ActiveMQException { + try { + long startTime = System.currentTimeMillis(); + while (System.currentTimeMillis() < startTime + sleepTime) { + Thread.sleep(100); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + return true; + } + } +} From 2a30b291be94b0283172b15fa573b86769a32070 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Thu, 12 Jul 2018 13:35:59 -0400 Subject: [PATCH 079/207] ARTEMIS-1966 Improving SharedNothingReplicationFlowControlTest Tests should always extend ActiveMQTestBase whenever is possible. This is because there are a few rules to avoid thread leakages. The test was also leaking an executor and I believe it was not always stopping the servers, which I fixed here. --- ...aredNothingReplicationFlowControlTest.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationFlowControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationFlowControlTest.java index 381b617ac74..923ce3a8fac 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationFlowControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/replication/SharedNothingReplicationFlowControlTest.java @@ -21,7 +21,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -53,13 +53,29 @@ import org.apache.activemq.artemis.core.server.JournalType; import org.apache.activemq.artemis.junit.Wait; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.jboss.logging.Logger; +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -public class SharedNothingReplicationFlowControlTest { +public class SharedNothingReplicationFlowControlTest extends ActiveMQTestBase { + + ExecutorService sendMessageExecutor; + + + @Before + public void setupExecutor() { + sendMessageExecutor = Executors.newCachedThreadPool(); + } + + @After + public void teardownExecutor() { + sendMessageExecutor.shutdownNow(); + } private static final Logger logger = Logger.getLogger(SharedNothingReplicationFlowControlTest.class); @@ -70,7 +86,7 @@ public class SharedNothingReplicationFlowControlTest { public void testReplicationIfFlowControlled() throws Exception { // start live Configuration liveConfiguration = createLiveConfiguration(); - ActiveMQServer liveServer = ActiveMQServers.newActiveMQServer(liveConfiguration); + ActiveMQServer liveServer = addServer(ActiveMQServers.newActiveMQServer(liveConfiguration)); liveServer.start(); Wait.waitFor(() -> liveServer.isStarted()); @@ -83,7 +99,7 @@ public void testReplicationIfFlowControlled() throws Exception { ClientSession sess = csf.createSession(); sess.createQueue("flowcontrol", RoutingType.ANYCAST, "flowcontrol", true); sess.close(); - Executor sendMessageExecutor = Executors.newCachedThreadPool(); + int i = 0; final int j = 100; @@ -91,7 +107,7 @@ public void testReplicationIfFlowControlled() throws Exception { // start backup Configuration backupConfiguration = createBackupConfiguration(); - ActiveMQServer backupServer = ActiveMQServers.newActiveMQServer(backupConfiguration); + ActiveMQServer backupServer = addServer(ActiveMQServers.newActiveMQServer(backupConfiguration)); backupServer.start(); Wait.waitFor(() -> backupServer.isStarted()); From 5ec2234010dfad8c7ff2e49f2505ea44dba9388a Mon Sep 17 00:00:00 2001 From: 17103355 <17103355@cnsuning.com> Date: Thu, 28 Jun 2018 20:14:50 +0800 Subject: [PATCH 080/207] ARTEMIS-1958 Artemis may not be able to delete pages when there are some empty page files --- .../cursor/impl/PageSubscriptionImpl.java | 21 +++++ .../tests/integration/paging/PagingTest.java | 93 +++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/cursor/impl/PageSubscriptionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/cursor/impl/PageSubscriptionImpl.java index e1c9537403c..5fec9a4f183 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/cursor/impl/PageSubscriptionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/cursor/impl/PageSubscriptionImpl.java @@ -383,19 +383,29 @@ private PagedReference internalGetNext(final PagePosition pos) { PageCache cache = cursorProvider.getPageCache(pos.getPageNr()); + PageCache emptyCache = null; if (cache != null && !cache.isLive() && retPos.getMessageNr() >= cache.getNumberOfMessages()) { + emptyCache = cache; + saveEmptyPageAsConsumedPage(emptyCache); // The next message is beyond what's available at the current page, so we need to move to the next page cache = null; } // it will scan for the next available page while ((cache == null && retPos.getPageNr() <= pageStore.getCurrentWritingPage()) || (cache != null && retPos.getPageNr() <= pageStore.getCurrentWritingPage() && cache.getNumberOfMessages() == 0)) { + emptyCache = cache; retPos = moveNextPage(retPos); cache = cursorProvider.getPageCache(retPos.getPageNr()); + + if (cache != null) { + saveEmptyPageAsConsumedPage(emptyCache); + } } if (cache == null) { + saveEmptyPageAsConsumedPage(emptyCache); + // it will be null in the case of the current writing page return null; } else { @@ -787,6 +797,17 @@ private PageCursorInfo getPageInfo(final long pageNr) { } + private void saveEmptyPageAsConsumedPage(final PageCache cache) { + if (cache != null && cache.getNumberOfMessages() == 0) { + synchronized (consumedPages) { + PageCursorInfo pageInfo = consumedPages.get(cache.getPageId()); + if (pageInfo == null) { + consumedPages.put(cache.getPageId(), new PageCursorInfo(cache.getPageId(), cache.getNumberOfMessages(), cache)); + } + } + } + } + // Package protected --------------------------------------------- // Protected ----------------------------------------------------- diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java index bc09fa15407..566142d5d46 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java @@ -5405,6 +5405,99 @@ public void testMultiFiltersRegularConsumer() throws Throwable { internalTestMultiFilters(false); } + @Test + public void testPageEmptyFile() throws Exception { + boolean persistentMessages = true; + + clearDataRecreateServerDirs(); + + Configuration config = createDefaultInVMConfig().setJournalSyncNonTransactional(false); + + server = createServer(true, config, PagingTest.PAGE_SIZE, PagingTest.PAGE_MAX); + + server.start(); + + final int messageSize = 1024; + + final int numberOfMessages = 100; + + try { + ServerLocator locator = createInVMNonHALocator().setClientFailureCheckPeriod(120000).setConnectionTTL(5000000).setCallTimeout(120000).setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setBlockOnAcknowledge(true); + + ClientSessionFactory sf = locator.createSessionFactory(); + + ClientSession session = sf.createSession(false, false, false); + + session.createQueue(PagingTest.ADDRESS, PagingTest.ADDRESS, null, true); + + PagingStore store = server.getPagingManager().getPageStore(ADDRESS); + store.forceAnotherPage(); + store.forceAnotherPage(); + + ClientProducer producer = session.createProducer(PagingTest.ADDRESS); + + ClientMessage message = null; + + byte[] body = new byte[messageSize]; + + for (int i = 0; i < numberOfMessages; i++) { + message = session.createMessage(persistentMessages); + + ActiveMQBuffer bodyLocal = message.getBodyBuffer(); + + bodyLocal.writeBytes(body); + + producer.send(message); + } + + session.commit(); + + Queue queue = server.locateQueue(PagingTest.ADDRESS); + Assert.assertEquals(numberOfMessages, queue.getMessageCount()); + + store.forceAnotherPage(); + + session.start(); + + ClientConsumer consumer = session.createConsumer(PagingTest.ADDRESS); + + for (int i = 0; i < numberOfMessages; i++) { + message = consumer.receive(5000); + assertNotNull(message); + message.acknowledge(); + } + + session.commit(); + + assertNull(consumer.receiveImmediate()); + + consumer.close(); + + store.getCursorProvider().cleanup(); + + Assert.assertEquals(0, queue.getMessageCount()); + + long timeout = System.currentTimeMillis() + 5000; + while (store.isPaging() && timeout > System.currentTimeMillis()) { + Thread.sleep(100); + } + + store.getCursorProvider().cleanup(); + + sf.close(); + + locator.close(); + + Assert.assertEquals(1, store.getNumberOfPages()); + + } finally { + try { + server.stop(); + } catch (Throwable ignored) { + } + } + } + public void internalTestMultiFilters(boolean browsing) throws Throwable { clearDataRecreateServerDirs(); From d35f01d25d898d50d90c17be534d127074ec7618 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 25 Jan 2018 08:48:39 -0600 Subject: [PATCH 081/207] ARTEMIS-1954 eliminate all possible usage of JMSServerManager --- .../factory/ConnectionFactoryProvider.java | 4 +- .../jms/server/embedded/EmbeddedJMS.java | 5 + .../artemis/junit/EmbeddedJMSResource.java | 4 + .../ActiveMQBootstrapListener.java | 15 +- .../integration/EmbeddedRestActiveMQ.java | 13 +- .../integration/EmbeddedRestActiveMQJMS.java | 7 +- .../EmbeddedRestActiveMQJMSTest.java | 34 ++--- .../artemis/rest/test/EmbeddedTest.java | 19 +-- artemis-rest/src/test/resources/broker.xml | 60 ++++---- docs/user-manual/en/unit-testing.md | 8 +- .../src/main/resources/spring-jms-beans.xml | 5 +- .../spring/SpringBindingRegistry.java | 4 + .../spring/SpringJmsBootstrap.java | 5 + .../addressConfig/artemisServer.groovy | 1 + .../resources/servers/artemisServer.groovy | 1 + .../byteman/JMSBridgeReconnectionTest.java | 6 +- .../extras/jms/bridge/BridgeTestBase.java | 64 ++------- .../jms/bridge/ClusteredBridgeTestBase.java | 37 ++--- .../jms/bridge/JMSBridgeReconnectionTest.java | 24 ++-- .../extras/jms/bridge/JMSBridgeTest.java | 12 +- .../hornetq/HornetQProtocolManagerTest.java | 51 ++----- .../client/FailureDeadlockTest.java | 9 +- .../crossprotocol/AMQPToOpenwireTest.java | 4 +- .../integration/jms/FloodServerTest.java | 36 +---- .../ManualReconnectionToSingleServerTest.java | 45 +++--- .../tests/integration/jms/RedeployTest.java | 113 +++++++-------- .../client/RemoteConnectionStressTest.java | 20 +-- .../jms/cluster/JMSFailoverListenerTest.java | 30 +--- .../jms/connection/ExceptionListenerTest.java | 12 +- .../jms/server/JMSServerStartStopTest.java | 22 +-- .../openwire/OpenWireTestBase.java | 41 +----- .../amq/ConnectionErrorSocketCloseTest.java | 11 +- .../paging/MultipleProducersPagingTest.java | 39 ++---- .../integration/persistence/SyncSendTest.java | 10 +- .../integration/plugin/StompPluginTest.java | 10 +- .../spring/SpringIntegrationTest.java | 4 +- .../integration/stomp/FQQNStompTest.java | 2 +- .../stomp/StompConnectionCleanupTest.java | 16 +-- .../tests/integration/stomp/StompTest.java | 130 ++++++++---------- .../integration/stomp/StompTestBase.java | 27 ++-- .../stomp/StompTestMultiThreaded.java | 6 +- .../stomp/StompWebSocketMaxFrameTest.java | 2 +- .../integration/stomp/StompWebSocketTest.java | 14 +- .../StompWithClientIdValidationTest.java | 14 +- .../stomp/StompWithLargeMessagesTest.java | 2 +- .../stomp/StompWithSecurityTest.java | 2 +- .../integration/stomp/v11/StompV11Test.java | 24 ++-- .../integration/stomp/v12/StompV12Test.java | 8 +- .../src/test/resources/spring-jms-beans.xml | 2 +- .../jms/tests/AcknowledgementTest.java | 13 +- .../jms/tests/ActiveMQServerTestCase.java | 5 - .../jms/tests/CTSMiscellaneousTest.java | 16 +-- .../artemis/jms/tests/JMSTestCase.java | 16 +-- .../artemis/jms/tests/SecurityTest.java | 1 + .../artemis/common/SpawnedJMSServer.java | 9 +- .../jms/bridge/impl/JMSBridgeImplTest.java | 20 +-- 56 files changed, 401 insertions(+), 713 deletions(-) diff --git a/artemis-cdi-client/src/main/java/org/apache/artemis/client/cdi/factory/ConnectionFactoryProvider.java b/artemis-cdi-client/src/main/java/org/apache/artemis/client/cdi/factory/ConnectionFactoryProvider.java index b2494742469..de53dc07c03 100644 --- a/artemis-cdi-client/src/main/java/org/apache/artemis/client/cdi/factory/ConnectionFactoryProvider.java +++ b/artemis-cdi-client/src/main/java/org/apache/artemis/client/cdi/factory/ConnectionFactoryProvider.java @@ -35,7 +35,6 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.artemis.client.cdi.configuration.ArtemisClientConfiguration; @ApplicationScoped @@ -56,8 +55,7 @@ public void setupConnection() { if (configuration.startEmbeddedBroker()) { try { ActiveMQServer activeMQServer = ActiveMQServers.newActiveMQServer(embeddedConfiguration, false); - JMSServerManagerImpl jmsServerManager = new JMSServerManagerImpl(activeMQServer); - jmsServerManager.start(); + activeMQServer.start(); } catch (Exception e) { throw new RuntimeException("Unable to start embedded JMS", e); } diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/embedded/EmbeddedJMS.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/embedded/EmbeddedJMS.java index 5efffe6add7..4904242c7b1 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/embedded/EmbeddedJMS.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/embedded/EmbeddedJMS.java @@ -30,12 +30,17 @@ import org.apache.activemq.artemis.spi.core.naming.BindingRegistry; /** + * Deprecated in favor of org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ. Since Artemis 2.0 all JMS + * specific broker management classes, interfaces, and methods have been deprecated in favor of their more general + * counter-parts. + * * Simple bootstrap class that parses activemq config files (server and jms and security) and starts * an ActiveMQServer instance and populates it with configured JMS endpoints. *

* JMS Endpoints are registered with a simple MapBindingRegistry. If you want to use a different registry * you must set the registry property of this class or call the setRegistry() method if you want to use JNDI */ +@Deprecated public class EmbeddedJMS extends EmbeddedActiveMQ { protected JMSServerManagerImpl serverManager; diff --git a/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/EmbeddedJMSResource.java b/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/EmbeddedJMSResource.java index 3a2b05da0c5..be21a6f60de 100644 --- a/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/EmbeddedJMSResource.java +++ b/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/EmbeddedJMSResource.java @@ -56,6 +56,9 @@ import org.slf4j.LoggerFactory; /** + * Deprecated in favor of EmbeddedActiveMQResource. Since Artemis 2.0 all JMS specific broker management classes, + * interfaces, and methods have been deprecated in favor of their more general counter-parts. + * * A JUnit Rule that embeds an ActiveMQ Artemis JMS server into a test. * * This JUnit Rule is designed to simplify using embedded servers in unit tests. Adding the rule to a test will startup @@ -73,6 +76,7 @@ * } * */ +@Deprecated public class EmbeddedJMSResource extends ExternalResource { static final String SERVER_NAME = "embedded-jms-server"; diff --git a/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/ActiveMQBootstrapListener.java b/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/ActiveMQBootstrapListener.java index 0fb373150c6..035cfeea20e 100644 --- a/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/ActiveMQBootstrapListener.java +++ b/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/ActiveMQBootstrapListener.java @@ -16,23 +16,20 @@ */ package org.apache.activemq.artemis.rest.integration; -import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; -import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS; +import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ; public class ActiveMQBootstrapListener implements ServletContextListener { - private EmbeddedJMS jms; + private EmbeddedActiveMQ activeMQ; @Override public void contextInitialized(ServletContextEvent contextEvent) { - ServletContext context = contextEvent.getServletContext(); - jms = new EmbeddedJMS(); - jms.setRegistry(new ServletContextBindingRegistry(context)); + activeMQ = new EmbeddedActiveMQ(); try { - jms.start(); + activeMQ.start(); } catch (Exception e) { throw new RuntimeException(e); } @@ -41,8 +38,8 @@ public void contextInitialized(ServletContextEvent contextEvent) { @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { try { - if (jms != null) - jms.stop(); + if (activeMQ != null) + activeMQ.stop(); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQ.java b/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQ.java index fff8a444d3b..7a24caa4c92 100644 --- a/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQ.java +++ b/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQ.java @@ -22,13 +22,13 @@ import org.jboss.resteasy.plugins.server.tjws.TJWSEmbeddedJaxrsServer; import org.jboss.resteasy.test.TestPortProvider; -class EmbeddedRestActiveMQ { +public class EmbeddedRestActiveMQ { private TJWSEmbeddedJaxrsServer tjws = new TJWSEmbeddedJaxrsServer(); - EmbeddedActiveMQ embeddedActiveMQ; + private EmbeddedActiveMQ embeddedActiveMQ; private MessageServiceManager manager = new MessageServiceManager(null); - EmbeddedRestActiveMQ(ConnectionFactoryOptions jmsOptions) { + public EmbeddedRestActiveMQ(ConnectionFactoryOptions jmsOptions) { int port = TestPortProvider.getPort(); tjws.setPort(port); tjws.setRootResourcePath(""); @@ -65,4 +65,11 @@ public void stop() throws Exception { embeddedActiveMQ.stop(); } + public EmbeddedActiveMQ getEmbeddedActiveMQ() { + return embeddedActiveMQ; + } + + public void setEmbeddedActiveMQ(EmbeddedActiveMQ embeddedActiveMQ) { + this.embeddedActiveMQ = embeddedActiveMQ; + } } diff --git a/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQJMS.java b/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQJMS.java index 550be7ded64..8be6903c8be 100644 --- a/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQJMS.java +++ b/artemis-rest/src/main/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQJMS.java @@ -20,6 +20,7 @@ import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS; import org.apache.activemq.artemis.spi.core.naming.BindingRegistry; +@Deprecated public class EmbeddedRestActiveMQJMS extends EmbeddedRestActiveMQ { public EmbeddedRestActiveMQJMS(ConnectionFactoryOptions jmsOptions) { @@ -28,14 +29,14 @@ public EmbeddedRestActiveMQJMS(ConnectionFactoryOptions jmsOptions) { @Override protected void initEmbeddedActiveMQ() { - embeddedActiveMQ = new EmbeddedJMS(); + super.setEmbeddedActiveMQ(new EmbeddedJMS()); } public BindingRegistry getRegistry() { - return ((EmbeddedJMS) embeddedActiveMQ).getRegistry(); + return ((EmbeddedJMS) getEmbeddedActiveMQ()).getRegistry(); } public EmbeddedJMS getEmbeddedJMS() { - return (EmbeddedJMS) embeddedActiveMQ; + return (EmbeddedJMS) getEmbeddedActiveMQ(); } } diff --git a/artemis-rest/src/test/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQJMSTest.java b/artemis-rest/src/test/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQJMSTest.java index f012020bc87..88ac35ffe29 100644 --- a/artemis-rest/src/test/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQJMSTest.java +++ b/artemis-rest/src/test/java/org/apache/activemq/artemis/rest/integration/EmbeddedRestActiveMQJMSTest.java @@ -19,26 +19,22 @@ import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; -import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.ObjectMessage; import javax.jms.Session; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import org.apache.activemq.artemis.api.jms.JMSFactoryType; +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration; import org.apache.activemq.artemis.rest.HttpHeaderProperty; import org.apache.activemq.artemis.rest.test.TransformTest; -import org.apache.activemq.artemis.spi.core.naming.BindingRegistry; +import org.apache.activemq.artemis.rest.test.Util; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule; import org.jboss.resteasy.client.ClientRequest; import org.jboss.resteasy.client.ClientResponse; import org.jboss.resteasy.spi.Link; import org.jboss.resteasy.test.TestPortProvider; -import org.apache.activemq.artemis.rest.test.Util; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -48,22 +44,22 @@ public class EmbeddedRestActiveMQJMSTest { - private static EmbeddedRestActiveMQJMS server; + private static EmbeddedRestActiveMQ server; + private static ConnectionFactory factory; private static Link consumeNext; @BeforeClass public static void startEmbedded() throws Exception { - server = new EmbeddedRestActiveMQJMS(null); - assertNotNull(server.embeddedActiveMQ); + server = new EmbeddedRestActiveMQ(null); + assertNotNull(server.getEmbeddedActiveMQ()); server.getManager().setConfigResourcePath("activemq-rest.xml"); SecurityConfiguration securityConfiguration = createDefaultSecurityConfiguration(); ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), securityConfiguration); - server.getEmbeddedJMS().setSecurityManager(securityManager); + server.getEmbeddedActiveMQ().setSecurityManager(securityManager); server.start(); - List connectors = createInVmConnector(); - server.getEmbeddedJMS().getJMSServerManager().createConnectionFactory("ConnectionFactory", false, JMSFactoryType.CF, connectors, "ConnectionFactory"); + factory = ActiveMQJMSClient.createConnectionFactory("vm://0", "cf"); ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/queues/exampleQueue")); @@ -79,12 +75,6 @@ public static void startEmbedded() throws Exception { System.out.println("consume-next: " + consumeNext); } - private static List createInVmConnector() { - List connectors = new ArrayList<>(); - connectors.add("in-vm"); - return connectors; - } - @AfterClass public static void stopEmbedded() throws Exception { server.stop(); @@ -167,12 +157,6 @@ public void shouldUseJsonAcceptHeaderToSetContentType() throws Exception { assertNotNull(consumeNext); } - private static Connection createConnection() throws JMSException { - BindingRegistry reg = server.getRegistry(); - ConnectionFactory factory = (ConnectionFactory) reg.lookup("ConnectionFactory"); - return factory.createConnection(); - } - private static SecurityConfiguration createDefaultSecurityConfiguration() { SecurityConfiguration securityConfiguration = new SecurityConfiguration(); securityConfiguration.addUser("guest", "guest"); @@ -189,7 +173,7 @@ private TransformTest.Order createTestOrder(String name, String amount) { } private static void publish(String destination, Serializable object, String contentType) throws Exception { - Connection conn = createConnection(); + Connection conn = factory.createConnection(); Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); Destination dest = session.createQueue(destination); diff --git a/artemis-rest/src/test/java/org/apache/activemq/artemis/rest/test/EmbeddedTest.java b/artemis-rest/src/test/java/org/apache/activemq/artemis/rest/test/EmbeddedTest.java index dc0ea0fb4d6..07d69803188 100644 --- a/artemis-rest/src/test/java/org/apache/activemq/artemis/rest/test/EmbeddedTest.java +++ b/artemis-rest/src/test/java/org/apache/activemq/artemis/rest/test/EmbeddedTest.java @@ -23,14 +23,11 @@ import javax.jms.ObjectMessage; import javax.jms.Session; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import org.apache.activemq.artemis.api.jms.JMSFactoryType; +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration; import org.apache.activemq.artemis.rest.HttpHeaderProperty; -import org.apache.activemq.artemis.rest.integration.EmbeddedRestActiveMQJMS; -import org.apache.activemq.artemis.spi.core.naming.BindingRegistry; +import org.apache.activemq.artemis.rest.integration.EmbeddedRestActiveMQ; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule; import org.jboss.resteasy.client.ClientRequest; @@ -44,22 +41,19 @@ public class EmbeddedTest { - public static EmbeddedRestActiveMQJMS server; + public static EmbeddedRestActiveMQ server; @BeforeClass public static void startEmbedded() throws Exception { - server = new EmbeddedRestActiveMQJMS(null); + server = new EmbeddedRestActiveMQ(null); server.getManager().setConfigResourcePath("activemq-rest.xml"); SecurityConfiguration securityConfiguration = new SecurityConfiguration(); securityConfiguration.addUser("guest", "guest"); securityConfiguration.addRole("guest", "guest"); securityConfiguration.setDefaultUser("guest"); ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), securityConfiguration); - server.getEmbeddedJMS().setSecurityManager(securityManager); + server.getEmbeddedActiveMQ().setSecurityManager(securityManager); server.start(); - List connectors = new ArrayList<>(); - connectors.add("in-vm"); - server.getEmbeddedJMS().getJMSServerManager().createConnectionFactory("ConnectionFactory", false, JMSFactoryType.CF, connectors, "ConnectionFactory"); } @AfterClass @@ -69,8 +63,7 @@ public static void stopEmbedded() throws Exception { } public static void publish(String destination, Serializable object, String contentType) throws Exception { - BindingRegistry reg = server.getRegistry(); - ConnectionFactory factory = (ConnectionFactory) reg.lookup("ConnectionFactory"); + ConnectionFactory factory = ActiveMQJMSClient.createConnectionFactory("vm://0","cf"); Connection conn = factory.createConnection(); Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); Destination dest = session.createQueue(destination); diff --git a/artemis-rest/src/test/resources/broker.xml b/artemis-rest/src/test/resources/broker.xml index 4d76412ead7..a74b1baaba9 100644 --- a/artemis-rest/src/test/resources/broker.xml +++ b/artemis-rest/src/test/resources/broker.xml @@ -14,40 +14,36 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - - - - + - false - - - - vm://0 - - - - vm://0 - - - - - - - - - - - - - - - + false + + + vm://0 + + + + + + + + + + + + + + + + + +

+ + + +
+ diff --git a/docs/user-manual/en/unit-testing.md b/docs/user-manual/en/unit-testing.md index 859ec007ac1..7c909b7957e 100644 --- a/docs/user-manual/en/unit-testing.md +++ b/docs/user-manual/en/unit-testing.md @@ -22,14 +22,14 @@ These are provided as JUnit "rules" and can make it easier to embed messaging fu ### Declare a rule on your JUnit Test ```java -import org.apache.activemq.artemis.junit.EmbeddedJMSResource; +import org.apache.activemq.artemis.junit.EmbeddedActiveMQResource; import org.junit.Rule; import org.junit.Test; public class MyTest { @Rule - public EmbeddedJMSResource resource = new EmbeddedJMSResource(); + public EmbeddedActiveMQResource resource = new EmbeddedActiveMQResource(); @Test public void myTest() { @@ -45,8 +45,8 @@ This will start a server that will be available for your test: [main] 17:00:16,666 INFO [org.apache.activemq.artemis.core.server] AMQ221045: libaio is not available, switching the configuration into NIO [main] 17:00:16,688 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-server]. Adding protocol support for: CORE [main] 17:00:16,801 INFO [org.apache.activemq.artemis.core.server] AMQ221007: Server is now live -[main] 17:00:16,801 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 1.5.0-SNAPSHOT [embedded-jms-server, nodeID=39e78380-842c-11e6-9e43-f45c8992f3c7] -[main] 17:00:16,891 INFO [org.apache.activemq.artemis.core.server] AMQ221002: Apache ActiveMQ Artemis Message Broker version 1.5.0-SNAPSHOT [39e78380-842c-11e6-9e43-f45c8992f3c7] stopped, uptime 0.272 seconds +[main] 17:00:16,801 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 2.5.0-SNAPSHOT [embedded-server, nodeID=39e78380-842c-11e6-9e43-f45c8992f3c7] +[main] 17:00:16,891 INFO [org.apache.activemq.artemis.core.server] AMQ221002: Apache ActiveMQ Artemis Message Broker version 2.5.0-SNAPSHOT [39e78380-842c-11e6-9e43-f45c8992f3c7] stopped, uptime 0.272 seconds ``` ### Ordering rules diff --git a/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml b/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml index 07545fecac2..4a50a81760f 100644 --- a/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml +++ b/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml @@ -47,9 +47,8 @@ under the License. - - + + diff --git a/integration/activemq-spring-integration/src/main/java/org/apache/activemq/artemis/integration/spring/SpringBindingRegistry.java b/integration/activemq-spring-integration/src/main/java/org/apache/activemq/artemis/integration/spring/SpringBindingRegistry.java index cda3f8a0850..892eb6ffa3b 100644 --- a/integration/activemq-spring-integration/src/main/java/org/apache/activemq/artemis/integration/spring/SpringBindingRegistry.java +++ b/integration/activemq-spring-integration/src/main/java/org/apache/activemq/artemis/integration/spring/SpringBindingRegistry.java @@ -20,6 +20,10 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.ConfigurableBeanFactory; +/** + * This has been deprecated since org.apache.activemq.artemis.integration.spring.SpringJmsBootstrap was also deprecated. + */ +@Deprecated public class SpringBindingRegistry implements BindingRegistry { private ConfigurableBeanFactory factory; diff --git a/integration/activemq-spring-integration/src/main/java/org/apache/activemq/artemis/integration/spring/SpringJmsBootstrap.java b/integration/activemq-spring-integration/src/main/java/org/apache/activemq/artemis/integration/spring/SpringJmsBootstrap.java index 3bbfc3dcde7..a5e406f38b7 100644 --- a/integration/activemq-spring-integration/src/main/java/org/apache/activemq/artemis/integration/spring/SpringJmsBootstrap.java +++ b/integration/activemq-spring-integration/src/main/java/org/apache/activemq/artemis/integration/spring/SpringJmsBootstrap.java @@ -22,6 +22,11 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableBeanFactory; +/** + * This really isn't needed for Spring integration as demonstrated by org.apache.activemq.artemis.tests.integration.spring.SpringIntegrationTest + * as well as the "spring-integration" example + */ +@Deprecated public class SpringJmsBootstrap extends EmbeddedJMS implements BeanFactoryAware { @Override diff --git a/tests/compatibility-tests/src/main/resources/addressConfig/artemisServer.groovy b/tests/compatibility-tests/src/main/resources/addressConfig/artemisServer.groovy index 0f85332d8db..c73994274f4 100644 --- a/tests/compatibility-tests/src/main/resources/addressConfig/artemisServer.groovy +++ b/tests/compatibility-tests/src/main/resources/addressConfig/artemisServer.groovy @@ -41,6 +41,7 @@ configuration.addAddressesSetting("myQueue", new AddressSettings().setAddressFul configuration.addAddressesSetting("#", new AddressSettings().setAddressFullMessagePolicy(AddressFullMessagePolicy.BLOCK).setMaxSizeBytes(10 * 1024).setPageSizeBytes(1024)); jmsConfiguration = new JMSConfigurationImpl(); +// used here even though it's deprecated to be compatible with older versions of the broker server = new EmbeddedJMS(); server.setConfiguration(configuration); server.setJmsConfiguration(jmsConfiguration); diff --git a/tests/compatibility-tests/src/main/resources/servers/artemisServer.groovy b/tests/compatibility-tests/src/main/resources/servers/artemisServer.groovy index fbae168ca8c..c8cc93be87a 100644 --- a/tests/compatibility-tests/src/main/resources/servers/artemisServer.groovy +++ b/tests/compatibility-tests/src/main/resources/servers/artemisServer.groovy @@ -55,6 +55,7 @@ try { jmsConfiguration = new JMSConfigurationImpl(); +// used here even though it's deprecated to be compatible with older versions of the broker server = new EmbeddedJMS(); server.setConfiguration(configuration); server.setJmsConfiguration(jmsConfiguration); diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/JMSBridgeReconnectionTest.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/JMSBridgeReconnectionTest.java index 03e2ddcfb9c..1733431399b 100644 --- a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/JMSBridgeReconnectionTest.java +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/JMSBridgeReconnectionTest.java @@ -24,10 +24,10 @@ import org.apache.activemq.artemis.core.protocol.core.Packet; import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionSendMessage; +import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.jms.bridge.ConnectionFactoryFactory; import org.apache.activemq.artemis.jms.bridge.QualityOfServiceMode; import org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl; -import org.apache.activemq.artemis.jms.server.JMSServerManager; import org.apache.activemq.artemis.tests.extras.jms.bridge.BridgeTestBase; import org.jboss.byteman.contrib.bmunit.BMRule; import org.jboss.byteman.contrib.bmunit.BMRules; @@ -52,7 +52,7 @@ public class JMSBridgeReconnectionTest extends BridgeTestBase { targetLocation = "ENTRY", action = "org.apache.activemq.artemis.tests.extras.byteman.JMSBridgeReconnectionTest.pause2($2,$3,$4);")}) public void performCrashDestinationStopBridge() throws Exception { - activeMQServer = jmsServer1; + activeMQServer = server1; ConnectionFactoryFactory factInUse0 = cff0; ConnectionFactoryFactory factInUse1 = cff1; final JMSBridgeImpl bridge = new JMSBridgeImpl(factInUse0, factInUse1, sourceQueueFactory, targetQueueFactory, null, null, null, null, null, 1000, -1, QualityOfServiceMode.DUPLICATES_OK, 10, -1, null, null, false).setBridgeName("test-bridge"); @@ -106,7 +106,7 @@ public static void pause(Packet packet) { } } - static JMSServerManager activeMQServer; + static ActiveMQServer activeMQServer; static boolean stopped = false; static int count = 20; static CountDownLatch stopLatch = new CountDownLatch(1); diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/BridgeTestBase.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/BridgeTestBase.java index c47d0263d1f..59ec0183b6d 100644 --- a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/BridgeTestBase.java +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/BridgeTestBase.java @@ -37,6 +37,8 @@ import com.arjuna.ats.arjuna.coordinator.TransactionReaper; import com.arjuna.ats.arjuna.coordinator.TxControl; import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.api.core.management.AddressControl; import org.apache.activemq.artemis.api.core.management.QueueControl; @@ -44,7 +46,6 @@ import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.api.jms.JMSFactoryType; import org.apache.activemq.artemis.core.config.Configuration; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; import org.apache.activemq.artemis.core.remoting.impl.invm.TransportConstants; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; @@ -53,13 +54,11 @@ import org.apache.activemq.artemis.jms.bridge.DestinationFactory; import org.apache.activemq.artemis.jms.bridge.QualityOfServiceMode; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; +import org.apache.activemq.artemis.jms.client.ActiveMQDestination; import org.apache.activemq.artemis.jms.client.ActiveMQJMSConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQMessage; import org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory; -import org.apache.activemq.artemis.jms.server.JMSServerManager; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; -import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.After; import org.junit.Assert; @@ -88,16 +87,8 @@ public abstract class BridgeTestBase extends ActiveMQTestBase { protected ActiveMQServer server0; - protected JMSServerManager jmsServer0; - protected ActiveMQServer server1; - protected JMSServerManager jmsServer1; - - private InVMNamingContext context0; - - protected InVMNamingContext context1; - protected HashMap params1; protected ConnectionFactoryFactory cff0LowProducerWindow; @@ -111,11 +102,7 @@ public void setUp() throws Exception { Configuration conf0 = createBasicConfig().setJournalDirectory(getJournalDir(0, false)).setBindingsDirectory(getBindingsDir(0, false)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY)); server0 = addServer(ActiveMQServers.newActiveMQServer(conf0, false)); - - context0 = new InVMNamingContext(); - jmsServer0 = new JMSServerManagerImpl(server0); - jmsServer0.setRegistry(new JndiBindingRegistry(context0)); - jmsServer0.start(); + server0.start(); params1 = new HashMap<>(); params1.put(TransportConstants.SERVER_ID_PROP_NAME, 1); @@ -123,20 +110,7 @@ public void setUp() throws Exception { Configuration conf1 = createBasicConfig().setJournalDirectory(getJournalDir(1, false)).setBindingsDirectory(getBindingsDir(1, false)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params1)); server1 = addServer(ActiveMQServers.newActiveMQServer(conf1, false)); - - context1 = new InVMNamingContext(); - - jmsServer1 = new JMSServerManagerImpl(server1); - jmsServer1.setRegistry(new JndiBindingRegistry(context1)); - jmsServer1.start(); - - createQueue("sourceQueue", 0); - - jmsServer0.createTopic(false, "sourceTopic", "/topic/sourceTopic"); - - createQueue("localTargetQueue", 0); - - createQueue("targetQueue", 1); + server1.start(); setUpAdministeredObjects(); TxControl.enable(); @@ -147,11 +121,11 @@ public void setUp() throws Exception { } protected void createQueue(final String queueName, final int index) throws Exception { - JMSServerManager server = jmsServer0; + ActiveMQServer server = server0; if (index == 1) { - server = jmsServer1; + server = server1; } - assertTrue("queue '/queue/" + queueName + "' created", server.createQueue(false, queueName, null, true, "/queue/" + queueName)); + assertTrue("queue '/queue/" + queueName + "' created", server.createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false) != null); } @Override @@ -170,8 +144,8 @@ public void tearDown() throws Exception { if (cff1 instanceof ActiveMQConnectionFactory) { ((ActiveMQConnectionFactory) cff1).close(); } - stopComponent(jmsServer0); - stopComponent(jmsServer1); + stopComponent(server0); + stopComponent(server1); cff0 = cff1 = null; cff0xa = cff1xa = null; @@ -187,18 +161,8 @@ public void tearDown() throws Exception { server0 = null; - jmsServer0 = null; - server1 = null; - jmsServer1 = null; - if (context0 != null) - context0.close(); - context0 = null; - if (context1 != null) - context1.close(); - context1 = null; - // Shutting down Arjuna threads TxControl.disable(true); @@ -297,7 +261,7 @@ public XAConnectionFactory createConnectionFactory() throws Exception { sourceQueueFactory = new DestinationFactory() { @Override public Destination createDestination() throws Exception { - return (Destination) context0.lookup("/queue/sourceQueue"); + return ActiveMQDestination.createDestination("/queue/sourceQueue", ActiveMQDestination.TYPE.QUEUE); } }; @@ -306,7 +270,7 @@ public Destination createDestination() throws Exception { targetQueueFactory = new DestinationFactory() { @Override public Destination createDestination() throws Exception { - return (Destination) context1.lookup("/queue/targetQueue"); + return ActiveMQDestination.createDestination("/queue/targetQueue", ActiveMQDestination.TYPE.QUEUE); } }; @@ -315,7 +279,7 @@ public Destination createDestination() throws Exception { sourceTopicFactory = new DestinationFactory() { @Override public Destination createDestination() throws Exception { - return (Destination) context0.lookup("/topic/sourceTopic"); + return ActiveMQDestination.createDestination("/topic/sourceTopic", ActiveMQDestination.TYPE.TOPIC); } }; @@ -324,7 +288,7 @@ public Destination createDestination() throws Exception { localTargetQueueFactory = new DestinationFactory() { @Override public Destination createDestination() throws Exception { - return (Destination) context0.lookup("/queue/localTargetQueue"); + return ActiveMQDestination.createDestination("/queue/localTargetQueue", ActiveMQDestination.TYPE.QUEUE); } }; diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java index 165601a18de..6fae34e77ca 100644 --- a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/ClusteredBridgeTestBase.java @@ -18,7 +18,6 @@ import javax.jms.ConnectionFactory; import javax.jms.Destination; -import javax.naming.Context; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -28,6 +27,8 @@ import com.arjuna.ats.arjuna.coordinator.TransactionReaper; import com.arjuna.ats.arjuna.coordinator.TxControl; import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.api.core.client.ClientConsumer; @@ -43,16 +44,13 @@ import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.ha.ReplicaPolicyConfiguration; import org.apache.activemq.artemis.core.config.ha.ReplicatedPolicyConfiguration; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; import org.apache.activemq.artemis.core.remoting.impl.invm.TransportConstants; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.jms.bridge.ConnectionFactoryFactory; import org.apache.activemq.artemis.jms.bridge.DestinationFactory; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; -import org.apache.activemq.artemis.jms.server.JMSServerManager; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; -import org.apache.activemq.artemis.tests.unit.util.InVMContext; +import org.apache.activemq.artemis.jms.client.ActiveMQDestination; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.After; import org.junit.Before; @@ -113,14 +111,12 @@ protected class ServerGroup { private String name; private int id; - private JMSServerManager liveNode; - private JMSServerManager backupNode; + private ActiveMQServer liveNode; + private ActiveMQServer backupNode; private TransportConfiguration liveConnector; private TransportConfiguration backupConnector; - private Context liveContext; - private ServerLocator locator; private ClientSessionFactory sessionFactory; @@ -149,30 +145,21 @@ public void create() throws Exception { //live Configuration conf0 = createBasicConfig().setJournalDirectory(getJournalDir(id, false)).setBindingsDirectory(getBindingsDir(id, false)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params0)).addConnectorConfiguration(liveConnector.getName(), liveConnector).setHAPolicyConfiguration(new ReplicatedPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())); - ActiveMQServer server0 = addServer(ActiveMQServers.newActiveMQServer(conf0, true)); - - liveContext = new InVMContext(); - liveNode = new JMSServerManagerImpl(server0); - liveNode.setRegistry(new JndiBindingRegistry(liveContext)); + liveNode = addServer(ActiveMQServers.newActiveMQServer(conf0, true)); //backup ReplicaPolicyConfiguration replicaPolicyConfiguration = new ReplicaPolicyConfiguration(); replicaPolicyConfiguration.setQuorumVoteWait(QUORUM_VOTE_WAIT_TIME_SEC); Configuration config = createBasicConfig().setJournalDirectory(getJournalDir(id, true)).setBindingsDirectory(getBindingsDir(id, true)).addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, params)).addConnectorConfiguration(backupConnector.getName(), backupConnector).addConnectorConfiguration(liveConnector.getName(), liveConnector).setHAPolicyConfiguration(replicaPolicyConfiguration).addClusterConfiguration(basicClusterConnectionConfig(backupConnector.getName(), liveConnector.getName())); - ActiveMQServer backup = addServer(ActiveMQServers.newActiveMQServer(config, true)); - - Context context = new InVMContext(); - - backupNode = new JMSServerManagerImpl(backup); - backupNode.setRegistry(new JndiBindingRegistry(context)); + backupNode = addServer(ActiveMQServers.newActiveMQServer(config, true)); } public void start() throws Exception { liveNode.start(); - waitForServerToStart(liveNode.getActiveMQServer()); + waitForServerToStart(liveNode); backupNode.start(); - waitForRemoteBackupSynchronization(backupNode.getActiveMQServer()); + waitForRemoteBackupSynchronization(backupNode); locator = ActiveMQClient.createServerLocatorWithHA(liveConnector).setReconnectAttempts(15); sessionFactory = locator.createSessionFactory(); @@ -186,7 +173,7 @@ public void stop() throws Exception { } public void createQueue(String queueName) throws Exception { - liveNode.createQueue(true, queueName, null, true, "/queue/" + queueName); + liveNode.createQueue(SimpleString.toSimpleString(queueName), RoutingType.ANYCAST, SimpleString.toSimpleString(queueName), null, true, false); } public ConnectionFactoryFactory getConnectionFactoryFactory() { @@ -207,7 +194,7 @@ public DestinationFactory getDestinationFactory(final String queueName) { DestinationFactory destFactory = new DestinationFactory() { @Override public Destination createDestination() throws Exception { - return (Destination) liveContext.lookup("/queue/" + queueName); + return ActiveMQDestination.createDestination(queueName, ActiveMQDestination.TYPE.QUEUE); } }; return destFactory; @@ -261,7 +248,7 @@ public void failoverEvent(FailoverEventType eventType) { } }); - liveNode.getActiveMQServer().stop(); + liveNode.stop(); boolean ok = latch.await(10000, TimeUnit.MILLISECONDS); assertTrue(ok); diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/JMSBridgeReconnectionTest.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/JMSBridgeReconnectionTest.java index 7c1b8783c26..4d101cc8081 100644 --- a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/JMSBridgeReconnectionTest.java +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/JMSBridgeReconnectionTest.java @@ -105,7 +105,7 @@ public void testCrashAndReconnectDestCrashBeforePrepare_NP() throws Exception { @Test public void testRetryConnectionOnStartup() throws Exception { - jmsServer1.stop(); + server1.stop(); JMSBridgeImpl bridge = new JMSBridgeImpl(cff0, cff1, sourceQueueFactory, targetQueueFactory, null, null, null, null, null, 1000, -1, QualityOfServiceMode.DUPLICATES_OK, 10, -1, null, null, false).setBridgeName("test-bridge"); bridge.setTransactionManager(newTransactionManager()); @@ -115,7 +115,7 @@ public void testRetryConnectionOnStartup() throws Exception { Assert.assertTrue(bridge.isFailed()); // Restart the server - jmsServer1.start(); + server1.start(); createQueue("targetQueue", 1); setUpAdministeredObjects(); @@ -131,7 +131,7 @@ public void testRetryConnectionOnStartup() throws Exception { */ @Test public void testStopBridgeWithFailureWhenStarted() throws Exception { - jmsServer1.stop(); + server1.stop(); JMSBridgeImpl bridge = new JMSBridgeImpl(cff0, cff1, sourceQueueFactory, targetQueueFactory, null, null, null, null, null, 500, -1, QualityOfServiceMode.DUPLICATES_OK, 10, -1, null, null, false).setBridgeName("test-bridge"); bridge.setTransactionManager(newTransactionManager()); @@ -144,7 +144,7 @@ public void testStopBridgeWithFailureWhenStarted() throws Exception { Assert.assertFalse(bridge.isStarted()); // we restart and setup the server for the test's tearDown checks - jmsServer1.start(); + server1.start(); createQueue("targetQueue", 1); setUpAdministeredObjects(); } @@ -187,7 +187,7 @@ private void performCrashAndReconnectDestBasic(final QualityOfServiceMode qosMod JMSBridgeReconnectionTest.log.info("About to crash server"); - jmsServer1.stop(); + server1.stop(); // Wait a while before starting up to simulate the dest being down for a while JMSBridgeReconnectionTest.log.info("Waiting 5 secs before bringing server back up"); @@ -196,7 +196,7 @@ private void performCrashAndReconnectDestBasic(final QualityOfServiceMode qosMod // Restart the server JMSBridgeReconnectionTest.log.info("Restarting server"); - jmsServer1.start(); + server1.start(); // jmsServer1.createQueue(false, "targetQueue", null, true, "queue/targetQueue"); @@ -212,7 +212,7 @@ private void performCrashAndReconnectDestBasic(final QualityOfServiceMode qosMod JMSBridgeReconnectionTest.log.info("Sent messages"); - jmsServer1.stop(); + server1.stop(); bridge.stop(); @@ -248,7 +248,7 @@ public void run() { JMSBridgeReconnectionTest.log.info("About to crash server"); - jmsServer1.stop(); + server1.stop(); // Wait a while before starting up to simulate the dest being down for a while JMSBridgeReconnectionTest.log.info("Waiting 5 secs before bringing server back up"); @@ -303,10 +303,10 @@ public Object createConnectionFactory() throws Exception { JMSBridgeReconnectionTest.log.info("About to crash server"); - jmsServer1.stop(); + server1.stop(); if (restart) { - jmsServer1.start(); + server1.start(); } // Wait a while before starting up to simulate the dest being down for a while JMSBridgeReconnectionTest.log.info("Waiting 5 secs before bringing server back up"); @@ -396,7 +396,7 @@ private void performCrashAndReconnectDestCrashBeforePrepare(final boolean persis JMSBridgeReconnectionTest.log.info("About to crash server"); - jmsServer1.stop(); + server1.stop(); // Wait a while before starting up to simulate the dest being down for a while JMSBridgeReconnectionTest.log.info("Waiting 5 secs before bringing server back up"); @@ -404,7 +404,7 @@ private void performCrashAndReconnectDestCrashBeforePrepare(final boolean persis JMSBridgeReconnectionTest.log.info("Done wait"); // Restart the server - jmsServer1.start(); + server1.start(); createQueue("targetQueue", 1); diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/JMSBridgeTest.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/JMSBridgeTest.java index fed218ba725..6f4b9b698a0 100644 --- a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/JMSBridgeTest.java +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/jms/bridge/JMSBridgeTest.java @@ -336,7 +336,7 @@ public void testStressSameServer_OnceAndOnlyOnce_NP_1() throws Exception { @Test public void testStartBridgeFirst() throws Exception { //stop the source server, we want to start the bridge first - jmsServer0.stop(); + server0.stop(); JMSBridgeImpl bridge = null; ConnectionFactoryFactory factInUse0 = cff0; @@ -349,10 +349,8 @@ public void testStartBridgeFirst() throws Exception { bridge.start(); //now start the server - jmsServer0.start(); - createQueue("sourceQueue", 0); - createQueue("localTargetQueue", 0); - jmsServer0.createTopic(false, "sourceTopic", "/topic/sourceTopic"); + server0.start(); + // Send half the messages sendMessages(cf0, sourceQueue, 0, NUM_MESSAGES / 2, false, false); @@ -1327,7 +1325,7 @@ public Object createConnectionFactory() throws Exception { JMSBridgeTest.log.info("About to crash server"); - jmsServer1.stop(); + server1.stop(); // Now stop the bridge while the failover is happening @@ -1337,7 +1335,7 @@ public Object createConnectionFactory() throws Exception { // Shutdown the source server - jmsServer0.stop(); + server0.stop(); } // Private ------------------------------------------------------------------------------- diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/protocols/hornetq/HornetQProtocolManagerTest.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/protocols/hornetq/HornetQProtocolManagerTest.java index 4c0c545eece..6e7c79c4285 100644 --- a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/protocols/hornetq/HornetQProtocolManagerTest.java +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/protocols/hornetq/HornetQProtocolManagerTest.java @@ -26,20 +26,16 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.activemq.artemis.api.core.Message; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; import org.apache.activemq.artemis.api.core.client.ServerLocator; -import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; +import org.apache.activemq.artemis.core.config.CoreQueueConfiguration; import org.apache.activemq.artemis.core.protocol.hornetq.client.HornetQClientProtocolManagerFactory; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQQueue; -import org.apache.activemq.artemis.jms.server.config.ConnectionFactoryConfiguration; -import org.apache.activemq.artemis.jms.server.config.JMSConfiguration; -import org.apache.activemq.artemis.jms.server.config.impl.ConnectionFactoryConfigurationImpl; -import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl; -import org.apache.activemq.artemis.jms.server.config.impl.JMSQueueConfigurationImpl; -import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS; import org.apache.activemq.artemis.ra.recovery.RecoveryManager; import org.apache.activemq.artemis.service.extensions.xa.recovery.XARecoveryConfig; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; @@ -53,32 +49,21 @@ public class HornetQProtocolManagerTest extends ActiveMQTestBase { ActiveMQServer server; - EmbeddedJMS embeddedJMS; @Override @Before public void setUp() throws Exception { super.setUp(); - Configuration configuration = createDefaultConfig(false); - configuration.setPersistenceEnabled(false); - configuration.getAcceptorConfigurations().clear(); - configuration.addAcceptorConfiguration("legacy", "tcp://localhost:61616?protocols=HORNETQ"). - addAcceptorConfiguration("corepr", "tcp://localhost:61617?protocols=CORE"); - - configuration.addConnectorConfiguration("legacy", "tcp://localhost:61616"); - JMSConfiguration jmsConfiguration = new JMSConfigurationImpl(); - - jmsConfiguration.getQueueConfigurations().add(new JMSQueueConfigurationImpl().setName("testQueue").setBindings("testQueue")); - embeddedJMS = new EmbeddedJMS(); - embeddedJMS.setConfiguration(configuration); - embeddedJMS.setJmsConfiguration(jmsConfiguration); - embeddedJMS.start(); - } - - @Override - public void tearDown() throws Exception { - embeddedJMS.stop(); - super.tearDown(); + server = createServer(createDefaultConfig(false) + .setPersistenceEnabled(false) + .clearAcceptorConfigurations() + .addAcceptorConfiguration("legacy", "tcp://localhost:61616?protocols=HORNETQ") + .addAcceptorConfiguration("corepr", "tcp://localhost:61617?protocols=CORE") + .addQueueConfiguration(new CoreQueueConfiguration() + .setName("testQueue") + .setAddress("testQueue") + .setRoutingType(RoutingType.ANYCAST))); + server.start(); } @Test @@ -105,18 +90,10 @@ public void testLegacy() throws Exception { /** This test will use an ArtemisConnectionFactory with clientProtocolManager=*/ @Test public void testLegacy2() throws Exception { - - ConnectionFactoryConfiguration configuration = new ConnectionFactoryConfigurationImpl(); - configuration.setConnectorNames("legacy"); - configuration.setName("legacy"); - configuration.setProtocolManagerFactoryStr(HornetQClientProtocolManagerFactory.class.getName()); - embeddedJMS.getJMSServerManager().createConnectionFactory(false, configuration, "legacy"); - // WORKAROUND: the 2.0.0 broker introduced addressing change and the 2.2.0 broker added compatibility for old // client libraries relying on the legacy prefixes. The new client being used in this test needs prefix explicitly. Queue queue = new ActiveMQQueue("jms.queue.testQueue"); - - ActiveMQConnectionFactory connectionFactory = (ActiveMQConnectionFactory) embeddedJMS.lookup("legacy"); + ActiveMQConnectionFactory connectionFactory = ActiveMQJMSClient.createConnectionFactory("tcp://localhost:61616?protocolManagerFactoryStr=" + HornetQClientProtocolManagerFactory.class.getName(), "legacy"); Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer producer = session.createProducer(queue); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/FailureDeadlockTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/FailureDeadlockTest.java index 4c6b646ad5b..d2ea515ccd1 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/FailureDeadlockTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/FailureDeadlockTest.java @@ -26,14 +26,11 @@ import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.api.jms.JMSFactoryType; import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQSession; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; -import org.apache.activemq.artemis.tests.integration.jms.server.management.NullInitialContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Assert; import org.junit.Before; @@ -45,8 +42,6 @@ public class FailureDeadlockTest extends ActiveMQTestBase { private ActiveMQServer server; - private JMSServerManagerImpl jmsServer; - private ActiveMQConnectionFactory cf1; private ActiveMQConnectionFactory cf2; @@ -56,9 +51,7 @@ public class FailureDeadlockTest extends ActiveMQTestBase { public void setUp() throws Exception { super.setUp(); server = createServer(false, createDefaultInVMConfig()); - jmsServer = new JMSServerManagerImpl(server); - jmsServer.setRegistry(new JndiBindingRegistry(new NullInitialContext())); - jmsServer.start(); + server.start(); cf1 = ActiveMQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, new TransportConfiguration(INVM_CONNECTOR_FACTORY)); cf2 = ActiveMQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, new TransportConfiguration(INVM_CONNECTOR_FACTORY)); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/crossprotocol/AMQPToOpenwireTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/crossprotocol/AMQPToOpenwireTest.java index 51c90386fa6..4dc2dfa9c97 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/crossprotocol/AMQPToOpenwireTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/crossprotocol/AMQPToOpenwireTest.java @@ -32,12 +32,11 @@ import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.ActiveMQXAConnectionFactory; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.server.ActiveMQServer; -import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; -import org.apache.activemq.artemis.jms.server.JMSServerManager; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.transport.amqp.client.AmqpClient; import org.apache.activemq.transport.amqp.client.AmqpConnection; @@ -57,7 +56,6 @@ public class AMQPToOpenwireTest extends ActiveMQTestBase { public static final int OWPORT = 61616; protected static final String urlString = "tcp://" + OWHOST + ":" + OWPORT + "?wireFormat.cacheEnabled=true"; - JMSServerManager serverManager; private ActiveMQServer server; protected ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(urlString); protected ActiveMQXAConnectionFactory xaFactory = new ActiveMQXAConnectionFactory(urlString); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/FloodServerTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/FloodServerTest.java index 79ed68032e9..9af59b51cc1 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/FloodServerTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/FloodServerTest.java @@ -24,21 +24,12 @@ import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Session; -import java.util.ArrayList; -import java.util.List; -import org.apache.activemq.artemis.api.core.TransportConfiguration; -import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; -import org.apache.activemq.artemis.api.jms.JMSFactoryType; import org.apache.activemq.artemis.core.config.Configuration; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; -import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; -import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Before; import org.junit.Test; @@ -53,10 +44,6 @@ public class FloodServerTest extends ActiveMQTestBase { private ActiveMQServer server; - private JMSServerManagerImpl serverManager; - - private InVMNamingContext initialContext; - private final String topicName = "my-topic"; // Static -------------------------------------------------------- @@ -81,39 +68,18 @@ public void setUp() throws Exception { Configuration config = createDefaultNettyConfig(); server = addServer(ActiveMQServers.newActiveMQServer(config, false)); server.start(); - - serverManager = new JMSServerManagerImpl(server); - initialContext = new InVMNamingContext(); - serverManager.setRegistry(new JndiBindingRegistry(initialContext)); - serverManager.start(); - serverManager.activated(); - - serverManager.createTopic(false, topicName, topicName); - registerConnectionFactory(); } // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- - private void registerConnectionFactory() throws Exception { - int retryInterval = 1000; - double retryIntervalMultiplier = 1.0; - int reconnectAttempts = -1; - long callTimeout = 30000; - - List connectorConfigs = new ArrayList<>(); - connectorConfigs.add(new TransportConfiguration(NettyConnectorFactory.class.getName())); - - serverManager.createConnectionFactory("ManualReconnectionToSingleServerTest", false, JMSFactoryType.CF, registerConnectors(server, connectorConfigs), null, 1000, ActiveMQClient.DEFAULT_CONNECTION_TTL, callTimeout, ActiveMQClient.DEFAULT_CALL_FAILOVER_TIMEOUT, ActiveMQClient.DEFAULT_CACHE_LARGE_MESSAGE_CLIENT, ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, ActiveMQClient.DEFAULT_COMPRESS_LARGE_MESSAGES, ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE, ActiveMQClient.DEFAULT_CONSUMER_MAX_RATE, ActiveMQClient.DEFAULT_CONFIRMATION_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_MAX_RATE, false, false, false, ActiveMQClient.DEFAULT_AUTO_GROUP, false, ActiveMQClient.DEFAULT_CONNECTION_LOAD_BALANCING_POLICY_CLASS_NAME, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_USE_GLOBAL_POOLS, ActiveMQClient.DEFAULT_SCHEDULED_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_THREAD_POOL_MAX_SIZE, retryInterval, retryIntervalMultiplier, 1000, reconnectAttempts, ActiveMQClient.DEFAULT_FAILOVER_ON_INITIAL_CONNECTION, null, "/cf"); - } - @Test public void testFoo() { } public void _testFlood() throws Exception { - ConnectionFactory cf = (ConnectionFactory) initialContext.lookup("/cf"); + ConnectionFactory cf = ActiveMQJMSClient.createConnectionFactory("tcp://127.0.0.1:61616?retryInterval=1000&retryIntervalMultiplier=1.0&reconnectAttempts=-1&callTimeout=30000&clientFailureCheckPeriod=1000&maxRetryInterval=1000&blockOnDurableSend=false&blockOnAcknowledge=false", "cf"); final int numProducers = 20; diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ManualReconnectionToSingleServerTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ManualReconnectionToSingleServerTest.java index 9ed0d5b0211..87ef00c2f0d 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ManualReconnectionToSingleServerTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ManualReconnectionToSingleServerTest.java @@ -28,22 +28,18 @@ import javax.jms.Queue; import javax.jms.Session; import javax.naming.Context; +import javax.naming.InitialContext; import java.util.ArrayList; import java.util.Date; +import java.util.Hashtable; import java.util.concurrent.CountDownLatch; import org.apache.activemq.artemis.api.core.TransportConfiguration; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; +import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.core.config.CoreQueueConfiguration; +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; import org.apache.activemq.artemis.core.server.ActiveMQServer; -import org.apache.activemq.artemis.jms.server.JMSServerManager; -import org.apache.activemq.artemis.jms.server.config.ConnectionFactoryConfiguration; -import org.apache.activemq.artemis.jms.server.config.JMSConfiguration; -import org.apache.activemq.artemis.jms.server.config.impl.ConnectionFactoryConfigurationImpl; -import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl; -import org.apache.activemq.artemis.jms.server.config.impl.JMSQueueConfigurationImpl; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; -import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Assert; import org.junit.Before; @@ -64,9 +60,7 @@ public class ManualReconnectionToSingleServerTest extends ActiveMQTestBase { private CountDownLatch reconnectionLatch; private CountDownLatch allMessagesReceived; - private JMSServerManager serverManager; - - private InVMNamingContext context; + private Context context; private static final String QUEUE_NAME = ManualReconnectionToSingleServerTest.class.getSimpleName() + ".queue"; @@ -90,7 +84,7 @@ public void onException(final JMSException e) { public void testExceptionListener() throws Exception { connect(); - ConnectionFactory cf = (ConnectionFactory) context.lookup("/cf"); + ConnectionFactory cf = (ConnectionFactory) context.lookup("cf"); Destination dest = (Destination) context.lookup(QUEUE_NAME); Connection conn = cf.createConnection(); Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -103,10 +97,10 @@ public void testExceptionListener() throws Exception { if (i == NUM / 2) { conn.close(); - serverManager.stop(); + server.stop(); Thread.sleep(5000); - serverManager.start(); - cf = (ConnectionFactory) context.lookup("/cf"); + server.start(); + cf = (ConnectionFactory) context.lookup("cf"); dest = (Destination) context.lookup(QUEUE_NAME); conn = cf.createConnection(); sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -139,21 +133,22 @@ public void testExceptionListener() throws Exception { public void setUp() throws Exception { super.setUp(); - context = new InVMNamingContext(); + Hashtable props = new Hashtable<>(); + props.put(Context.INITIAL_CONTEXT_FACTORY, org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory.class.getCanonicalName()); + props.put("queue." + QUEUE_NAME, QUEUE_NAME); + props.put("connectionFactory.cf", "tcp://127.0.0.1:61616?retryInterval=1000&reconnectAttempts=-1"); + + context = new InitialContext(props); server = createServer(false, createDefaultNettyConfig()); - JMSConfiguration configuration = new JMSConfigurationImpl(); - serverManager = new JMSServerManagerImpl(server, configuration); - serverManager.setRegistry(new JndiBindingRegistry(context)); + Configuration configuration = new ConfigurationImpl(); - configuration.getQueueConfigurations().add(new JMSQueueConfigurationImpl().setName(QUEUE_NAME).setBindings(QUEUE_NAME)); + configuration.getQueueConfigurations().add(new CoreQueueConfiguration().setName(QUEUE_NAME)); ArrayList configs = new ArrayList<>(); configs.add(new TransportConfiguration(NETTY_CONNECTOR_FACTORY)); - ConnectionFactoryConfiguration cfConfig = new ConnectionFactoryConfigurationImpl().setName("cf").setConnectorNames(registerConnectors(server, configs)).setBindings("/cf").setRetryInterval(1000).setReconnectAttempts(-1); - configuration.getConnectionFactoryConfigurations().add(cfConfig); - serverManager.start(); + server.start(); listener = new Listener(); @@ -198,7 +193,7 @@ protected void connect() { while (true) { try { queue = (Queue) initialContext.lookup(QUEUE_NAME); - cf = (ConnectionFactory) initialContext.lookup("/cf"); + cf = (ConnectionFactory) initialContext.lookup("cf"); break; } catch (Exception e) { if (retries++ > retryLimit) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java index 2ba7f3d1068..2df59cee997 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java @@ -35,6 +35,7 @@ import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.postoffice.QueueBinding; import org.apache.activemq.artemis.core.security.Role; +import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ; import org.apache.activemq.artemis.core.server.impl.AddressInfo; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; @@ -54,9 +55,9 @@ public void testRedeploy() throws Exception { URL url2 = RedeployTest.class.getClassLoader().getResource("reload-test-updated-jms.xml"); Files.copy(url1.openStream(), brokerXML); - EmbeddedJMS embeddedJMS = new EmbeddedJMS(); - embeddedJMS.setConfigResourcePath(brokerXML.toUri().toString()); - embeddedJMS.start(); + EmbeddedActiveMQ embeddedActiveMQ = new EmbeddedActiveMQ(); + embeddedActiveMQ.setConfigResourcePath(brokerXML.toUri().toString()); + embeddedActiveMQ.start(); final ReusableLatch latch = new ReusableLatch(1); @@ -67,23 +68,23 @@ public void run() { } }; - embeddedJMS.getActiveMQServer().getReloadManager().setTick(tick); + embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); try { latch.await(10, TimeUnit.SECONDS); - Assert.assertEquals("DLQ", embeddedJMS.getActiveMQServer().getAddressSettingsRepository().getMatch("jms").getDeadLetterAddress().toString()); - Assert.assertEquals("ExpiryQueue", embeddedJMS.getActiveMQServer().getAddressSettingsRepository().getMatch("jms").getExpiryAddress().toString()); + Assert.assertEquals("DLQ", embeddedActiveMQ.getActiveMQServer().getAddressSettingsRepository().getMatch("jms").getDeadLetterAddress().toString()); + Assert.assertEquals("ExpiryQueue", embeddedActiveMQ.getActiveMQServer().getAddressSettingsRepository().getMatch("jms").getExpiryAddress().toString()); Assert.assertFalse(tryConsume()); Files.copy(url2.openStream(), brokerXML, StandardCopyOption.REPLACE_EXISTING); brokerXML.toFile().setLastModified(System.currentTimeMillis() + 1000); latch.setCount(1); - embeddedJMS.getActiveMQServer().getReloadManager().setTick(tick); + embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); latch.await(10, TimeUnit.SECONDS); Assert.assertTrue(tryConsume()); - Assert.assertEquals("NewQueue", embeddedJMS.getActiveMQServer().getAddressSettingsRepository().getMatch("jms").getDeadLetterAddress().toString()); - Assert.assertEquals("NewQueue", embeddedJMS.getActiveMQServer().getAddressSettingsRepository().getMatch("jms").getExpiryAddress().toString()); + Assert.assertEquals("NewQueue", embeddedActiveMQ.getActiveMQServer().getAddressSettingsRepository().getMatch("jms").getDeadLetterAddress().toString()); + Assert.assertEquals("NewQueue", embeddedActiveMQ.getActiveMQServer().getAddressSettingsRepository().getMatch("jms").getExpiryAddress().toString()); ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); try (Connection connection = factory.createConnection()) { @@ -97,7 +98,7 @@ public void run() { } } finally { - embeddedJMS.stop(); + embeddedActiveMQ.stop(); } } @@ -206,9 +207,9 @@ public void testRedeployAddressQueue() throws Exception { URL url2 = RedeployTest.class.getClassLoader().getResource("reload-address-queues-updated.xml"); Files.copy(url1.openStream(), brokerXML); - EmbeddedJMS embeddedJMS = new EmbeddedJMS(); - embeddedJMS.setConfigResourcePath(brokerXML.toUri().toString()); - embeddedJMS.start(); + EmbeddedActiveMQ embeddedActiveMQ = new EmbeddedActiveMQ(); + embeddedActiveMQ.setConfigResourcePath(brokerXML.toUri().toString()); + embeddedActiveMQ.start(); final ReusableLatch latch = new ReusableLatch(1); @@ -219,49 +220,49 @@ public void run() { } }; - embeddedJMS.getActiveMQServer().getReloadManager().setTick(tick); + embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); try { latch.await(10, TimeUnit.SECONDS); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_address_removal_no_queue")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_address_removal")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_queue_removal")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_removal").contains("config_test_queue_removal_queue_1")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_removal").contains("config_test_queue_removal_queue_2")); - - Assert.assertNotNull(getAddressInfo(embeddedJMS, "permanent_test_address_removal")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "permanent_test_queue_removal")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "permanent_test_queue_removal").contains("permanent_test_queue_removal_queue_1")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "permanent_test_queue_removal").contains("permanent_test_queue_removal_queue_2")); - - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_queue_change")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_change").contains("config_test_queue_change_queue")); - Assert.assertEquals(10, getQueue(embeddedJMS, "config_test_queue_change_queue").getMaxConsumers()); - Assert.assertEquals(false, getQueue(embeddedJMS, "config_test_queue_change_queue").isPurgeOnNoConsumers()); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal_no_queue")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_removal")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_removal").contains("config_test_queue_removal_queue_1")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_removal").contains("config_test_queue_removal_queue_2")); + + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "permanent_test_address_removal")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "permanent_test_queue_removal")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "permanent_test_queue_removal").contains("permanent_test_queue_removal_queue_1")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "permanent_test_queue_removal").contains("permanent_test_queue_removal_queue_2")); + + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_change")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_change").contains("config_test_queue_change_queue")); + Assert.assertEquals(10, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").getMaxConsumers()); + Assert.assertEquals(false, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").isPurgeOnNoConsumers()); Files.copy(url2.openStream(), brokerXML, StandardCopyOption.REPLACE_EXISTING); brokerXML.toFile().setLastModified(System.currentTimeMillis() + 1000); latch.setCount(1); - embeddedJMS.getActiveMQServer().getReloadManager().setTick(tick); + embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); latch.await(10, TimeUnit.SECONDS); - Assert.assertNull(getAddressInfo(embeddedJMS, "config_test_address_removal_no_queue")); - Assert.assertNull(getAddressInfo(embeddedJMS, "config_test_address_removal")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_queue_removal")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_removal").contains("config_test_queue_removal_queue_1")); - Assert.assertFalse(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_removal").contains("config_test_queue_removal_queue_2")); - - Assert.assertNotNull(getAddressInfo(embeddedJMS, "permanent_test_address_removal")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "permanent_test_queue_removal")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "permanent_test_queue_removal").contains("permanent_test_queue_removal_queue_1")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "permanent_test_queue_removal").contains("permanent_test_queue_removal_queue_2")); - - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_queue_change")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_change").contains("config_test_queue_change_queue")); - Assert.assertEquals(1, getQueue(embeddedJMS, "config_test_queue_change_queue").getMaxConsumers()); - Assert.assertEquals(true, getQueue(embeddedJMS, "config_test_queue_change_queue").isPurgeOnNoConsumers()); + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal_no_queue")); + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_removal")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_removal").contains("config_test_queue_removal_queue_1")); + Assert.assertFalse(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_removal").contains("config_test_queue_removal_queue_2")); + + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "permanent_test_address_removal")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "permanent_test_queue_removal")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "permanent_test_queue_removal").contains("permanent_test_queue_removal_queue_1")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "permanent_test_queue_removal").contains("permanent_test_queue_removal_queue_2")); + + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_change")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_change").contains("config_test_queue_change_queue")); + Assert.assertEquals(1, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").getMaxConsumers()); + Assert.assertEquals(true, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").isPurgeOnNoConsumers()); } finally { - embeddedJMS.stop(); + embeddedActiveMQ.stop(); } } @@ -368,25 +369,25 @@ public void testRedeployStopAndRestart() throws Exception { } } - private AddressSettings getAddressSettings(EmbeddedJMS embeddedJMS, String address) { - return embeddedJMS.getActiveMQServer().getAddressSettingsRepository().getMatch(address); + private AddressSettings getAddressSettings(EmbeddedActiveMQ embeddedActiveMQ, String address) { + return embeddedActiveMQ.getActiveMQServer().getAddressSettingsRepository().getMatch(address); } - private Set getSecurityRoles(EmbeddedJMS embeddedJMS, String address) { - return embeddedJMS.getActiveMQServer().getSecurityRepository().getMatch(address); + private Set getSecurityRoles(EmbeddedActiveMQ embeddedActiveMQ, String address) { + return embeddedActiveMQ.getActiveMQServer().getSecurityRepository().getMatch(address); } - private AddressInfo getAddressInfo(EmbeddedJMS embeddedJMS, String address) { - return embeddedJMS.getActiveMQServer().getPostOffice().getAddressInfo(SimpleString.toSimpleString(address)); + private AddressInfo getAddressInfo(EmbeddedActiveMQ embeddedActiveMQ, String address) { + return embeddedActiveMQ.getActiveMQServer().getPostOffice().getAddressInfo(SimpleString.toSimpleString(address)); } - private org.apache.activemq.artemis.core.server.Queue getQueue(EmbeddedJMS embeddedJMS, String queueName) throws Exception { - QueueBinding queueBinding = (QueueBinding) embeddedJMS.getActiveMQServer().getPostOffice().getBinding(SimpleString.toSimpleString(queueName)); + private org.apache.activemq.artemis.core.server.Queue getQueue(EmbeddedActiveMQ embeddedActiveMQ, String queueName) throws Exception { + QueueBinding queueBinding = (QueueBinding) embeddedActiveMQ.getActiveMQServer().getPostOffice().getBinding(SimpleString.toSimpleString(queueName)); return queueBinding == null ? null : queueBinding.getQueue(); } - private List listQueuesNamesForAddress(EmbeddedJMS embeddedJMS, String address) throws Exception { - return embeddedJMS.getActiveMQServer().getPostOffice().listQueuesForAddress(SimpleString.toSimpleString(address)).stream().map( + private List listQueuesNamesForAddress(EmbeddedActiveMQ embeddedActiveMQ, String address) throws Exception { + return embeddedActiveMQ.getActiveMQServer().getPostOffice().listQueuesForAddress(SimpleString.toSimpleString(address)).stream().map( org.apache.activemq.artemis.core.server.Queue::getName).map(SimpleString::toString).collect(Collectors.toList()); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/RemoteConnectionStressTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/RemoteConnectionStressTest.java index a7775e4d52d..bcd2fe721c7 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/RemoteConnectionStressTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/RemoteConnectionStressTest.java @@ -21,18 +21,13 @@ import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; -import javax.management.MBeanServer; -import javax.management.MBeanServerFactory; import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.api.jms.JMSFactoryType; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; -import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Before; import org.junit.Test; @@ -43,25 +38,14 @@ public class RemoteConnectionStressTest extends ActiveMQTestBase { ActiveMQServer server; - MBeanServer mbeanServer; - JMSServerManagerImpl jmsServer; @Override @Before public void setUp() throws Exception { super.setUp(); - mbeanServer = MBeanServerFactory.createMBeanServer(); - - server = addServer(ActiveMQServers.newActiveMQServer(createDefaultNettyConfig(), mbeanServer, false)); - - InVMNamingContext namingContext = new InVMNamingContext(); - jmsServer = new JMSServerManagerImpl(server); - jmsServer.setRegistry(new JndiBindingRegistry(namingContext)); - - jmsServer.start(); - - jmsServer.createQueue(true, "SomeQueue", null, true, "/jms/SomeQueue"); + server = addServer(ActiveMQServers.newActiveMQServer(createDefaultNettyConfig(), false)); + server.start(); } @Test diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverListenerTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverListenerTest.java index 45742cc8e86..cec8e1b8fa3 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverListenerTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/cluster/JMSFailoverListenerTest.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.api.core.client.ClientSession; @@ -39,20 +40,15 @@ import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.ha.SharedStoreMasterPolicyConfiguration; import org.apache.activemq.artemis.core.config.ha.SharedStoreSlavePolicyConfiguration; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; import org.apache.activemq.artemis.core.remoting.impl.invm.TransportConstants; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.NodeManager; -import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.core.server.impl.InVMNodeManager; import org.apache.activemq.artemis.jms.client.ActiveMQConnection; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQSession; -import org.apache.activemq.artemis.jms.server.JMSServerManager; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.integration.jms.server.management.JMSUtil; -import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.tests.util.InVMNodeManagerServer; import org.apache.activemq.artemis.utils.RandomUtil; @@ -73,20 +69,12 @@ public class JMSFailoverListenerTest extends ActiveMQTestBase { // Attributes ---------------------------------------------------- - protected InVMNamingContext ctx1 = new InVMNamingContext(); - - protected InVMNamingContext ctx2 = new InVMNamingContext(); - protected Configuration backupConf; protected Configuration liveConf; - protected JMSServerManager liveJMSServer; - protected ActiveMQServer liveServer; - protected JMSServerManager backupJMSServer; - protected ActiveMQServer backupServer; protected Map backupParams = new HashMap<>(); @@ -281,26 +269,18 @@ protected void startServers() throws Exception { backupServer = addServer(new InVMNodeManagerServer(backupConf, nodeManager)); - backupJMSServer = new JMSServerManagerImpl(backupServer); - - backupJMSServer.setRegistry(new JndiBindingRegistry(ctx2)); - - backupJMSServer.getActiveMQServer().setIdentity("JMSBackup"); + backupServer.setIdentity("JMSBackup"); log.info("Starting backup"); - backupJMSServer.start(); + backupServer.start(); liveConf = createBasicConfig().setJournalDirectory(getJournalDir()).setBindingsDirectory(getBindingsDir()).addAcceptorConfiguration(liveAcceptortc).setJournalType(getDefaultJournalType()).setBindingsDirectory(getBindingsDir()).setJournalMinFiles(2).setJournalDirectory(getJournalDir()).setPagingDirectory(getPageDir()).setLargeMessagesDirectory(getLargeMessagesDir()).addConnectorConfiguration(livetc.getName(), livetc).setPersistenceEnabled(true).setHAPolicyConfiguration(new SharedStoreMasterPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(livetc.getName())); liveServer = addServer(new InVMNodeManagerServer(liveConf, nodeManager)); - liveJMSServer = new JMSServerManagerImpl(liveServer); - - liveJMSServer.setRegistry(new JndiBindingRegistry(ctx1)); - - liveJMSServer.getActiveMQServer().setIdentity("JMSLive"); + liveServer.setIdentity("JMSLive"); log.info("Starting life"); - liveJMSServer.start(); + liveServer.start(); JMSUtil.waitForServer(backupServer); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/connection/ExceptionListenerTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/connection/ExceptionListenerTest.java index 9b913089bf2..b37d8a6aecd 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/connection/ExceptionListenerTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/connection/ExceptionListenerTest.java @@ -28,14 +28,11 @@ import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.api.jms.JMSFactoryType; import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.jms.client.ActiveMQConnection; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQSession; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; -import org.apache.activemq.artemis.tests.integration.jms.server.management.NullInitialContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Assert; import org.junit.Before; @@ -48,22 +45,15 @@ public class ExceptionListenerTest extends ActiveMQTestBase { private ActiveMQServer server; - private JMSServerManagerImpl jmsServer; - private ActiveMQConnectionFactory cf; - private static final String Q_NAME = "ConnectionTestQueue"; - @Override @Before public void setUp() throws Exception { super.setUp(); server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig(), false)); - jmsServer = new JMSServerManagerImpl(server); - jmsServer.setRegistry(new JndiBindingRegistry(new NullInitialContext())); - jmsServer.start(); - jmsServer.createQueue(false, ExceptionListenerTest.Q_NAME, null, true, ExceptionListenerTest.Q_NAME); + server.start(); cf = ActiveMQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, new TransportConfiguration(INVM_CONNECTOR_FACTORY)); cf.setBlockOnDurableSend(true); cf.setPreAcknowledge(true); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/server/JMSServerStartStopTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/server/JMSServerStartStopTest.java index 1e20fb0354f..130f43fc8c6 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/server/JMSServerStartStopTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/server/JMSServerStartStopTest.java @@ -34,9 +34,6 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; -import org.apache.activemq.artemis.jms.server.JMSServerManager; -import org.apache.activemq.artemis.jms.server.config.impl.FileJMSConfiguration; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; @@ -49,7 +46,7 @@ public class JMSServerStartStopTest extends ActiveMQTestBase { private static final IntegrationTestLogger log = IntegrationTestLogger.LOGGER; - private JMSServerManager jmsServer; + private ActiveMQServer server; private Connection conn; @@ -60,18 +57,13 @@ public class JMSServerStartStopTest extends ActiveMQTestBase { @Before public void setUp() throws Exception { FileConfiguration fc = new FileConfiguration(); - FileJMSConfiguration fileConfiguration = new FileJMSConfiguration(); FileDeploymentManager deploymentManager = new FileDeploymentManager("server-start-stop-config1.xml"); deploymentManager.addDeployable(fc); - deploymentManager.addDeployable(fileConfiguration); deploymentManager.readConfiguration(); ActiveMQJAASSecurityManager sm = new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), new SecurityConfiguration()); - ActiveMQServer server = addServer(new ActiveMQServerImpl(fc, sm)); - - jmsServer = new JMSServerManagerImpl(server, fileConfiguration); - jmsServer.setRegistry(null); + server = addServer(new ActiveMQServerImpl(fc, sm)); } @Test @@ -81,7 +73,7 @@ public void testStopStart1() throws Exception { for (int j = 0; j < numMessages; j++) { JMSServerStartStopTest.log.info("Iteration " + j); - jmsServer.start(); + server.start(); ActiveMQConnectionFactory jbcf = createConnectionFactory(); @@ -104,11 +96,11 @@ public void testStopStart1() throws Exception { jbcf.close(); - jmsServer.stop(); + server.stop(); } } - jmsServer.start(); + server.start(); jbcf = createConnectionFactory(); @@ -141,7 +133,7 @@ public void testStopStart1() throws Exception { // https://jira.jboss.org/jira/browse/HORNETQ-315 @Test public void testCloseConnectionAfterServerIsShutdown() throws Exception { - jmsServer.start(); + server.start(); jbcf = createConnectionFactory(); @@ -151,7 +143,7 @@ public void testCloseConnectionAfterServerIsShutdown() throws Exception { conn = jbcf.createConnection(); - jmsServer.stop(); + server.stop(); conn.close(); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/OpenWireTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/OpenWireTestBase.java index 831887fd290..2f06a425238 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/OpenWireTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/OpenWireTestBase.java @@ -19,21 +19,15 @@ import javax.jms.ConnectionFactory; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Set; import org.apache.activemq.artemis.api.core.SimpleString; -import org.apache.activemq.artemis.api.core.TransportConfiguration; +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.core.config.Configuration; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; import org.apache.activemq.artemis.core.security.Role; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; -import org.apache.activemq.artemis.jms.server.config.ConnectionFactoryConfiguration; -import org.apache.activemq.artemis.jms.server.config.impl.ConnectionFactoryConfigurationImpl; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; @@ -50,7 +44,6 @@ public class OpenWireTestBase extends ActiveMQTestBase { protected ActiveMQServer server; - protected JMSServerManagerImpl jmsServer; protected boolean realStore = false; protected boolean enableSecurity = false; @@ -108,12 +101,9 @@ public void setUp() throws Exception { mbeanServer = MBeanServerFactory.createMBeanServer(); server.setMBeanServer(mbeanServer); addServer(server); - jmsServer = new JMSServerManagerImpl(server); - namingContext = new InVMNamingContext(); - jmsServer.setRegistry(new JndiBindingRegistry(namingContext)); - jmsServer.start(); + server.start(); - registerConnectionFactory(); + coreCf = ActiveMQJMSClient.createConnectionFactory("vm://0?reconnectAttempts=-1","cf"); System.out.println("debug: server started"); } @@ -121,31 +111,6 @@ public void setUp() throws Exception { protected void extraServerConfig(Configuration serverConfig) { } - protected void registerConnectionFactory() throws Exception { - List connectorConfigs = new ArrayList<>(); - connectorConfigs.add(new TransportConfiguration(INVM_CONNECTOR_FACTORY)); - - createCF(connectorConfigs, "/cf"); - - coreCf = (ConnectionFactory) namingContext.lookup("/cf"); - } - - protected void createCF(final List connectorConfigs, - final String... jndiBindings) throws Exception { - final int retryInterval = 1000; - final double retryIntervalMultiplier = 1.0; - final int reconnectAttempts = -1; - final int callTimeout = 30000; - List connectorNames = registerConnectors(server, connectorConfigs); - - String cfName = name.getMethodName(); - if (cfName == null) { - cfName = "cfOpenWire"; - } - ConnectionFactoryConfiguration configuration = new ConnectionFactoryConfigurationImpl().setName(cfName).setConnectorNames(connectorNames).setRetryInterval(retryInterval).setRetryIntervalMultiplier(retryIntervalMultiplier).setCallTimeout(callTimeout).setReconnectAttempts(reconnectAttempts); - jmsServer.createConnectionFactory(false, configuration, jndiBindings); - } - @Override @After public void tearDown() throws Exception { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/amq/ConnectionErrorSocketCloseTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/amq/ConnectionErrorSocketCloseTest.java index 1c58bcce3d0..8ac4987553e 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/amq/ConnectionErrorSocketCloseTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/amq/ConnectionErrorSocketCloseTest.java @@ -18,7 +18,6 @@ import javax.jms.Connection; -import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.tests.integration.openwire.BasicOpenWireTest; import org.apache.activemq.artemis.tests.util.Wait; import org.junit.Test; @@ -37,7 +36,7 @@ protected void createFactories() { @Test(timeout = 60000) public void testDuplicateClientIdCloseConnection() throws Exception { connection.start(); - Wait.waitFor(() -> getActiveMQServer().getRemotingService().getConnections().size() == 1, 10000, 500); + Wait.waitFor(() -> server.getRemotingService().getConnections().size() == 1, 10000, 500); try (Connection con = factory.createConnection()) { // Try and create second connection the second should fail because of a @@ -53,13 +52,7 @@ public void testDuplicateClientIdCloseConnection() throws Exception { // after 2 seconds the second connection should be terminated by the // broker because of the exception - assertTrue(Wait.waitFor(() -> getActiveMQServer().getRemotingService().getConnections().size() == 1, 10000, 500)); + assertTrue(Wait.waitFor(() -> server.getRemotingService().getConnections().size() == 1, 10000, 500)); } } - - @SuppressWarnings("deprecation") - private ActiveMQServer getActiveMQServer() { - return jmsServer.getActiveMQServer(); - } - } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/MultipleProducersPagingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/MultipleProducersPagingTest.java index ab183c293f7..367ae9f15c6 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/MultipleProducersPagingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/MultipleProducersPagingTest.java @@ -24,7 +24,6 @@ import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -36,16 +35,11 @@ import java.util.concurrent.atomic.AtomicLong; import org.apache.activemq.artemis.api.core.TransportConfiguration; -import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory; -import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory; +import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; -import org.apache.activemq.artemis.jms.server.config.JMSConfiguration; -import org.apache.activemq.artemis.jms.server.config.impl.ConnectionFactoryConfigurationImpl; -import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl; -import org.apache.activemq.artemis.jms.server.config.impl.JMSQueueConfigurationImpl; -import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.utils.ActiveMQThreadFactory; import org.junit.After; @@ -66,7 +60,7 @@ public class MultipleProducersPagingTest extends ActiveMQTestBase { private AtomicLong msgReceived; private AtomicLong msgSent; private final Set connections = new HashSet<>(); - private EmbeddedJMS jmsServer; + private ActiveMQServer server; private ConnectionFactory cf; private Queue queue; @@ -76,21 +70,18 @@ public void setUp() throws Exception { super.setUp(); executor = Executors.newCachedThreadPool(ActiveMQThreadFactory.defaultThreadFactory()); - AddressSettings addressSettings = new AddressSettings().setAddressFullMessagePolicy(AddressFullMessagePolicy.PAGE).setPageSizeBytes(50000).setMaxSizeBytes(404850); + server = createServer(createBasicConfig() + .setPersistenceEnabled(false) + .setAddressesSettings(Collections.singletonMap("#", new AddressSettings() + .setAddressFullMessagePolicy(AddressFullMessagePolicy.PAGE) + .setPageSizeBytes(50000) + .setMaxSizeBytes(404850))) + .setAcceptorConfigurations(Collections.singleton(new TransportConfiguration(NettyAcceptorFactory.class.getName())))); - Configuration config = createBasicConfig().setPersistenceEnabled(false).setAddressesSettings(Collections.singletonMap("#", addressSettings)).setAcceptorConfigurations(Collections.singleton(new TransportConfiguration(NettyAcceptorFactory.class.getName()))).setConnectorConfigurations(Collections.singletonMap("netty", new TransportConfiguration(NettyConnectorFactory.class.getName()))); + server.start(); - final JMSConfiguration jmsConfig = new JMSConfigurationImpl(); - jmsConfig.getConnectionFactoryConfigurations().add(new ConnectionFactoryConfigurationImpl().setName("cf").setConnectorNames(Arrays.asList("netty")).setBindings("/cf")); - jmsConfig.getQueueConfigurations().add(new JMSQueueConfigurationImpl().setName("simple").setSelector("").setDurable(false).setBindings("/queue/simple")); - - jmsServer = new EmbeddedJMS(); - jmsServer.setConfiguration(config); - jmsServer.setJmsConfiguration(jmsConfig); - jmsServer.start(); - - cf = (ConnectionFactory) jmsServer.lookup("/cf"); - queue = (Queue) jmsServer.lookup("/queue/simple"); + cf = ActiveMQJMSClient.createConnectionFactory("tcp://127.0.0.1:61616", "cf"); + queue = ActiveMQJMSClient.createQueue("simple"); barrierLatch = new CyclicBarrier(PRODUCERS + 1); runnersLatch = new CountDownLatch(PRODUCERS + 1); @@ -168,8 +159,8 @@ public void tearDown() throws Exception { conn.close(); } connections.clear(); - if (jmsServer != null) - jmsServer.stop(); + if (server != null) + server.stop(); super.tearDown(); } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/SyncSendTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/SyncSendTest.java index c4c221406b1..6a94f177224 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/SyncSendTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/SyncSendTest.java @@ -32,11 +32,12 @@ import java.util.Collection; import java.util.concurrent.TimeUnit; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.JournalType; import org.apache.activemq.artemis.jlibaio.LibaioContext; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.qpid.jms.JmsConnectionFactory; import org.junit.Assert; @@ -79,7 +80,6 @@ public SyncSendTest(String storage, String protocol) { } ActiveMQServer server; - JMSServerManagerImpl jms; @Override public void setUp() throws Exception { @@ -91,15 +91,13 @@ public void setUp() throws Exception { server = createServer(true, true); } - jms = new JMSServerManagerImpl(server); - if (storage.equals("libaio")) { server.getConfiguration().setJournalType(JournalType.ASYNCIO); } else { server.getConfiguration().setJournalType(JournalType.NIO); } - jms.start(); + server.start(); } private long getTimePerSync() throws Exception { @@ -154,7 +152,7 @@ public void testSendConsumeAudoACK() throws Exception { long recordTime = getTimePerSync(); - jms.createQueue(true, "queue", null, true, null); + server.createQueue(SimpleString.toSimpleString("queue"), RoutingType.ANYCAST, SimpleString.toSimpleString("queue"), null, true, false); ConnectionFactory factory = newCF(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/StompPluginTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/StompPluginTest.java index e426e3aa947..bb32407524f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/StompPluginTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/StompPluginTest.java @@ -62,9 +62,9 @@ import org.apache.activemq.artemis.core.persistence.OperationContext; import org.apache.activemq.artemis.core.protocol.stomp.Stomp; import org.apache.activemq.artemis.core.protocol.stomp.StompConnection; +import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ServerSession; import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerPlugin; -import org.apache.activemq.artemis.jms.server.JMSServerManager; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.spi.core.protocol.SessionCallback; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; @@ -118,10 +118,10 @@ public void tearDown() throws Exception { private final AtomicBoolean stompBeforeRemoveSession = new AtomicBoolean(); @Override - protected JMSServerManager createServer() throws Exception { - JMSServerManager server = super.createServer(); - server.getActiveMQServer().registerBrokerPlugin(verifier); - server.getActiveMQServer().registerBrokerPlugin(new ActiveMQServerPlugin() { + protected ActiveMQServer createServer() throws Exception { + ActiveMQServer server = super.createServer(); + server.registerBrokerPlugin(verifier); + server.registerBrokerPlugin(new ActiveMQServerPlugin() { @Override public void beforeCreateSession(String name, String username, int minLargeMessageSize, diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/spring/SpringIntegrationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/spring/SpringIntegrationTest.java index e8e5998ce0f..5fe43d9b16e 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/spring/SpringIntegrationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/spring/SpringIntegrationTest.java @@ -18,8 +18,8 @@ import java.util.concurrent.TimeUnit; +import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; -import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Assert; @@ -67,7 +67,7 @@ public void testSpring() throws Exception { } try { if (context != null) { - EmbeddedJMS jms = (EmbeddedJMS) context.getBean("EmbeddedJms"); + EmbeddedActiveMQ jms = (EmbeddedActiveMQ) context.getBean("EmbeddedActiveMQ"); jms.stop(); } } catch (Throwable ignored) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/FQQNStompTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/FQQNStompTest.java index 23774d7c689..ce727a95ea0 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/FQQNStompTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/FQQNStompTest.java @@ -45,7 +45,7 @@ public static Collection data() { public void setUp() throws Exception { super.setUp(); conn = StompClientConnectionFactory.createClientConnection(uri); - QueueQueryResult result = server.getActiveMQServer().queueQuery(new SimpleString(getQueueName())); + QueueQueryResult result = server.queueQuery(new SimpleString(getQueueName())); assertTrue(result.isExists()); System.out.println("address: " + result.getAddress() + " queue " + result.getName()); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompConnectionCleanupTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompConnectionCleanupTest.java index ac89c1d6470..7955703fe92 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompConnectionCleanupTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompConnectionCleanupTest.java @@ -19,7 +19,7 @@ import javax.jms.Message; import javax.jms.MessageConsumer; -import org.apache.activemq.artemis.jms.server.JMSServerManager; +import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.tests.integration.stomp.util.ClientStompFrame; import org.junit.Test; @@ -40,9 +40,9 @@ public void testConnectionCleanupWithTopicSubscription() throws Exception { long start = System.currentTimeMillis(); while (true) { - int connCount = server.getActiveMQServer().getRemotingService().getConnections().size(); + int connCount = server.getRemotingService().getConnections().size(); - int sessionCount = server.getActiveMQServer().getSessions().size(); + int sessionCount = server.getSessions().size(); // All connections and sessions should be timed out including STOMP + JMS connection @@ -77,9 +77,9 @@ public void testConnectionCleanup() throws Exception { long start = System.currentTimeMillis(); while (true) { - int connCount = server.getActiveMQServer().getRemotingService().getConnections().size(); + int connCount = server.getRemotingService().getConnections().size(); - int sessionCount = server.getActiveMQServer().getSessions().size(); + int sessionCount = server.getSessions().size(); // All connections and sessions should be timed out including STOMP + JMS connection @@ -124,10 +124,10 @@ public void testConnectionNotCleanedUp() throws Exception { } @Override - protected JMSServerManager createServer() throws Exception { - JMSServerManager s = super.createServer(); + protected ActiveMQServer createServer() throws Exception { + ActiveMQServer s = super.createServer(); - s.getActiveMQServer().getConfiguration().setConnectionTTLOverride(CONNECTION_TTL); + s.getConfiguration().setConnectionTTLOverride(CONNECTION_TTL); return s; } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java index 5c6eefec85a..16ffca243cb 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java @@ -49,7 +49,6 @@ import org.apache.activemq.artemis.core.postoffice.impl.LocalQueueBinding; import org.apache.activemq.artemis.core.protocol.stomp.Stomp; import org.apache.activemq.artemis.core.protocol.stomp.StompProtocolManagerFactory; -import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.Queue; import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; import org.apache.activemq.artemis.core.server.impl.AddressInfo; @@ -108,7 +107,7 @@ public void testConnectionTTL() throws Exception { URI uri = createStompClientUri(scheme, hostname, port); - server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://127.0.0.1:" + port + "?connectionTtl=1000").start(); + server.getRemotingService().createAcceptor("test", "tcp://127.0.0.1:" + port + "?connectionTtl=1000").start(); StompClientConnection conn = StompClientConnectionFactory.createClientConnection(uri); conn.connect("brianm", "wombats"); @@ -161,9 +160,9 @@ public void onMessage(Message arg0) { } }); - ((ActiveMQServerImpl) server.getActiveMQServer()).getMonitor() - .setMaxUsage(0) - .tick(); + ((ActiveMQServerImpl) server).getMonitor() + .setMaxUsage(0) + .tick(); // Connection should be closed by broker when disk is full and attempt to send Exception e = null; @@ -303,9 +302,9 @@ public void sendMessageToNonExistentQueue(String queuePrefix, String queue, Rout Assert.assertTrue(Math.abs(tnow - tmsg) < 1500); // closing the consumer here should trigger auto-deletion - assertNotNull(server.getActiveMQServer().getPostOffice().getBinding(new SimpleString(queue))); + assertNotNull(server.getPostOffice().getBinding(new SimpleString(queue))); consumer.close(); - assertNull(server.getActiveMQServer().getPostOffice().getBinding(new SimpleString(queue))); + assertNull(server.getPostOffice().getBinding(new SimpleString(queue))); } @Test @@ -316,7 +315,7 @@ public void testSendMessageToNonExistentQueue() throws Exception { @Test public void testSendMessageToNonExistentQueueUsingExplicitDefaultRouting() throws Exception { String nonExistentQueue = RandomUtil.randomString(); - server.getActiveMQServer().getAddressSettingsRepository().addMatch(nonExistentQueue, new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST).setDefaultQueueRoutingType(RoutingType.ANYCAST)); + server.getAddressSettingsRepository().addMatch(nonExistentQueue, new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST).setDefaultQueueRoutingType(RoutingType.ANYCAST)); sendMessageToNonExistentQueue(getQueuePrefix(), nonExistentQueue, null); } @@ -341,12 +340,12 @@ public void sendMessageToNonExistentTopic(String topicPrefix, String topic, Rout long tmsg = message.getJMSTimestamp(); Assert.assertTrue(Math.abs(tnow - tmsg) < 1500); - assertNotNull(server.getActiveMQServer().getAddressInfo(new SimpleString(topic))); + assertNotNull(server.getAddressInfo(new SimpleString(topic))); // closing the consumer here should trigger auto-deletion of the subscription queue and address consumer.close(); Thread.sleep(200); - assertNull(server.getActiveMQServer().getAddressInfo(new SimpleString(topic))); + assertNull(server.getAddressInfo(new SimpleString(topic))); } @Test @@ -357,7 +356,7 @@ public void testSendMessageToNonExistentTopic() throws Exception { @Test public void testSendMessageToNonExistentTopicUsingExplicitDefaultRouting() throws Exception { String nonExistentTopic = RandomUtil.randomString(); - server.getActiveMQServer().getAddressSettingsRepository().addMatch(nonExistentTopic, new AddressSettings().setDefaultAddressRoutingType(RoutingType.MULTICAST).setDefaultQueueRoutingType(RoutingType.MULTICAST)); + server.getAddressSettingsRepository().addMatch(nonExistentTopic, new AddressSettings().setDefaultAddressRoutingType(RoutingType.MULTICAST).setDefaultQueueRoutingType(RoutingType.MULTICAST)); sendMessageToNonExistentTopic(getTopicPrefix(), nonExistentTopic, null); } @@ -1122,7 +1121,7 @@ public void testTransactionRollback() throws Exception { @Test public void testSubscribeToTopic() throws Exception { - final int baselineQueueCount = server.getActiveMQServer().getActiveMQServerControl().getQueueNames().length; + final int baselineQueueCount = server.getActiveMQServerControl().getQueueNames().length; conn.connect(defUser, defPass); @@ -1132,7 +1131,7 @@ public void testSubscribeToTopic() throws Exception { @Override public boolean isSatisfied() throws Exception { - int length = server.getActiveMQServer().getActiveMQServerControl().getQueueNames().length; + int length = server.getActiveMQServerControl().getQueueNames().length; if (length - baselineQueueCount == 1) { return true; } else { @@ -1157,14 +1156,14 @@ public boolean isSatisfied() throws Exception { log.info("Received frame: " + frame); Assert.assertNull("No message should have been received since subscription was removed", frame); - assertEquals("Subscription queue should be deleted", 0, server.getActiveMQServer().getActiveMQServerControl().getQueueNames().length - baselineQueueCount); + assertEquals("Subscription queue should be deleted", 0, server.getActiveMQServerControl().getQueueNames().length - baselineQueueCount); conn.disconnect(); } @Test public void testSubscribeToQueue() throws Exception { - final int baselineQueueCount = server.getActiveMQServer().getActiveMQServerControl().getQueueNames().length; + final int baselineQueueCount = server.getActiveMQServerControl().getQueueNames().length; conn.connect(defUser, defPass); subscribe(conn, null, null, null, true); @@ -1172,7 +1171,7 @@ public void testSubscribeToQueue() throws Exception { assertFalse("Queue should not be created here", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisfied() throws Exception { - if (server.getActiveMQServer().getActiveMQServerControl().getQueueNames().length - baselineQueueCount == 1) { + if (server.getActiveMQServerControl().getQueueNames().length - baselineQueueCount == 1) { return true; } else { return false; @@ -1195,7 +1194,7 @@ public boolean isSatisfied() throws Exception { log.info("Received frame: " + frame); Assert.assertNull("No message should have been received since subscription was removed", frame); - assertEquals("Subscription queue should not be deleted", baselineQueueCount, server.getActiveMQServer().getActiveMQServerControl().getQueueNames().length); + assertEquals("Subscription queue should not be deleted", baselineQueueCount, server.getActiveMQServerControl().getQueueNames().length); conn.disconnect(); } @@ -1214,9 +1213,9 @@ public void testSubscribeToNonExistentQueue() throws Exception { Assert.assertEquals(getQueuePrefix() + nonExistentQueue, frame.getHeader(Stomp.Headers.Send.DESTINATION)); Assert.assertEquals(getName(), frame.getBody()); - assertNotNull(server.getActiveMQServer().getPostOffice().getBinding(new SimpleString(nonExistentQueue))); + assertNotNull(server.getPostOffice().getBinding(new SimpleString(nonExistentQueue))); - final Queue subscription = ((LocalQueueBinding) server.getActiveMQServer().getPostOffice().getBinding(new SimpleString(nonExistentQueue))).getQueue(); + final Queue subscription = ((LocalQueueBinding) server.getPostOffice().getBinding(new SimpleString(nonExistentQueue))).getQueue(); assertTrue(Wait.waitFor(new Wait.Condition() { @Override @@ -1230,7 +1229,7 @@ public boolean isSatisfied() throws Exception { unsubscribe(conn, null, getQueuePrefix() + nonExistentQueue, true, false); - assertNull(server.getActiveMQServer().getPostOffice().getBinding(new SimpleString(nonExistentQueue))); + assertNull(server.getPostOffice().getBinding(new SimpleString(nonExistentQueue))); sendJmsMessage(getName(), ActiveMQJMSClient.createQueue(nonExistentQueue)); @@ -1330,7 +1329,7 @@ public void testDurableUnSubscribe() throws Exception { conn.disconnect(); Thread.sleep(500); - assertNotNull(server.getActiveMQServer().locateQueue(SimpleString.toSimpleString("myclientid." + getName()))); + assertNotNull(server.locateQueue(SimpleString.toSimpleString("myclientid." + getName()))); conn.destroy(); conn = StompClientConnectionFactory.createClientConnection(uri); @@ -1340,13 +1339,13 @@ public void testDurableUnSubscribe() throws Exception { conn.disconnect(); Thread.sleep(500); - assertNull(server.getActiveMQServer().locateQueue(SimpleString.toSimpleString("myclientid." + getName()))); + assertNull(server.locateQueue(SimpleString.toSimpleString("myclientid." + getName()))); } @Test public void testDurableUnSubscribeWithoutDurableSubName() throws Exception { - server.getActiveMQServer().getConfiguration().getWildcardConfiguration().setDelimiter('/'); - server.getActiveMQServer().getAddressSettingsRepository().addMatch("/topic/#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.MULTICAST).setDefaultQueueRoutingType(RoutingType.MULTICAST)); + server.getConfiguration().getWildcardConfiguration().setDelimiter('/'); + server.getAddressSettingsRepository().addMatch("/topic/#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.MULTICAST).setDefaultQueueRoutingType(RoutingType.MULTICAST)); conn.connect(defUser, defPass, "myclientid"); String subId = UUID.randomUUID().toString(); String durableSubName = UUID.randomUUID().toString(); @@ -1361,7 +1360,7 @@ public void testDurableUnSubscribeWithoutDurableSubName() throws Exception { frame = conn.sendFrame(frame); assertEquals(receipt, frame.getHeader(Stomp.Headers.Response.RECEIPT_ID)); - assertTrue(Wait.waitFor(() -> server.getActiveMQServer().locateQueue(SimpleString.toSimpleString("myclientid." + durableSubName)) != null, 2000, 100)); + assertTrue(Wait.waitFor(() -> server.locateQueue(SimpleString.toSimpleString("myclientid." + durableSubName)) != null, 2000, 100)); receipt = UUID.randomUUID().toString(); frame = conn.createFrame(Stomp.Commands.UNSUBSCRIBE) @@ -1374,7 +1373,7 @@ public void testDurableUnSubscribeWithoutDurableSubName() throws Exception { conn.disconnect(); // make sure the durable subscription queue is still there - assertTrue(Wait.waitFor(() -> server.getActiveMQServer().locateQueue(SimpleString.toSimpleString("myclientid." + durableSubName)) != null, 2000, 100)); + assertTrue(Wait.waitFor(() -> server.locateQueue(SimpleString.toSimpleString("myclientid." + durableSubName)) != null, 2000, 100)); } @Test @@ -1384,7 +1383,7 @@ public void testDurableUnSubscribeLegacySubscriptionHeader() throws Exception { conn.disconnect(); Thread.sleep(500); - assertNotNull(server.getActiveMQServer().locateQueue(SimpleString.toSimpleString("myclientid." + getName()))); + assertNotNull(server.locateQueue(SimpleString.toSimpleString("myclientid." + getName()))); conn.destroy(); conn = StompClientConnectionFactory.createClientConnection(uri); @@ -1394,7 +1393,7 @@ public void testDurableUnSubscribeLegacySubscriptionHeader() throws Exception { conn.disconnect(); Thread.sleep(500); - assertNull(server.getActiveMQServer().locateQueue(SimpleString.toSimpleString("myclientid." + getName()))); + assertNull(server.locateQueue(SimpleString.toSimpleString("myclientid." + getName()))); } @Test @@ -1578,7 +1577,7 @@ public void testPrefix(final String prefix, final RoutingType routingType, final final String PREFIXED_ADDRESS = prefix + ADDRESS; String param = routingType.toString(); String urlParam = param.toLowerCase() + "Prefix"; - server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://" + hostname + ":" + port + "?protocols=" + StompProtocolManagerFactory.STOMP_PROTOCOL_NAME + "&" + urlParam + "=" + prefix).start(); + server.getRemotingService().createAcceptor("test", "tcp://" + hostname + ":" + port + "?protocols=" + StompProtocolManagerFactory.STOMP_PROTOCOL_NAME + "&" + urlParam + "=" + prefix).start(); StompClientConnection conn = StompClientConnectionFactory.createClientConnection(uri); conn.connect(defUser, defPass); @@ -1597,7 +1596,7 @@ public void testPrefix(final String prefix, final RoutingType routingType, final assertEquals(uuid, frame.getHeader(Stomp.Headers.Response.RECEIPT_ID)); } - AddressInfo addressInfo = server.getActiveMQServer().getAddressInfo(SimpleString.toSimpleString(ADDRESS)); + AddressInfo addressInfo = server.getAddressInfo(SimpleString.toSimpleString(ADDRESS)); assertNotNull("No address was created with the name " + ADDRESS, addressInfo); Set routingTypes = new HashSet<>(); @@ -1619,7 +1618,7 @@ public void testPrefixedAutoCreatedAnycastAndMulticastWithSameName() throws Exce URI uri = createStompClientUri(scheme, hostname, port); final String ADDRESS = UUID.randomUUID().toString(); - server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://" + hostname + ":" + port + "?protocols=STOMP&anycastPrefix=/queue/&multicastPrefix=/topic/").start(); + server.getRemotingService().createAcceptor("test", "tcp://" + hostname + ":" + port + "?protocols=STOMP&anycastPrefix=/queue/&multicastPrefix=/topic/").start(); StompClientConnection conn = StompClientConnectionFactory.createClientConnection(uri); conn.connect(defUser, defPass); @@ -1631,16 +1630,16 @@ public void testPrefixedAutoCreatedAnycastAndMulticastWithSameName() throws Exce frame = conn.sendFrame(frame); assertEquals(uuid, frame.getHeader(Stomp.Headers.Response.RECEIPT_ID)); - AddressInfo addressInfo = server.getActiveMQServer().getAddressInfo(SimpleString.toSimpleString(ADDRESS)); + AddressInfo addressInfo = server.getAddressInfo(SimpleString.toSimpleString(ADDRESS)); assertNotNull("No address was created with the name " + ADDRESS, addressInfo); assertTrue(addressInfo.getRoutingTypes().contains(RoutingType.ANYCAST)); assertFalse(addressInfo.getRoutingTypes().contains(RoutingType.MULTICAST)); - assertNotNull(server.getActiveMQServer().locateQueue(SimpleString.toSimpleString(ADDRESS))); + assertNotNull(server.locateQueue(SimpleString.toSimpleString(ADDRESS))); // sending a MULTICAST message should alter the address to support MULTICAST frame = send(conn, "/topic/" + ADDRESS, null, "Hello World 1", true); assertFalse(frame.getCommand().equals("ERROR")); - addressInfo = server.getActiveMQServer().getAddressInfo(SimpleString.toSimpleString(ADDRESS)); + addressInfo = server.getAddressInfo(SimpleString.toSimpleString(ADDRESS)); assertTrue(addressInfo.getRoutingTypes().contains(RoutingType.ANYCAST)); assertTrue(addressInfo.getRoutingTypes().contains(RoutingType.MULTICAST)); @@ -1699,7 +1698,7 @@ public void testPrefixedAutoCreatedMulticastAndAnycastWithSameName() throws Exce URI uri = createStompClientUri(scheme, hostname, port); final String ADDRESS = UUID.randomUUID().toString(); - server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://" + hostname + ":" + port + "?protocols=STOMP&anycastPrefix=/queue/&multicastPrefix=/topic/").start(); + server.getRemotingService().createAcceptor("test", "tcp://" + hostname + ":" + port + "?protocols=STOMP&anycastPrefix=/queue/&multicastPrefix=/topic/").start(); StompClientConnection conn = StompClientConnectionFactory.createClientConnection(uri); conn.connect(defUser, defPass); @@ -1711,7 +1710,7 @@ public void testPrefixedAutoCreatedMulticastAndAnycastWithSameName() throws Exce frame = conn.sendFrame(frame); assertEquals(uuid, frame.getHeader(Stomp.Headers.Response.RECEIPT_ID)); - AddressInfo addressInfo = server.getActiveMQServer().getAddressInfo(SimpleString.toSimpleString(ADDRESS)); + AddressInfo addressInfo = server.getAddressInfo(SimpleString.toSimpleString(ADDRESS)); assertNotNull("No address was created with the name " + ADDRESS, addressInfo); assertTrue(addressInfo.getRoutingTypes().contains(RoutingType.MULTICAST)); assertFalse(addressInfo.getRoutingTypes().contains(RoutingType.ANYCAST)); @@ -1719,10 +1718,10 @@ public void testPrefixedAutoCreatedMulticastAndAnycastWithSameName() throws Exce // sending an ANYCAST message should alter the address to support ANYCAST and create an ANYCAST queue frame = send(conn, "/queue/" + ADDRESS, null, "Hello World 1", true); assertFalse(frame.getCommand().equals("ERROR")); - addressInfo = server.getActiveMQServer().getAddressInfo(SimpleString.toSimpleString(ADDRESS)); + addressInfo = server.getAddressInfo(SimpleString.toSimpleString(ADDRESS)); assertTrue(addressInfo.getRoutingTypes().contains(RoutingType.ANYCAST)); assertTrue(addressInfo.getRoutingTypes().contains(RoutingType.MULTICAST)); - assertNotNull(server.getActiveMQServer().locateQueue(SimpleString.toSimpleString(ADDRESS))); + assertNotNull(server.locateQueue(SimpleString.toSimpleString(ADDRESS))); // however, no message should be routed to the MULTICAST queue frame = conn.receiveFrame(1000); @@ -1792,7 +1791,7 @@ public void testPrefixedSendAndRecieve(final String prefix, RoutingType routingT final String ADDRESS = UUID.randomUUID().toString(); final String PREFIXED_ADDRESS = prefix + ADDRESS; String urlParam = routingType.toString().toLowerCase() + "Prefix"; - server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://" + hostname + ":" + port + "?protocols=" + StompProtocolManagerFactory.STOMP_PROTOCOL_NAME + "&" + urlParam + "=" + prefix).start(); + server.getRemotingService().createAcceptor("test", "tcp://" + hostname + ":" + port + "?protocols=" + StompProtocolManagerFactory.STOMP_PROTOCOL_NAME + "&" + urlParam + "=" + prefix).start(); StompClientConnection conn = StompClientConnectionFactory.createClientConnection(uri); conn.connect(defUser, defPass); String uuid = UUID.randomUUID().toString(); @@ -1817,13 +1816,13 @@ public void testPrefixedSendAndRecieve(final String prefix, RoutingType routingT @Test public void testMulticastOperationsOnAnycastAddress() throws Exception { - server.getActiveMQServer().getAddressSettingsRepository().addMatch("#", new AddressSettings().setAutoCreateAddresses(false).setAutoCreateQueues(false)); + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setAutoCreateAddresses(false).setAutoCreateQueues(false)); testRoutingSemantics(RoutingType.MULTICAST.toString(), getQueuePrefix() + getQueueName()); } @Test public void testAnycastOperationsOnMulticastAddress() throws Exception { - server.getActiveMQServer().getAddressSettingsRepository().addMatch("#", new AddressSettings().setAutoCreateAddresses(false).setAutoCreateQueues(false)); + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setAutoCreateAddresses(false).setAutoCreateQueues(false)); testRoutingSemantics(RoutingType.ANYCAST.toString(), getTopicPrefix() + getTopicName()); } @@ -1853,7 +1852,7 @@ public void testRoutingSemantics(String routingType, String destination) throws @Test public void testGetManagementAttributeFromStomp() throws Exception { - server.getActiveMQServer().getAddressSettingsRepository().addMatch("#", new AddressSettings().setAutoCreateAddresses(false).setAutoCreateQueues(false)); + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setAutoCreateAddresses(false).setAutoCreateQueues(false)); conn.connect(defUser, defPass); subscribe(conn, null); @@ -1916,8 +1915,7 @@ public void testAnycastMessageRoutingExclusivity() throws Exception { final String queueB = "queueB"; final String queueC = "queueC"; - ActiveMQServer activeMQServer = server.getActiveMQServer(); - ActiveMQServerControl serverControl = server.getActiveMQServer().getActiveMQServerControl(); + ActiveMQServerControl serverControl = server.getActiveMQServerControl(); serverControl.createAddress(addressA, RoutingType.ANYCAST.toString() + "," + RoutingType.MULTICAST.toString()); serverControl.createQueue(addressA, queueA, RoutingType.ANYCAST.toString()); serverControl.createQueue(addressA, queueB, RoutingType.ANYCAST.toString()); @@ -1925,8 +1923,8 @@ public void testAnycastMessageRoutingExclusivity() throws Exception { send(conn, addressA, null, "Hello World!", true, RoutingType.ANYCAST); - assertTrue(Wait.waitFor(() -> activeMQServer.locateQueue(SimpleString.toSimpleString(queueA)).getMessageCount() + activeMQServer.locateQueue(SimpleString.toSimpleString(queueB)).getMessageCount() == 1, 2000, 100)); - assertTrue(Wait.waitFor(() -> activeMQServer.locateQueue(SimpleString.toSimpleString(queueC)).getMessageCount() == 0, 2000, 100)); + assertTrue(Wait.waitFor(() -> server.locateQueue(SimpleString.toSimpleString(queueA)).getMessageCount() + server.locateQueue(SimpleString.toSimpleString(queueB)).getMessageCount() == 1, 2000, 100)); + assertTrue(Wait.waitFor(() -> server.locateQueue(SimpleString.toSimpleString(queueC)).getMessageCount() == 0, 2000, 100)); } @Test @@ -1938,8 +1936,7 @@ public void testMulticastMessageRoutingExclusivity() throws Exception { final String queueB = "queueB"; final String queueC = "queueC"; - ActiveMQServer activeMQServer = server.getActiveMQServer(); - ActiveMQServerControl serverControl = server.getActiveMQServer().getActiveMQServerControl(); + ActiveMQServerControl serverControl = server.getActiveMQServerControl(); serverControl.createAddress(addressA, RoutingType.ANYCAST.toString() + "," + RoutingType.MULTICAST.toString()); serverControl.createQueue(addressA, queueA, RoutingType.ANYCAST.toString()); serverControl.createQueue(addressA, queueB, RoutingType.MULTICAST.toString()); @@ -1947,8 +1944,8 @@ public void testMulticastMessageRoutingExclusivity() throws Exception { send(conn, addressA, null, "Hello World!", true, RoutingType.MULTICAST); - assertTrue(Wait.waitFor(() -> activeMQServer.locateQueue(SimpleString.toSimpleString(queueA)).getMessageCount() == 0, 2000, 100)); - assertTrue(Wait.waitFor(() -> activeMQServer.locateQueue(SimpleString.toSimpleString(queueC)).getMessageCount() + activeMQServer.locateQueue(SimpleString.toSimpleString(queueB)).getMessageCount() == 2, 2000, 100)); + assertTrue(Wait.waitFor(() -> server.locateQueue(SimpleString.toSimpleString(queueA)).getMessageCount() == 0, 2000, 100)); + assertTrue(Wait.waitFor(() -> server.locateQueue(SimpleString.toSimpleString(queueC)).getMessageCount() + server.locateQueue(SimpleString.toSimpleString(queueB)).getMessageCount() == 2, 2000, 100)); } @Test @@ -1961,8 +1958,7 @@ public void testAmbiguousMessageRouting() throws Exception { final String queueC = "queueC"; final String queueD = "queueD"; - ActiveMQServer activeMQServer = server.getActiveMQServer(); - ActiveMQServerControl serverControl = server.getActiveMQServer().getActiveMQServerControl(); + ActiveMQServerControl serverControl = server.getActiveMQServerControl(); serverControl.createAddress(addressA, RoutingType.ANYCAST.toString() + "," + RoutingType.MULTICAST.toString()); serverControl.createQueue(addressA, queueA, RoutingType.ANYCAST.toString()); serverControl.createQueue(addressA, queueB, RoutingType.ANYCAST.toString()); @@ -1971,8 +1967,8 @@ public void testAmbiguousMessageRouting() throws Exception { send(conn, addressA, null, "Hello World!", true); - assertTrue(Wait.waitFor(() -> activeMQServer.locateQueue(SimpleString.toSimpleString(queueA)).getMessageCount() + activeMQServer.locateQueue(SimpleString.toSimpleString(queueB)).getMessageCount() == 1, 2000, 100)); - assertTrue(Wait.waitFor(() -> activeMQServer.locateQueue(SimpleString.toSimpleString(queueC)).getMessageCount() + activeMQServer.locateQueue(SimpleString.toSimpleString(queueD)).getMessageCount() == 2, 2000, 100)); + assertTrue(Wait.waitFor(() -> server.locateQueue(SimpleString.toSimpleString(queueA)).getMessageCount() + server.locateQueue(SimpleString.toSimpleString(queueB)).getMessageCount() == 1, 2000, 100)); + assertTrue(Wait.waitFor(() -> server.locateQueue(SimpleString.toSimpleString(queueC)).getMessageCount() + server.locateQueue(SimpleString.toSimpleString(queueD)).getMessageCount() == 2, 2000, 100)); } @Test @@ -1982,21 +1978,19 @@ public void testAutoCreatedAnycastAddress() throws Exception { String queueName = UUID.randomUUID().toString(); SimpleString simpleQueueName = SimpleString.toSimpleString(queueName); - ActiveMQServer activeMQServer = server.getActiveMQServer(); + Assert.assertNull(server.getAddressInfo(simpleQueueName)); + Assert.assertNull(server.locateQueue(simpleQueueName)); - Assert.assertNull(activeMQServer.getAddressInfo(simpleQueueName)); - Assert.assertNull(activeMQServer.locateQueue(simpleQueueName)); - - activeMQServer.getAddressSettingsRepository().addMatch(queueName, new AddressSettings() + server.getAddressSettingsRepository().addMatch(queueName, new AddressSettings() .setDefaultAddressRoutingType(RoutingType.ANYCAST) .setDefaultQueueRoutingType(RoutingType.ANYCAST) ); send(conn, queueName, null, "Hello ANYCAST"); - assertTrue("Address and queue should be created now", Wait.waitFor(() -> (activeMQServer.getAddressInfo(simpleQueueName) != null) && (activeMQServer.locateQueue(simpleQueueName) != null), 2000, 200)); - assertTrue(activeMQServer.getAddressInfo(simpleQueueName).getRoutingTypes().contains(RoutingType.ANYCAST)); - assertEquals(RoutingType.ANYCAST, activeMQServer.locateQueue(simpleQueueName).getRoutingType()); + assertTrue("Address and queue should be created now", Wait.waitFor(() -> (server.getAddressInfo(simpleQueueName) != null) && (server.locateQueue(simpleQueueName) != null), 2000, 200)); + assertTrue(server.getAddressInfo(simpleQueueName).getRoutingTypes().contains(RoutingType.ANYCAST)); + assertEquals(RoutingType.ANYCAST, server.locateQueue(simpleQueueName).getRoutingType()); } @Test @@ -2006,15 +2000,13 @@ public void testAutoCreatedMulticastAddress() throws Exception { String queueName = UUID.randomUUID().toString(); SimpleString simpleQueueName = SimpleString.toSimpleString(queueName); - ActiveMQServer activeMQServer = server.getActiveMQServer(); - - Assert.assertNull(activeMQServer.getAddressInfo(simpleQueueName)); - Assert.assertNull(activeMQServer.locateQueue(simpleQueueName)); + Assert.assertNull(server.getAddressInfo(simpleQueueName)); + Assert.assertNull(server.locateQueue(simpleQueueName)); send(conn, queueName, null, "Hello MULTICAST"); - assertTrue("Address should be created now", Wait.waitFor(() -> (activeMQServer.getAddressInfo(simpleQueueName) != null), 2000, 200)); - assertTrue(activeMQServer.getAddressInfo(simpleQueueName).getRoutingTypes().contains(RoutingType.MULTICAST)); - Assert.assertNull(activeMQServer.locateQueue(simpleQueueName)); + assertTrue("Address should be created now", Wait.waitFor(() -> (server.getAddressInfo(simpleQueueName) != null), 2000, 200)); + assertTrue(server.getAddressInfo(simpleQueueName).getRoutingTypes().contains(RoutingType.MULTICAST)); + Assert.assertNull(server.locateQueue(simpleQueueName)); } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestBase.java index 66a55fc7cdc..58b8336c5db 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestBase.java @@ -40,10 +40,11 @@ import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.core.config.CoreAddressConfiguration; +import org.apache.activemq.artemis.core.config.CoreQueueConfiguration; import org.apache.activemq.artemis.core.protocol.mqtt.MQTTProtocolManagerFactory; import org.apache.activemq.artemis.core.protocol.stomp.Stomp; import org.apache.activemq.artemis.core.protocol.stomp.StompProtocolManagerFactory; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; import org.apache.activemq.artemis.core.remoting.impl.invm.InVMAcceptorFactory; import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory; import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory; @@ -53,17 +54,10 @@ import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQJMSConnectionFactory; -import org.apache.activemq.artemis.jms.server.JMSServerManager; -import org.apache.activemq.artemis.jms.server.config.JMSConfiguration; -import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl; -import org.apache.activemq.artemis.jms.server.config.impl.JMSQueueConfigurationImpl; -import org.apache.activemq.artemis.jms.server.config.impl.TopicConfigurationImpl; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.integration.stomp.util.ClientStompFrame; import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnection; -import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.After; import org.junit.Before; @@ -97,7 +91,7 @@ public static Collection data() { protected Topic topic; - protected JMSServerManager server; + protected ActiveMQServer server; protected String defUser = "brianm"; @@ -143,7 +137,7 @@ public void setUp() throws Exception { server = createServer(); server.start(); - waitForServerToStart(server.getActiveMQServer()); + waitForServerToStart(server); connectionFactory = createConnectionFactory(); @@ -174,7 +168,7 @@ public void tearDown() throws Exception { * @return * @throws Exception */ - protected JMSServerManager createServer() throws Exception { + protected ActiveMQServer createServer() throws Exception { Map params = new HashMap<>(); params.put(TransportConstants.PROTOCOLS_PROP_NAME, StompProtocolManagerFactory.STOMP_PROTOCOL_NAME + "," + MQTTProtocolManagerFactory.MQTT_PROTOCOL_NAME); params.put(TransportConstants.PORT_PROP_NAME, TransportConstants.DEFAULT_STOMP_PORT); @@ -191,7 +185,9 @@ protected JMSServerManager createServer() throws Exception { .setPersistenceEnabled(isPersistenceEnabled()) .addAcceptorConfiguration(stompTransport) .addAcceptorConfiguration(new TransportConfiguration(InVMAcceptorFactory.class.getName())) - .setConnectionTtlCheckInterval(500); + .setConnectionTtlCheckInterval(500) + .addQueueConfiguration(new CoreQueueConfiguration().setAddress(getQueueName()).setName(getQueueName()).setRoutingType(RoutingType.ANYCAST)) + .addAddressConfiguration(new CoreAddressConfiguration().setName(getTopicName()).addRoutingType(RoutingType.MULTICAST)); if (getIncomingInterceptors() != null) { config.setIncomingInterceptorClassNames(getIncomingInterceptors()); @@ -217,12 +213,7 @@ protected JMSServerManager createServer() throws Exception { }); } - JMSConfiguration jmsConfig = new JMSConfigurationImpl(); - jmsConfig.getQueueConfigurations().add(new JMSQueueConfigurationImpl().setName(getQueueName()).setBindings(getQueueName())); - jmsConfig.getTopicConfigurations().add(new TopicConfigurationImpl().setName(getTopicName()).setBindings(getTopicName())); - server = new JMSServerManagerImpl(activeMQServer, jmsConfig); - server.setRegistry(new JndiBindingRegistry(new InVMNamingContext())); - return server; + return activeMQServer; } protected ConnectionFactory createConnectionFactory() { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestMultiThreaded.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestMultiThreaded.java index 63dd35bcc6f..3b71ec22dcb 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestMultiThreaded.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestMultiThreaded.java @@ -67,8 +67,8 @@ public void run() { @Test public void testTwoConcurrentSubscribers() throws Exception { - server.getActiveMQServer().getAddressSettingsRepository().addMatch("#", new AddressSettings().setAutoDeleteAddresses(false).setAutoDeleteQueues(false)); - server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://localhost:61614?protocols=STOMP&anycastPrefix=/queue/").start(); + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setAutoDeleteAddresses(false).setAutoDeleteQueues(false)); + server.getRemotingService().createAcceptor("test", "tcp://localhost:61614?protocols=STOMP&anycastPrefix=/queue/").start(); int nThreads = 2; @@ -89,7 +89,7 @@ public void testTwoConcurrentSubscribers() throws Exception { } // delete queue here so it can be auto-created again during the next loop iteration - server.getActiveMQServer().locateQueue(QUEUE).deleteQueue(); + server.locateQueue(QUEUE).deleteQueue(); } } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWebSocketMaxFrameTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWebSocketMaxFrameTest.java index f48e5cd0836..8bdf2f78f28 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWebSocketMaxFrameTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWebSocketMaxFrameTest.java @@ -46,7 +46,7 @@ public static Collection data() { @Override public void setUp() throws Exception { super.setUp(); - server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://127.0.0.1:" + wsport + "?stompMaxFramePayloadLength=" + stompWSMaxFrameSize).start(); + server.getRemotingService().createAcceptor("test", "tcp://127.0.0.1:" + wsport + "?stompMaxFramePayloadLength=" + stompWSMaxFrameSize).start(); wsURI = createStompClientUri(scheme, hostname, wsport); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWebSocketTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWebSocketTest.java index dd1b5ca5a67..4dcd4fed06f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWebSocketTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWebSocketTest.java @@ -28,17 +28,13 @@ import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; -import org.apache.activemq.artemis.jms.server.JMSServerManager; -import org.apache.activemq.artemis.jms.server.config.JMSConfiguration; -import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Before; import org.junit.Test; public class StompWebSocketTest extends ActiveMQTestBase { - private JMSServerManager server; + private ActiveMQServer server; /** * to test the Stomp over Web Sockets protocol, @@ -63,7 +59,7 @@ public void setUp() throws Exception { * @return * @throws Exception */ - private JMSServerManager createServer() throws Exception { + private ActiveMQServer createServer() throws Exception { Map params = new HashMap<>(); params.put(TransportConstants.PROTOCOLS_PROP_NAME, StompProtocolManagerFactory.STOMP_PROTOCOL_NAME); params.put(TransportConstants.PORT_PROP_NAME, TransportConstants.DEFAULT_STOMP_PORT + 1); @@ -71,11 +67,7 @@ private JMSServerManager createServer() throws Exception { Configuration config = createBasicConfig().addAcceptorConfiguration(stompTransport).addAcceptorConfiguration(new TransportConfiguration(InVMAcceptorFactory.class.getName())).addQueueConfiguration(new CoreQueueConfiguration().setAddress(getQueueName()).setName(getQueueName()).setDurable(false)); - ActiveMQServer activeMQServer = addServer(ActiveMQServers.newActiveMQServer(config)); - - JMSConfiguration jmsConfig = new JMSConfigurationImpl(); - server = new JMSServerManagerImpl(activeMQServer, jmsConfig); - server.setRegistry(null); + server = addServer(ActiveMQServers.newActiveMQServer(config)); return server; } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithClientIdValidationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithClientIdValidationTest.java index 49f3de5d1d7..8a5077de968 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithClientIdValidationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithClientIdValidationTest.java @@ -22,20 +22,14 @@ import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; import org.apache.activemq.artemis.core.remoting.impl.invm.InVMAcceptorFactory; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; -import org.apache.activemq.artemis.jms.server.JMSServerManager; -import org.apache.activemq.artemis.jms.server.config.JMSConfiguration; -import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule; import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnection; import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnectionFactory; -import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.junit.Test; @SuppressWarnings("deprecation") @@ -47,7 +41,7 @@ public boolean isSecurityEnabled() { } @Override - protected JMSServerManager createServer() throws Exception { + protected ActiveMQServer createServer() throws Exception { Configuration config = createBasicConfig() .setSecurityEnabled(isSecurityEnabled()) .setPersistenceEnabled(isPersistenceEnabled()) @@ -79,11 +73,7 @@ public String validateUser(String user, String password, RemotingConnection remo securityManager.getConfiguration().addUser(defUser, defPass); - ActiveMQServer activeMqServer = addServer(ActiveMQServers.newActiveMQServer(config, ManagementFactory.getPlatformMBeanServer(), securityManager)); - - JMSConfiguration jmsConfig = new JMSConfigurationImpl(); - server = new JMSServerManagerImpl(activeMqServer, jmsConfig); - server.setRegistry(new JndiBindingRegistry(new InVMNamingContext())); + server = addServer(ActiveMQServers.newActiveMQServer(config, ManagementFactory.getPlatformMBeanServer(), securityManager)); return server; } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithLargeMessagesTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithLargeMessagesTest.java index eb515bda74f..3f9cb6e9e91 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithLargeMessagesTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithLargeMessagesTest.java @@ -71,7 +71,7 @@ public void testSendReceiveLargeMessage() throws Exception { try { String address = "testLargeMessageAddress"; - server.getActiveMQServer().createQueue(SimpleString.toSimpleString(address), RoutingType.ANYCAST, SimpleString.toSimpleString(address), null, true, false); + server.createQueue(SimpleString.toSimpleString(address), RoutingType.ANYCAST, SimpleString.toSimpleString(address), null, true, false); // STOMP default is UTF-8 == 1 byte per char. int largeMessageStringSize = 10 * 1024 * 1024; // 10MB diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithSecurityTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithSecurityTest.java index 7bdc18b7699..5ed88326547 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithSecurityTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithSecurityTest.java @@ -34,7 +34,7 @@ public boolean isSecurityEnabled() { @Test public void testJMSXUserID() throws Exception { - server.getActiveMQServer().getConfiguration().setPopulateValidatedUser(true); + server.getConfiguration().setPopulateValidatedUser(true); MessageConsumer consumer = session.createConsumer(queue); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v11/StompV11Test.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v11/StompV11Test.java index 0647ae8998e..167ad460eaf 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v11/StompV11Test.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v11/StompV11Test.java @@ -96,7 +96,7 @@ public void tearDown() throws Exception { @Test public void testConnection() throws Exception { - server.getActiveMQServer().getSecurityStore().setSecurityEnabled(true); + server.getSecurityStore().setSecurityEnabled(true); StompClientConnection connection = StompClientConnectionFactory.createClientConnection(v10Uri); connection.connect(defUser, defPass); @@ -708,7 +708,7 @@ public void testHeartBeatToTTL() throws Exception { uri = createStompClientUri(scheme, hostname, port); - server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://127.0.0.1:" + port + "?connectionTtl=1000&connectionTtlMin=5000&connectionTtlMax=10000").start(); + server.getRemotingService().createAcceptor("test", "tcp://127.0.0.1:" + port + "?connectionTtl=1000&connectionTtlMin=5000&connectionTtlMax=10000").start(); StompClientConnection connection = StompClientConnectionFactory.createClientConnection(uri); //no heart beat at all if heat-beat absent @@ -857,7 +857,7 @@ public void testHeartBeatToConnectionTTLModifier() throws Exception { StompClientConnection connection; int port = 61614; - server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://127.0.0.1:" + port + "?heartBeatToConnectionTtlModifier=1").start(); + server.getRemotingService().createAcceptor("test", "tcp://127.0.0.1:" + port + "?heartBeatToConnectionTtlModifier=1").start(); connection = StompClientConnectionFactory.createClientConnection(uri); frame = connection.createFrame(Stomp.Commands.CONNECT) @@ -881,8 +881,8 @@ public void testHeartBeatToConnectionTTLModifier() throws Exception { connection.closeTransport(); } - server.getActiveMQServer().getRemotingService().destroyAcceptor("test"); - server.getActiveMQServer().getRemotingService().createAcceptor("test", "tcp://127.0.0.1:" + port + "?heartBeatToConnectionTtlModifier=1.5").start(); + server.getRemotingService().destroyAcceptor("test"); + server.getRemotingService().createAcceptor("test", "tcp://127.0.0.1:" + port + "?heartBeatToConnectionTtlModifier=1.5").start(); connection = StompClientConnectionFactory.createClientConnection(uri); frame = connection.createFrame(Stomp.Commands.CONNECT) @@ -1518,11 +1518,11 @@ public void testDurableUnSubscribe() throws Exception { long start = System.currentTimeMillis(); SimpleString queueName = SimpleString.toSimpleString(CLIENT_ID + "." + getName()); - while (server.getActiveMQServer().locateQueue(queueName) != null && (System.currentTimeMillis() - start) < 5000) { + while (server.locateQueue(queueName) != null && (System.currentTimeMillis() - start) < 5000) { Thread.sleep(100); } - assertNull(server.getActiveMQServer().locateQueue(queueName)); + assertNull(server.locateQueue(queueName)); conn.disconnect(); } @@ -2165,8 +2165,8 @@ public void testHeartBeat3() throws Exception { conn.startPinger(100); - Assert.assertEquals(1, server.getActiveMQServer().getRemotingService().getConnections().size()); - StompConnection stompConnection = (StompConnection)server.getActiveMQServer().getRemotingService().getConnections().iterator().next(); + Assert.assertEquals(1, server.getRemotingService().getConnections().size()); + StompConnection stompConnection = (StompConnection)server.getRemotingService().getConnections().iterator().next(); StompFrameHandlerV11 stompFrameHandler = (StompFrameHandlerV11)stompConnection.getStompVersionHandler(); Thread.sleep(1000); @@ -2177,7 +2177,7 @@ public void testHeartBeat3() throws Exception { conn.stopPinger(); //((AbstractStompClientConnection)conn).killReaderThread(); Wait.waitFor(() -> { - return server.getActiveMQServer().getRemotingService().getConnections().size() == 0; + return server.getRemotingService().getConnections().size() == 0; }); Assert.assertFalse(stompFrameHandler.getHeartBeater().isStarted()); @@ -2202,7 +2202,7 @@ public void testHeartBeat4() throws Exception { // Obtain a reference to the server StompConnection object RemotingConnection remotingConnection = null; StompConnection stompConnection = null; - Iterator iterator = server.getActiveMQServer().getRemotingService().getConnections().iterator(); + Iterator iterator = server.getRemotingService().getConnections().iterator(); while (iterator.hasNext()) { remotingConnection = iterator.next(); if (remotingConnection instanceof StompConnection) { @@ -2236,7 +2236,7 @@ public void testHeartBeat4() throws Exception { Thread.sleep(2000); Wait.waitFor(() -> { - return server.getActiveMQServer().getRemotingService().getConnections().size() == 0; + return server.getRemotingService().getConnections().size() == 0; }); Assert.assertFalse("HeartBeater is still running!!", stompFrameHandler.getHeartBeater().isStarted()); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v12/StompV12Test.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v12/StompV12Test.java index c91e5a8c837..61ef22c5ac1 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v12/StompV12Test.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/v12/StompV12Test.java @@ -97,7 +97,7 @@ public void tearDown() throws Exception { @Test public void testConnection() throws Exception { - server.getActiveMQServer().getSecurityStore().setSecurityEnabled(true); + server.getSecurityStore().setSecurityEnabled(true); StompClientConnection connection = StompClientConnectionFactory.createClientConnection(v10Uri); connection.connect(defUser, defPass); @@ -375,7 +375,7 @@ public void testHeaderRepetitive() throws Exception { AddressSettings addressSettings = new AddressSettings(); addressSettings.setAutoCreateQueues(false); addressSettings.setAutoCreateAddresses(false); - server.getActiveMQServer().getAddressSettingsRepository().addMatch("#", addressSettings); + server.getAddressSettingsRepository().addMatch("#", addressSettings); conn.connect(defUser, defPass); @@ -1502,11 +1502,11 @@ public void testDurableUnSubscribe() throws Exception { long start = System.currentTimeMillis(); SimpleString queueName = SimpleString.toSimpleString(CLIENT_ID + "." + getName()); - while (server.getActiveMQServer().locateQueue(queueName) != null && (System.currentTimeMillis() - start) < 5000) { + while (server.locateQueue(queueName) != null && (System.currentTimeMillis() - start) < 5000) { Thread.sleep(100); } - assertNull(server.getActiveMQServer().locateQueue(queueName)); + assertNull(server.locateQueue(queueName)); conn.disconnect(); } diff --git a/tests/integration-tests/src/test/resources/spring-jms-beans.xml b/tests/integration-tests/src/test/resources/spring-jms-beans.xml index 1d5bce32b6b..854de87daf9 100644 --- a/tests/integration-tests/src/test/resources/spring-jms-beans.xml +++ b/tests/integration-tests/src/test/resources/spring-jms-beans.xml @@ -20,7 +20,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - + diff --git a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/AcknowledgementTest.java b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/AcknowledgementTest.java index 9d72dc5ca79..767b27a62d2 100644 --- a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/AcknowledgementTest.java +++ b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/AcknowledgementTest.java @@ -33,9 +33,7 @@ import javax.jms.TopicSubscriber; import java.util.concurrent.CountDownLatch; -import org.apache.activemq.artemis.api.core.client.ActiveMQClient; -import org.apache.activemq.artemis.api.jms.JMSFactoryType; -import org.apache.activemq.artemis.jms.client.ActiveMQJMSConnectionFactory; +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.jms.tests.util.ProxyAssertSupport; import org.junit.Assert; import org.junit.Test; @@ -1309,13 +1307,8 @@ public void testTransactionalIgnoreACK() throws Exception { */ @Test public void testNonBlockingAckPerf() throws Exception { - getJmsServerManager().createConnectionFactory("testsuitecf1", false, JMSFactoryType.CF, NETTY_CONNECTOR, null, ActiveMQClient.DEFAULT_CLIENT_FAILURE_CHECK_PERIOD, ActiveMQClient.DEFAULT_CONNECTION_TTL, ActiveMQClient.DEFAULT_CALL_TIMEOUT, ActiveMQClient.DEFAULT_CALL_FAILOVER_TIMEOUT, ActiveMQClient.DEFAULT_CACHE_LARGE_MESSAGE_CLIENT, ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, ActiveMQClient.DEFAULT_COMPRESS_LARGE_MESSAGES, ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE, ActiveMQClient.DEFAULT_CONSUMER_MAX_RATE, ActiveMQClient.DEFAULT_CONFIRMATION_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_MAX_RATE, true, true, true, ActiveMQClient.DEFAULT_AUTO_GROUP, ActiveMQClient.DEFAULT_PRE_ACKNOWLEDGE, ActiveMQClient.DEFAULT_CONNECTION_LOAD_BALANCING_POLICY_CLASS_NAME, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_USE_GLOBAL_POOLS, ActiveMQClient.DEFAULT_SCHEDULED_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RETRY_INTERVAL_MULTIPLIER, ActiveMQClient.DEFAULT_MAX_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RECONNECT_ATTEMPTS, ActiveMQClient.DEFAULT_FAILOVER_ON_INITIAL_CONNECTION, null, "/testsuitecf1"); - getJmsServerManager().createConnectionFactory("testsuitecf2", false, JMSFactoryType.CF, NETTY_CONNECTOR, null, ActiveMQClient.DEFAULT_CLIENT_FAILURE_CHECK_PERIOD, ActiveMQClient.DEFAULT_CONNECTION_TTL, ActiveMQClient.DEFAULT_CALL_TIMEOUT, ActiveMQClient.DEFAULT_CALL_FAILOVER_TIMEOUT, ActiveMQClient.DEFAULT_CACHE_LARGE_MESSAGE_CLIENT, ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, ActiveMQClient.DEFAULT_COMPRESS_LARGE_MESSAGES, ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE, ActiveMQClient.DEFAULT_CONSUMER_MAX_RATE, ActiveMQClient.DEFAULT_CONFIRMATION_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_MAX_RATE, true, true, true, ActiveMQClient.DEFAULT_AUTO_GROUP, ActiveMQClient.DEFAULT_PRE_ACKNOWLEDGE, ActiveMQClient.DEFAULT_CONNECTION_LOAD_BALANCING_POLICY_CLASS_NAME, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_USE_GLOBAL_POOLS, ActiveMQClient.DEFAULT_SCHEDULED_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RETRY_INTERVAL_MULTIPLIER, ActiveMQClient.DEFAULT_MAX_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RECONNECT_ATTEMPTS, ActiveMQClient.DEFAULT_FAILOVER_ON_INITIAL_CONNECTION, null, "/testsuitecf2"); - - ActiveMQJMSConnectionFactory cf1 = (ActiveMQJMSConnectionFactory) getInitialContext().lookup("/testsuitecf1"); - cf1.setBlockOnAcknowledge(false); - ActiveMQJMSConnectionFactory cf2 = (ActiveMQJMSConnectionFactory) getInitialContext().lookup("/testsuitecf2"); - cf2.setBlockOnAcknowledge(true); + ConnectionFactory cf1 = ActiveMQJMSClient.createConnectionFactory("tcp://127.0.0.1:61616?blockOnNonDurableSend=true&blockOnAcknowledge=false", "testsuitecf1"); + ConnectionFactory cf2 = ActiveMQJMSClient.createConnectionFactory("tcp://127.0.0.1:61616?blockOnNonDurableSend=true&blockOnAcknowledge=true", "testsuitecf2"); int messageCount = 100; diff --git a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/ActiveMQServerTestCase.java b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/ActiveMQServerTestCase.java index 7a45f29f287..e7a79104669 100644 --- a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/ActiveMQServerTestCase.java +++ b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/ActiveMQServerTestCase.java @@ -43,7 +43,6 @@ import org.apache.activemq.artemis.core.security.Role; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; -import org.apache.activemq.artemis.jms.server.JMSServerManager; import org.apache.activemq.artemis.jms.tests.tools.ServerManagement; import org.apache.activemq.artemis.jms.tests.tools.container.Server; import org.apache.activemq.artemis.jms.tests.util.ProxyAssertSupport; @@ -248,10 +247,6 @@ protected ActiveMQServer getJmsServer() throws Exception { return ActiveMQServerTestCase.servers.get(0).getActiveMQServer(); } - protected JMSServerManager getJmsServerManager() throws Exception { - return ActiveMQServerTestCase.servers.get(0).getJMSServerManager(); - } - protected void checkNoSubscriptions(final Topic topic) throws Exception { } diff --git a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/CTSMiscellaneousTest.java b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/CTSMiscellaneousTest.java index f1245750c8a..e86fee920f5 100644 --- a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/CTSMiscellaneousTest.java +++ b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/CTSMiscellaneousTest.java @@ -20,11 +20,8 @@ import javax.jms.DeliveryMode; import javax.jms.MessageProducer; import javax.jms.Session; -import java.util.ArrayList; -import java.util.List; -import org.apache.activemq.artemis.api.core.client.ActiveMQClient; -import org.apache.activemq.artemis.api.jms.JMSFactoryType; +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.junit.After; import org.junit.Before; @@ -41,21 +38,13 @@ public class CTSMiscellaneousTest extends JMSTest { // Attributes ---------------------------------------------------- protected static ActiveMQConnectionFactory cf; - private static final String ORG_JBOSS_MESSAGING_SERVICE_LBCONNECTION_FACTORY = "StrictTCKConnectionFactory"; - // Constructors -------------------------------------------------- @Override @Before public void setUp() throws Exception { try { super.setUp(); - // Deploy a connection factory with load balancing but no failover on node0 - List bindings = new ArrayList<>(); - bindings.add("StrictTCKConnectionFactory"); - - getJmsServerManager().createConnectionFactory("StrictTCKConnectionFactory", false, JMSFactoryType.CF, NETTY_CONNECTOR, null, ActiveMQClient.DEFAULT_CLIENT_FAILURE_CHECK_PERIOD, ActiveMQClient.DEFAULT_CONNECTION_TTL, ActiveMQClient.DEFAULT_CALL_TIMEOUT, ActiveMQClient.DEFAULT_CALL_FAILOVER_TIMEOUT, ActiveMQClient.DEFAULT_CACHE_LARGE_MESSAGE_CLIENT, ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, ActiveMQClient.DEFAULT_COMPRESS_LARGE_MESSAGES, ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE, ActiveMQClient.DEFAULT_CONSUMER_MAX_RATE, ActiveMQClient.DEFAULT_CONFIRMATION_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_MAX_RATE, true, true, true, ActiveMQClient.DEFAULT_AUTO_GROUP, ActiveMQClient.DEFAULT_PRE_ACKNOWLEDGE, ActiveMQClient.DEFAULT_CONNECTION_LOAD_BALANCING_POLICY_CLASS_NAME, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_USE_GLOBAL_POOLS, ActiveMQClient.DEFAULT_SCHEDULED_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RETRY_INTERVAL_MULTIPLIER, ActiveMQClient.DEFAULT_MAX_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RECONNECT_ATTEMPTS, ActiveMQClient.DEFAULT_FAILOVER_ON_INITIAL_CONNECTION, null, "/StrictTCKConnectionFactory"); - - CTSMiscellaneousTest.cf = (ActiveMQConnectionFactory) getInitialContext().lookup("/StrictTCKConnectionFactory"); + CTSMiscellaneousTest.cf = ActiveMQJMSClient.createConnectionFactory("tcp://127.0.0.1:61616?blockOnAcknowledge=true&blockOnDurableSend=true&blockOnNonDurableSend=true", "StrictTCKConnectionFactory"); } catch (Exception e) { e.printStackTrace(); } @@ -101,7 +90,6 @@ public void testNonPersistentMessagesSentSynchronously() throws Exception { @After public void tearDown() throws Exception { super.tearDown(); - ActiveMQServerTestCase.undeployConnectionFactory(CTSMiscellaneousTest.ORG_JBOSS_MESSAGING_SERVICE_LBCONNECTION_FACTORY); } // Package protected --------------------------------------------- diff --git a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/JMSTestCase.java b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/JMSTestCase.java index 8c66e7a6a45..45f5a4923e8 100644 --- a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/JMSTestCase.java +++ b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/JMSTestCase.java @@ -26,8 +26,6 @@ import javax.naming.InitialContext; import java.util.ArrayList; -import org.apache.activemq.artemis.api.core.client.ActiveMQClient; -import org.apache.activemq.artemis.api.jms.JMSFactoryType; import org.apache.activemq.artemis.jms.client.ActiveMQJMSConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQQueueConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQTopicConnectionFactory; @@ -68,16 +66,9 @@ public void setUp() throws Exception { // All jms tests should use a specific cg which has blockOnAcknowledge = true and // both np and p messages are sent synchronously - - getJmsServerManager().createConnectionFactory("testsuitecf", false, JMSFactoryType.CF, NETTY_CONNECTOR, null, ActiveMQClient.DEFAULT_CLIENT_FAILURE_CHECK_PERIOD, ActiveMQClient.DEFAULT_CONNECTION_TTL, ActiveMQClient.DEFAULT_CALL_TIMEOUT, ActiveMQClient.DEFAULT_CALL_FAILOVER_TIMEOUT, ActiveMQClient.DEFAULT_CACHE_LARGE_MESSAGE_CLIENT, ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, ActiveMQClient.DEFAULT_COMPRESS_LARGE_MESSAGES, ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE, ActiveMQClient.DEFAULT_CONSUMER_MAX_RATE, ActiveMQClient.DEFAULT_CONFIRMATION_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_MAX_RATE, true, true, true, ActiveMQClient.DEFAULT_AUTO_GROUP, ActiveMQClient.DEFAULT_PRE_ACKNOWLEDGE, ActiveMQClient.DEFAULT_CONNECTION_LOAD_BALANCING_POLICY_CLASS_NAME, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_USE_GLOBAL_POOLS, ActiveMQClient.DEFAULT_SCHEDULED_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RETRY_INTERVAL_MULTIPLIER, ActiveMQClient.DEFAULT_MAX_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RECONNECT_ATTEMPTS, ActiveMQClient.DEFAULT_FAILOVER_ON_INITIAL_CONNECTION, null, "/testsuitecf"); - - getJmsServerManager().createConnectionFactory("testsuitecf_queue", false, JMSFactoryType.QUEUE_CF, NETTY_CONNECTOR, null, ActiveMQClient.DEFAULT_CLIENT_FAILURE_CHECK_PERIOD, ActiveMQClient.DEFAULT_CONNECTION_TTL, ActiveMQClient.DEFAULT_CALL_TIMEOUT, ActiveMQClient.DEFAULT_CALL_FAILOVER_TIMEOUT, ActiveMQClient.DEFAULT_CACHE_LARGE_MESSAGE_CLIENT, ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, ActiveMQClient.DEFAULT_COMPRESS_LARGE_MESSAGES, ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE, ActiveMQClient.DEFAULT_CONSUMER_MAX_RATE, ActiveMQClient.DEFAULT_CONFIRMATION_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_MAX_RATE, true, true, true, ActiveMQClient.DEFAULT_AUTO_GROUP, ActiveMQClient.DEFAULT_PRE_ACKNOWLEDGE, ActiveMQClient.DEFAULT_CONNECTION_LOAD_BALANCING_POLICY_CLASS_NAME, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_USE_GLOBAL_POOLS, ActiveMQClient.DEFAULT_SCHEDULED_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RETRY_INTERVAL_MULTIPLIER, ActiveMQClient.DEFAULT_MAX_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RECONNECT_ATTEMPTS, ActiveMQClient.DEFAULT_FAILOVER_ON_INITIAL_CONNECTION, null, "/testsuitecf_queue"); - - getJmsServerManager().createConnectionFactory("testsuitecf_topic", false, JMSFactoryType.TOPIC_CF, NETTY_CONNECTOR, null, ActiveMQClient.DEFAULT_CLIENT_FAILURE_CHECK_PERIOD, ActiveMQClient.DEFAULT_CONNECTION_TTL, ActiveMQClient.DEFAULT_CALL_TIMEOUT, ActiveMQClient.DEFAULT_CALL_FAILOVER_TIMEOUT, ActiveMQClient.DEFAULT_CACHE_LARGE_MESSAGE_CLIENT, ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, ActiveMQClient.DEFAULT_COMPRESS_LARGE_MESSAGES, ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE, ActiveMQClient.DEFAULT_CONSUMER_MAX_RATE, ActiveMQClient.DEFAULT_CONFIRMATION_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_WINDOW_SIZE, ActiveMQClient.DEFAULT_PRODUCER_MAX_RATE, true, true, true, ActiveMQClient.DEFAULT_AUTO_GROUP, ActiveMQClient.DEFAULT_PRE_ACKNOWLEDGE, ActiveMQClient.DEFAULT_CONNECTION_LOAD_BALANCING_POLICY_CLASS_NAME, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE, ActiveMQClient.DEFAULT_USE_GLOBAL_POOLS, ActiveMQClient.DEFAULT_SCHEDULED_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_THREAD_POOL_MAX_SIZE, ActiveMQClient.DEFAULT_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RETRY_INTERVAL_MULTIPLIER, ActiveMQClient.DEFAULT_MAX_RETRY_INTERVAL, ActiveMQClient.DEFAULT_RECONNECT_ATTEMPTS, ActiveMQClient.DEFAULT_FAILOVER_ON_INITIAL_CONNECTION, null, "/testsuitecf_topic"); - - cf = (ActiveMQJMSConnectionFactory) getInitialContext().lookup("/testsuitecf"); - queueCf = (ActiveMQQueueConnectionFactory) getInitialContext().lookup("/testsuitecf_queue"); - topicCf = (ActiveMQTopicConnectionFactory) getInitialContext().lookup("/testsuitecf_topic"); + cf = new ActiveMQJMSConnectionFactory("tcp://127.0.0.1:61616?blockOnAcknowledge=true&blockOnDurableSend=true&blockOnNonDurableSend=true"); + queueCf = new ActiveMQQueueConnectionFactory("tcp://127.0.0.1:61616?blockOnAcknowledge=true&blockOnDurableSend=true&blockOnNonDurableSend=true"); + topicCf = new ActiveMQTopicConnectionFactory("tcp://127.0.0.1:61616?blockOnAcknowledge=true&blockOnDurableSend=true&blockOnNonDurableSend=true"); assertRemainingMessages(0); } @@ -119,7 +110,6 @@ protected final Connection createConnection(String user, String password) throws @After public void tearDown() throws Exception { super.tearDown(); - getJmsServerManager().destroyConnectionFactory("testsuitecf"); if (cf != null) { cf.close(); } diff --git a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/SecurityTest.java b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/SecurityTest.java index d7137ae58fc..e83d815467e 100644 --- a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/SecurityTest.java +++ b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/SecurityTest.java @@ -64,6 +64,7 @@ public void clearProperty() { } else { System.setProperty(DefaultConnectionProperties.BROKER_BIND_URL, originalBrokerBindUrl); } + DefaultConnectionProperties.initialize(); } diff --git a/tests/joram-tests/src/test/java/org/apache/activemq/artemis/common/SpawnedJMSServer.java b/tests/joram-tests/src/test/java/org/apache/activemq/artemis/common/SpawnedJMSServer.java index 17d1c7a57ac..3e559e87cb0 100644 --- a/tests/joram-tests/src/test/java/org/apache/activemq/artemis/common/SpawnedJMSServer.java +++ b/tests/joram-tests/src/test/java/org/apache/activemq/artemis/common/SpawnedJMSServer.java @@ -26,14 +26,11 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; -import org.apache.activemq.artemis.jms.server.JMSServerManager; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.utils.FileUtil; public class SpawnedJMSServer { public static ActiveMQServer server; - public static JMSServerManager serverManager; // Using files may be useful for debugging (through print-data for instance) private static final boolean useFiles = false; @@ -92,18 +89,16 @@ public static ActiveMQServer startServer() throws Exception { // set DLA and expiry to avoid spamming the log with warnings server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDeadLetterAddress(SimpleString.toSimpleString("DLA")).setExpiryAddress(SimpleString.toSimpleString("Expiry"))); - serverManager = new JMSServerManagerImpl(server); - serverManager.start(); + server.start(); } return server; } public static void stopServer() throws Exception { if (server != null) { - serverManager.stop(); + server.stop(); } server = null; - serverManager = null; } // Constructors -------------------------------------------------- diff --git a/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java b/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java index f6a08a05fb2..3bcaeacf444 100644 --- a/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java +++ b/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java @@ -44,6 +44,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder; @@ -51,20 +52,16 @@ import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.api.jms.JMSFactoryType; import org.apache.activemq.artemis.core.config.Configuration; -import org.apache.activemq.artemis.core.registry.JndiBindingRegistry; import org.apache.activemq.artemis.core.remoting.impl.invm.InVMAcceptorFactory; import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory; +import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; -import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.jms.bridge.ConnectionFactoryFactory; import org.apache.activemq.artemis.jms.bridge.DestinationFactory; import org.apache.activemq.artemis.jms.bridge.QualityOfServiceMode; import org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl; import org.apache.activemq.artemis.jms.client.ActiveMQJMSConnectionFactory; -import org.apache.activemq.artemis.jms.server.JMSServerManager; -import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; import org.apache.activemq.artemis.tests.unit.UnitTestLogger; -import org.apache.activemq.artemis.tests.unit.util.InVMNamingContext; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.utils.RandomUtil; import org.junit.Assert; @@ -84,7 +81,7 @@ public class JMSBridgeImplTest extends ActiveMQTestBase { private static final String TARGET = RandomUtil.randomString(); - private JMSServerManager jmsServer; + private ActiveMQServer server; private static final AtomicBoolean tcclClassFound = new AtomicBoolean(false); @@ -701,14 +698,11 @@ public void setUp() throws Exception { super.setUp(); Configuration config = createBasicConfig().addAcceptorConfiguration(new TransportConfiguration(InVMAcceptorFactory.class.getName())); - InVMNamingContext context = new InVMNamingContext(); - jmsServer = new JMSServerManagerImpl(addServer(ActiveMQServers.newActiveMQServer(config, false))); - jmsServer.setRegistry(new JndiBindingRegistry(context)); - jmsServer.start(); - - jmsServer.createQueue(false, JMSBridgeImplTest.SOURCE, null, true, "/queue/" + JMSBridgeImplTest.SOURCE); - jmsServer.createQueue(false, JMSBridgeImplTest.TARGET, null, true, "/queue/" + JMSBridgeImplTest.TARGET); + server = addServer(ActiveMQServers.newActiveMQServer(config, false)); + server.start(); + server.createQueue(SimpleString.toSimpleString(JMSBridgeImplTest.SOURCE), RoutingType.ANYCAST, SimpleString.toSimpleString(JMSBridgeImplTest.SOURCE), null, true, false); + server.createQueue(SimpleString.toSimpleString(JMSBridgeImplTest.TARGET), RoutingType.ANYCAST, SimpleString.toSimpleString(JMSBridgeImplTest.TARGET), null, true, false); } @Test From 9ad90511659c72b99488062a21a91d454bbfc367 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Wed, 27 Jun 2018 14:34:00 -0500 Subject: [PATCH 082/207] ARTEMIS-1956 move MessageCounterInfo to core-client --- .../core/management/MessageCounterInfo.java | 20 ------------- .../management/impl/QueueControlImpl.java | 3 +- .../core/messagecounter/MessageCounter.java | 28 +++++++++++++++++++ 3 files changed, 29 insertions(+), 22 deletions(-) rename {artemis-server => artemis-core-client}/src/main/java/org/apache/activemq/artemis/api/core/management/MessageCounterInfo.java (78%) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/api/core/management/MessageCounterInfo.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/MessageCounterInfo.java similarity index 78% rename from artemis-server/src/main/java/org/apache/activemq/artemis/api/core/management/MessageCounterInfo.java rename to artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/MessageCounterInfo.java index 8339a0fea27..b0dd16c7490 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/api/core/management/MessageCounterInfo.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/MessageCounterInfo.java @@ -17,14 +17,8 @@ package org.apache.activemq.artemis.api.core.management; import javax.json.JsonObject; -import java.text.DateFormat; -import java.util.Date; import org.apache.activemq.artemis.api.core.JsonUtil; -import org.apache.activemq.artemis.core.messagecounter.MessageCounter; -import org.apache.activemq.artemis.utils.JsonLoader; - -import static org.apache.activemq.artemis.api.core.JsonUtil.nullSafe; /** * Helper class to create Java Objects from the @@ -50,20 +44,6 @@ public final class MessageCounterInfo { private final String udpateTimestamp; - /** - * Returns a JSON String serialization of a {@link MessageCounter} object. - * - * @param counter - * @return - * @throws Exception - */ - public static String toJSon(final MessageCounter counter) throws Exception { - DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM); - String lastAddTimestamp = dateFormat.format(new Date(counter.getLastAddedMessageTime())); - String updateTimestamp = dateFormat.format(new Date(counter.getLastUpdate())); - return JsonLoader.createObjectBuilder().add("destinationName", nullSafe(counter.getDestinationName())).add("destinationSubscription", nullSafe(counter.getDestinationSubscription())).add("destinationDurable", counter.isDestinationDurable()).add("count", counter.getCount()).add("countDelta", counter.getCountDelta()).add("messageCount", counter.getMessageCount()).add("messageCountDelta", counter.getMessageCountDelta()).add("lastAddTimestamp", lastAddTimestamp).add("updateTimestamp", updateTimestamp).build().toString(); - } - /** * Returns a MessageCounterInfo corresponding to the JSON serialization returned * by {@link QueueControl#listMessageCounter()}. diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java index 8963b24e317..16e948339e4 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java @@ -35,7 +35,6 @@ import org.apache.activemq.artemis.api.core.JsonUtil; import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.SimpleString; -import org.apache.activemq.artemis.api.core.management.MessageCounterInfo; import org.apache.activemq.artemis.api.core.management.QueueControl; import org.apache.activemq.artemis.core.filter.Filter; import org.apache.activemq.artemis.core.filter.impl.FilterImpl; @@ -993,7 +992,7 @@ public String listMessageCounter() { clearIO(); try { - return MessageCounterInfo.toJSon(counter); + return counter.toJSon(); } catch (Exception e) { throw new IllegalStateException(e); } finally { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/messagecounter/MessageCounter.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/messagecounter/MessageCounter.java index 3ee5e4adaa2..be581823b90 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/messagecounter/MessageCounter.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/messagecounter/MessageCounter.java @@ -19,10 +19,14 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Calendar; +import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import org.apache.activemq.artemis.core.server.Queue; +import org.apache.activemq.artemis.utils.JsonLoader; + +import static org.apache.activemq.artemis.api.core.JsonUtil.nullSafe; /** * This class stores message count informations for a given queue @@ -308,6 +312,30 @@ public String toString() { "]"; } + /** + * Returns a JSON String serialization of a {@link MessageCounter} object. + * + * @return + */ + public String toJSon() { + DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM); + String lastAddTimestamp = dateFormat.format(new Date(this.getLastAddedMessageTime())); + String updateTimestamp = dateFormat.format(new Date(this.getLastUpdate())); + return JsonLoader + .createObjectBuilder() + .add("destinationName", nullSafe(this.getDestinationName())) + .add("destinationSubscription", nullSafe(this.getDestinationSubscription())) + .add("destinationDurable", this.isDestinationDurable()) + .add("count", this.getCount()) + .add("countDelta", this.getCountDelta()) + .add("messageCount", this.getMessageCount()) + .add("messageCountDelta", this.getMessageCountDelta()) + .add("lastAddTimestamp", lastAddTimestamp) + .add("updateTimestamp", updateTimestamp) + .build() + .toString(); + } + // Package protected --------------------------------------------- // Protected ----------------------------------------------------- From 2748ef0253dbbf68402887f34f2c4dd94d8bdd2f Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Fri, 13 Jul 2018 09:11:47 -0400 Subject: [PATCH 083/207] NO-JIRA removing dumb debug message left by accident --- .../artemis/core/config/ha/ReplicatedPolicyConfiguration.java | 1 - 1 file changed, 1 deletion(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java index de1de4f17ea..3acad777673 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java @@ -126,7 +126,6 @@ public int getQuorumVoteWait() { } public ReplicatedPolicyConfiguration setQuorumVoteWait(int quorumVoteWait) { - new Exception("here").printStackTrace(); this.quorumVoteWait = quorumVoteWait; return this; } From fbdd6fe0adb65863bfc82202df143ba77d7812f6 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Mon, 16 Jul 2018 14:34:44 -0500 Subject: [PATCH 084/207] ARTEMIS-1971 make LDAP pooling test more robust --- .../security/jaas/LDAPLoginModuleTest.java | 114 +++++++----------- 1 file changed, 45 insertions(+), 69 deletions(-) diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java index d28bd4c1f8c..47bedd9b079 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java @@ -115,7 +115,7 @@ public void testRunning() throws Exception { } @Test - public void testLogin() throws LoginException { + public void testLogin() throws Exception { logger.info("num session: " + ldapServer.getLdapSessionManager().getSessions().length); LoginContext context = new LoginContext("LDAPLogin", new CallbackHandler() { @@ -135,100 +135,76 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback context.login(); context.logout(); - assertTrue("no sessions after logout", waitForSessions(0)); + assertTrue("sessions still active after logout", waitFor(() -> ldapServer.getLdapSessionManager().getSessions().length == 0)); } @Test - public void testLoginPooled() throws LoginException { - - LoginContext context = new LoginContext("LDAPLoginPooled", new CallbackHandler() { - @Override - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - for (int i = 0; i < callbacks.length; i++) { - if (callbacks[i] instanceof NameCallback) { - ((NameCallback) callbacks[i]).setName("first"); - } else if (callbacks[i] instanceof PasswordCallback) { - ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); - } else { - throw new UnsupportedCallbackException(callbacks[i]); - } + public void testLoginPooled() throws Exception { + CallbackHandler callbackHandler = callbacks -> { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); } } - }); + }; + + LoginContext context = new LoginContext("LDAPLoginPooled", callbackHandler); context.login(); context.logout(); // again - context.login(); context.logout(); // new context - context = new LoginContext("LDAPLoginPooled", new CallbackHandler() { - @Override - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - for (int i = 0; i < callbacks.length; i++) { - if (callbacks[i] instanceof NameCallback) { - ((NameCallback) callbacks[i]).setName("first"); - } else if (callbacks[i] instanceof PasswordCallback) { - ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); - } else { - throw new UnsupportedCallbackException(callbacks[i]); - } - } - } - }); + context = new LoginContext("LDAPLoginPooled", callbackHandler); context.login(); context.logout(); Executor pool = Executors.newCachedThreadPool(); - for (int i = 0; i < 10; i++) { - ((ExecutorService) pool).execute(new Runnable() { - @Override - public void run() { - try { - LoginContext context = new LoginContext("LDAPLoginPooled", new CallbackHandler() { - @Override - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - for (int i = 0; i < callbacks.length; i++) { - if (callbacks[i] instanceof NameCallback) { - ((NameCallback) callbacks[i]).setName("first"); - } else if (callbacks[i] instanceof PasswordCallback) { - ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); - } else { - throw new UnsupportedCallbackException(callbacks[i]); - } - } - } - }); - context.login(); - context.logout(); - } catch (Exception ignored) { - } + for (int i = 0; i < 20; i++) { + pool.execute(() -> { + try { + LoginContext context1 = new LoginContext("LDAPLoginPooled", callbackHandler); + context1.login(); + context1.logout(); + } catch (Exception ignored) { } }); } - assertTrue("no sessions after logout", waitForSessions(10)); + + /* + * The number of sessions here is variable due to the pool used to create the LoginContext objects and the pooling + * for the LDAP connections (which are managed by the JVM implementation). We really just need to confirm that + * there are still connections to the LDAP server open even after all the LoginContext objects are closed as that + * will indicate the LDAP connection pooling is working. + */ + assertTrue("not enough active sessions after logout", waitFor(() -> ldapServer.getLdapSessionManager().getSessions().length >= 5)); + + ((ExecutorService) pool).shutdown(); + ((ExecutorService) pool).awaitTermination(2, TimeUnit.SECONDS); } - private boolean waitForSessions(int expected) { - final long expiry = System.currentTimeMillis() + 5000; - int numSession = ldapServer.getLdapSessionManager().getSessions().length; - while (numSession != expected && System.currentTimeMillis() < expiry) { - try { - TimeUnit.MILLISECONDS.sleep(100); - } catch (InterruptedException ok) { - break; - } - numSession = ldapServer.getLdapSessionManager().getSessions().length; - logger.info("num session " + numSession); + public interface Condition { + boolean isSatisfied() throws Exception; + } + private boolean waitFor(final Condition condition) throws Exception { + final long expiry = System.currentTimeMillis() + 5000; + boolean conditionSatisified = condition.isSatisfied(); + while (!conditionSatisified && System.currentTimeMillis() < expiry) { + TimeUnit.MILLISECONDS.sleep(100); + conditionSatisified = condition.isSatisfied(); } - return numSession == expected; + return conditionSatisified; } @Test - public void testUnauthenticated() throws LoginException { + public void testUnauthenticated() throws Exception { LoginContext context = new LoginContext("UnAuthenticatedLDAPLogin", new CallbackHandler() { @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { @@ -250,7 +226,7 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback return; } fail("Should have failed authenticating"); - assertTrue("no sessions after logout", waitForSessions(0)); + assertTrue("sessions still active after logout", waitFor(() -> ldapServer.getLdapSessionManager().getSessions().length == 0)); } @Test From b2d04b9ceb544ceb83c0c93e182a2b7f2841fbdb Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Mon, 16 Jul 2018 19:23:45 -0400 Subject: [PATCH 085/207] ARTEMIS-1954 Fixing RedeployTest and moving JMS Parsing --- .../artemis/integration/FileBroker.java | 47 +---- .../config/impl/LegacyJMSConfiguration.java | 196 ++++++++++++++++++ .../artemis/core/server/ActiveMQServers.java | 4 +- .../server/embedded/EmbeddedActiveMQ.java | 4 +- .../core/server/impl/ActiveMQServerImpl.java | 4 + 5 files changed, 209 insertions(+), 46 deletions(-) create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/integration/FileBroker.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/integration/FileBroker.java index 744a0e6c3b9..3929ed388ad 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/integration/FileBroker.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/integration/FileBroker.java @@ -18,22 +18,16 @@ import java.lang.management.ManagementFactory; import java.util.ArrayList; -import java.util.List; import java.util.Map; -import org.apache.activemq.artemis.api.core.RoutingType; -import org.apache.activemq.artemis.core.config.CoreAddressConfiguration; -import org.apache.activemq.artemis.core.config.CoreQueueConfiguration; import org.apache.activemq.artemis.core.config.FileDeploymentManager; import org.apache.activemq.artemis.core.config.impl.FileConfiguration; +import org.apache.activemq.artemis.core.config.impl.LegacyJMSConfiguration; import org.apache.activemq.artemis.core.server.ActiveMQComponent; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ServiceComponent; import org.apache.activemq.artemis.dto.ServerDTO; import org.apache.activemq.artemis.integration.bootstrap.ActiveMQBootstrapLogger; -import org.apache.activemq.artemis.jms.server.config.JMSQueueConfiguration; -import org.apache.activemq.artemis.jms.server.config.TopicConfiguration; -import org.apache.activemq.artemis.jms.server.config.impl.FileJMSConfiguration; import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager; public class FileBroker implements Broker { @@ -60,49 +54,14 @@ public synchronized void start() throws Exception { //todo if we start to pullout more configs from the main config then we should pull out the configuration objects from factories if available FileConfiguration configuration = new FileConfiguration(); - // Keep this as we still want to parse destinations in the element - FileJMSConfiguration jmsConfiguration = new FileJMSConfiguration(); + LegacyJMSConfiguration legacyJMSConfiguration = new LegacyJMSConfiguration(configuration); FileDeploymentManager fileDeploymentManager = new FileDeploymentManager(configurationUrl); - fileDeploymentManager.addDeployable(configuration).addDeployable(jmsConfiguration); + fileDeploymentManager.addDeployable(configuration).addDeployable(legacyJMSConfiguration); fileDeploymentManager.readConfiguration(); createDirectories(configuration); - /** - * This is a bit of a hack for backwards config compatibility since we no longer want to start the broker - * using the JMSServerManager which would normally deploy JMS destinations. Here we take the JMS destination - * configurations from the parsed JMS configuration and add them to the core configuration. - * - * It's also important here that we are adding them to the core ADDRESS configurations as those will be - * deployed first and therefore their configuration will take precedence over other legacy queue configurations - * which are deployed later. This is so we can maintain support for configurations like those found in the - * bridge and divert examples where there are JMS and core queues with the same name (which was itself a bit - * of a hack). - * - * This should be removed when support for the old "jms" configuation element is also removed. - */ - { - for (JMSQueueConfiguration jmsQueueConfig : jmsConfiguration.getQueueConfigurations()) { - List coreAddressConfigurations = configuration.getAddressConfigurations(); - coreAddressConfigurations.add(new CoreAddressConfiguration() - .setName(jmsQueueConfig.getName()) - .addRoutingType(RoutingType.ANYCAST) - .addQueueConfiguration(new CoreQueueConfiguration() - .setAddress(jmsQueueConfig.getName()) - .setName(jmsQueueConfig.getName()) - .setFilterString(jmsQueueConfig.getSelector()) - .setRoutingType(RoutingType.ANYCAST))); - } - - for (TopicConfiguration topicConfig : jmsConfiguration.getTopicConfigurations()) { - List coreAddressConfigurations = configuration.getAddressConfigurations(); - coreAddressConfigurations.add(new CoreAddressConfiguration() - .setName(topicConfig.getName()) - .addRoutingType(RoutingType.MULTICAST)); - } - } - components = fileDeploymentManager.buildService(securityManager, ManagementFactory.getPlatformMBeanServer()); ArrayList componentsByStartOrder = getComponentsByStartOrder(components); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java new file mode 100644 index 00000000000..bc4a741c4da --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java @@ -0,0 +1,196 @@ +/* + * 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.artemis.core.config.impl; + +import javax.management.MBeanServer; +import javax.xml.XMLConstants; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.List; +import java.util.Map; + +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.core.config.CoreAddressConfiguration; +import org.apache.activemq.artemis.core.config.CoreQueueConfiguration; +import org.apache.activemq.artemis.core.deployers.Deployable; +import org.apache.activemq.artemis.core.server.ActiveMQComponent; +import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager; +import org.apache.activemq.artemis.utils.XMLConfigurationUtil; +import org.apache.activemq.artemis.utils.XMLUtil; +import org.jboss.logging.Logger; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class LegacyJMSConfiguration implements Deployable { + + private static final Logger logger = Logger.getLogger(LegacyJMSConfiguration.class); + + private static final String CONFIGURATION_SCHEMA_URL = "schema/artemis-jms.xsd"; + + private static final String CONFIGURATION_SCHEMA_ROOT_ELEMENT = "jms"; + + private static final String NAME_ATTR = "name"; + + private static final String QUEUE_NODE_NAME = "queue"; + + private static final String QUEUE_SELECTOR_NODE_NAME = "selector"; + + private static final String TOPIC_NODE_NAME = "topic"; + + private static final String JMX_DOMAIN_NAME = "jmx-domain"; + + private static final boolean DEFAULT_QUEUE_DURABILITY = true; + + private URL configurationUrl; + + final Configuration configuration; + + + public LegacyJMSConfiguration(Configuration configuration) { + this.configuration = configuration; + } + + + @Override + public void parse(Element config, URL url) throws Exception { + parseConfiguration(config); + } + + public Configuration getConfiguration() { + return configuration; + } + + @Override + public boolean isParsed() { + // always return false here so that the FileDeploymentManager will not invoke buildService() + return false; + } + + @Override + public String getRootElement() { + return CONFIGURATION_SCHEMA_ROOT_ELEMENT; + } + + @Override + public void buildService(ActiveMQSecurityManager securityManager, + MBeanServer mBeanServer, + Map deployables, + Map components) throws Exception { + } + + @Override + public String getSchema() { + return CONFIGURATION_SCHEMA_URL; + } + + + public void parseConfiguration(final InputStream input) throws Exception { + Reader reader = new InputStreamReader(input); + String xml = XMLUtil.readerToString(reader); + xml = XMLUtil.replaceSystemProps(xml); + Element e = XMLUtil.stringToElement(xml); + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = schemaFactory.newSchema(XMLUtil.findResource(CONFIGURATION_SCHEMA_URL)); + parseConfiguration(e); + } + + /** + * Parse the JMS Configuration XML + */ + public void parseConfiguration(final Node rootnode) throws Exception { + + Element e = (Element) rootnode; + + String[] elements = new String[]{QUEUE_NODE_NAME, TOPIC_NODE_NAME}; + for (String element : elements) { + NodeList children = e.getElementsByTagName(element); + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + Node keyNode = node.getAttributes().getNamedItem(NAME_ATTR); + if (keyNode == null) { + logger.warn("Configuration missing jms key " + node); + continue; + } + + if (node.getNodeName().equals(TOPIC_NODE_NAME)) { + parseTopicConfiguration(node); + } else if (node.getNodeName().equals(QUEUE_NODE_NAME)) { + parseQueueConfiguration(node); + } + } + } + } + + /** + * Parse the topic node as a TopicConfiguration object + * + * @param node + * @return topic configuration + * @throws Exception + */ + public void parseTopicConfiguration(final Node node) throws Exception { + String topicName = node.getAttributes().getNamedItem(NAME_ATTR).getNodeValue(); + List coreAddressConfigurations = configuration.getAddressConfigurations(); + coreAddressConfigurations.add(new CoreAddressConfiguration() + .setName(topicName) + .addRoutingType(RoutingType.MULTICAST)); + } + + /** + * Parse the Queue Configuration node as a QueueConfiguration object + * + * @param node + * @return jms queue configuration + * @throws Exception + */ + public void parseQueueConfiguration(final Node node) throws Exception { + Element e = (Element) node; + NamedNodeMap atts = node.getAttributes(); + String queueName = atts.getNamedItem(NAME_ATTR).getNodeValue(); + String selectorString = null; + boolean durable = XMLConfigurationUtil.getBoolean(e, "durable", DEFAULT_QUEUE_DURABILITY); + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + + if (QUEUE_SELECTOR_NODE_NAME.equals(children.item(i).getNodeName())) { + Node selectorNode = children.item(i); + Node attNode = selectorNode.getAttributes().getNamedItem("string"); + selectorString = attNode.getNodeValue(); + } + } + + List coreAddressConfigurations = configuration.getAddressConfigurations(); + coreAddressConfigurations.add(new CoreAddressConfiguration() + .setName(queueName) + .addRoutingType(RoutingType.ANYCAST) + .addQueueConfiguration(new CoreQueueConfiguration() + .setAddress(queueName) + .setName(queueName) + .setFilterString(selectorString) + .setRoutingType(RoutingType.ANYCAST))); + } + + +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServers.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServers.java index aed594448c5..0cb1de17f8c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServers.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServers.java @@ -22,6 +22,7 @@ import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.FileDeploymentManager; import org.apache.activemq.artemis.core.config.impl.FileConfiguration; +import org.apache.activemq.artemis.core.config.impl.LegacyJMSConfiguration; import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration; import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; @@ -71,7 +72,8 @@ public static ActiveMQServer newActiveMQServer(final String configURL, final ActiveMQSecurityManager securityManager) throws Exception { FileConfiguration config = new FileConfiguration(); - new FileDeploymentManager(configURL).addDeployable(config).readConfiguration(); + LegacyJMSConfiguration legacyJMSConfiguration = new LegacyJMSConfiguration(config); + new FileDeploymentManager(configURL).addDeployable(config).addDeployable(legacyJMSConfiguration).readConfiguration(); ActiveMQServer server = ActiveMQServers.newActiveMQServer(config, mbeanServer, securityManager); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/EmbeddedActiveMQ.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/EmbeddedActiveMQ.java index 1065a49e572..8a6512608e0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/EmbeddedActiveMQ.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/EmbeddedActiveMQ.java @@ -22,6 +22,7 @@ import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.FileDeploymentManager; import org.apache.activemq.artemis.core.config.impl.FileConfiguration; +import org.apache.activemq.artemis.core.config.impl.LegacyJMSConfiguration; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.cluster.ClusterConnection; import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; @@ -122,7 +123,8 @@ protected void initStart() throws Exception { configResourcePath = "broker.xml"; FileDeploymentManager deploymentManager = new FileDeploymentManager(configResourcePath); FileConfiguration config = new FileConfiguration(); - deploymentManager.addDeployable(config); + LegacyJMSConfiguration legacyJMSConfiguration = new LegacyJMSConfiguration(config); + deploymentManager.addDeployable(config).addDeployable(legacyJMSConfiguration); deploymentManager.readConfiguration(); configuration = config; } 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 9359eccd2f6..4a4ebb66fde 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 @@ -66,6 +66,7 @@ import org.apache.activemq.artemis.core.config.HAPolicyConfiguration; import org.apache.activemq.artemis.core.config.StoreConfiguration; import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.config.impl.LegacyJMSConfiguration; import org.apache.activemq.artemis.core.config.storage.DatabaseStorageConfiguration; import org.apache.activemq.artemis.core.deployers.impl.FileConfigurationParser; import org.apache.activemq.artemis.core.filter.Filter; @@ -3151,6 +3152,9 @@ private final class ConfigurationFileReloader implements ReloadCallback { public void reload(URL uri) throws Exception { if (isActive()) { Configuration config = new FileConfigurationParser().parseMainConfig(uri.openStream()); + LegacyJMSConfiguration legacyJMSConfiguration = new LegacyJMSConfiguration(config); + legacyJMSConfiguration.parseConfiguration(uri.openStream()); + ActiveMQServerLogger.LOGGER.reloadingConfiguration("security"); securityRepository.swap(config.getSecurityRoles().entrySet()); configuration.setSecurityRoles(config.getSecurityRoles()); From bfc9aec33b10582f85e7c2d525681ddf7455ddad Mon Sep 17 00:00:00 2001 From: Codrin Bucur Date: Wed, 18 Jul 2018 11:22:44 +0200 Subject: [PATCH 086/207] NO-JIRA spelling fixes on docs --- docs/user-manual/en/clusters.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user-manual/en/clusters.md b/docs/user-manual/en/clusters.md index 54dc54e19b6..4aa21540379 100644 --- a/docs/user-manual/en/clusters.md +++ b/docs/user-manual/en/clusters.md @@ -389,13 +389,13 @@ connection. The default value for this parameter is `10000` milliseconds. Sometimes it may be impossible to use UDP on the network you are using. In this case its possible to configure a connection with an initial list -if possible servers. This could be just one server that you know will +of possible servers. This could be just one server that you know will always be available or a list of servers where at least one will be available. This doesn't mean that you have to know where all your servers are going to be hosted, you can configure these servers to use the reliable -servers to connect to. Once they are connected there connection details +servers to connect to. Once they are connected their connection details will be propagated via the server it connects to #### Configuring a Cluster Connection From 993499daafbe546dbeae22febeeb5ad98b140396 Mon Sep 17 00:00:00 2001 From: JiriOndrusek Date: Mon, 23 Jul 2018 14:17:24 +0200 Subject: [PATCH 087/207] [ARTEMIS-1986] PagingTest#testDeletePhysicalPages will fail if a record about deleting a page is not saved in journal --- .../tests/integration/paging/PagingTest.java | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java index 566142d5d46..1436bea0312 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java @@ -1585,6 +1585,154 @@ public void testDeletePhysicalPages() throws Exception { } + // 4 messages are send/received, it creates 2 pages, where for second page there is no delete completion record in journal + // server is restarted and 4 messages sent/received again. There should be no lost message. + @Test + public void testRestartWithCompleteAndDeletedPhysicalPage() throws Exception { + clearDataRecreateServerDirs(); + + Configuration config = createDefaultInVMConfig(); + + final AtomicBoolean mainCleanup = new AtomicBoolean(true); + + class InterruptedCursorProvider extends PageCursorProviderImpl { + + InterruptedCursorProvider(PagingStore pagingStore, + StorageManager storageManager, + ArtemisExecutor executor, + int maxCacheSize) { + super(pagingStore, storageManager, executor, maxCacheSize); + } + + @Override + public void cleanup() { + if (mainCleanup.get()) { + super.cleanup(); + } else { + try { + pagingStore.unlock(); + } catch (Throwable ignored) { + } + } + } + } + + server = new ActiveMQServerImpl(config, ManagementFactory.getPlatformMBeanServer(), new ActiveMQSecurityManagerImpl()) { + @Override + protected PagingStoreFactoryNIO getPagingStoreFactory() { + return new PagingStoreFactoryNIO(this.getStorageManager(), this.getConfiguration().getPagingLocation(), this.getConfiguration().getJournalBufferTimeout_NIO(), this.getScheduledPool(), this.getExecutorFactory(), this.getConfiguration().isJournalSyncNonTransactional(), null) { + @Override + public PageCursorProvider newCursorProvider(PagingStore store, + StorageManager storageManager, + AddressSettings addressSettings, + ArtemisExecutor executor) { + return new InterruptedCursorProvider(store, storageManager, executor, addressSettings.getPageCacheMaxSize()); + } + }; + } + + }; + + addServer(server); + + AddressSettings defaultSetting = new AddressSettings().setPageSizeBytes(MESSAGE_SIZE). + setMaxSizeBytes(2 * MESSAGE_SIZE).setAddressFullMessagePolicy(AddressFullMessagePolicy.PAGE); + + server.getAddressSettingsRepository().addMatch("#", defaultSetting); + + server.start(); + + locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setBlockOnAcknowledge(true); + + sf = createSessionFactory(locator); + ClientSession session = sf.createSession(true, true, 0); + session.createQueue(PagingTest.ADDRESS, PagingTest.ADDRESS, null, true); + + Queue queue = server.locateQueue(ADDRESS); + + ClientProducer producer = session.createProducer(PagingTest.ADDRESS); + + ClientMessage message; + + for (int i = 0; i < 4; i++) { + message = session.createMessage(true); + + ActiveMQBuffer bodyLocal = message.getBodyBuffer(); + + bodyLocal.writeBytes(new byte[MESSAGE_SIZE]); + + producer.send(message); + session.commit(); + + //last page (#2, whch contains only message #3) is marked as complete - is full - but no delete complete record is added + if (i == 3) { + queue.getPageSubscription().getPagingStore().forceAnotherPage(); + } + + } + + Assert.assertEquals(3, queue.getPageSubscription().getPagingStore().getCurrentWritingPage()); + + ClientConsumer consumer = session.createConsumer(ADDRESS); + session.start(); + + for (int i = 0; i < 4; i++) { + message = consumer.receive(5000); + Assert.assertNotNull("Before restart - message " + i + " is empty.",message); + message.acknowledge(); + } + + + + server.stop(); + mainCleanup.set(false); + + + + // Deleting the paging data. Simulating a failure + // a dumb user, or anything that will remove the data + deleteDirectory(new File(getPageDir())); + + logger.trace("Server restart"); + + server.start(); + + locator = createInVMNonHALocator(); + sf = createSessionFactory(locator); + session = sf.createSession(null, null, false, false, true, false, 0); + producer = session.createProducer(PagingTest.ADDRESS); + + for (int i = 0; i < 4; i++) { + message = session.createMessage(true); + + ActiveMQBuffer bodyLocal = message.getBodyBuffer(); + + bodyLocal.writeBytes(new byte[MESSAGE_SIZE]); + + + producer.send(message); + } + session.commit(); + + mainCleanup.set(true); + + queue = server.locateQueue(ADDRESS); + queue.getPageSubscription().cleanupEntries(false); + queue.getPageSubscription().getPagingStore().getCursorProvider().cleanup(); + + consumer = session.createConsumer(ADDRESS); + session.start(); + + for (int i = 0; i < 4; i++) { + message = consumer.receive(5000); + Assert.assertNotNull("After restart - message " + i + " is empty.",message); + message.acknowledge(); + } + + server.stop(); + + } + @Test public void testMissingTXEverythingAcked() throws Exception { clearDataRecreateServerDirs(); From 66ba17846fbdafa6716f73386c2562ec776b144d Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Wed, 25 Jul 2018 14:44:09 +0100 Subject: [PATCH 088/207] ARTEMIS-1988 Do not clear Activate Callbacks on stop This is reverting 36fc14a30d6f1128c525c91cc9f8752fdd3f540c --- .../core/server/impl/ActiveMQServerImpl.java | 5 +- .../server/ClearActivateCallbackTest.java | 46 ------------------- 2 files changed, 2 insertions(+), 49 deletions(-) delete mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ClearActivateCallbackTest.java 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 4a4ebb66fde..fb7fbccb6a9 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 @@ -1110,8 +1110,6 @@ void stop(boolean failoverOnServerShutdown, final boolean criticalIOError, boole sessions.clear(); - activateCallbacks.clear(); - state = SERVER_STATE.STOPPED; activationLatch.setCount(1); @@ -3187,4 +3185,5 @@ public Set getActivateCallbacks() { public List getExternalComponents() { return externalComponents; } -} \ No newline at end of file + +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ClearActivateCallbackTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ClearActivateCallbackTest.java deleted file mode 100644 index 273f21e7e46..00000000000 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ClearActivateCallbackTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.artemis.tests.integration.server; - -import org.apache.activemq.artemis.core.server.ActivateCallback; -import org.apache.activemq.artemis.core.server.ActiveMQServer; -import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; -import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; -import org.junit.Test; - -/** - * A simple test-case used for documentation purposes. - */ -public class ClearActivateCallbackTest extends ActiveMQTestBase { - - protected ActiveMQServer server; - - @Test - public void simpleTest() throws Exception { - server = createServer(false, createDefaultNettyConfig()); - server.start(); - int initialCallbackCount = ((ActiveMQServerImpl) server).getActivateCallbacks().size(); - server.registerActivateCallback(new ActivateCallback() { - }); - assertEquals(1, ((ActiveMQServerImpl) server).getActivateCallbacks().size() - initialCallbackCount); - server.stop(); - assertEquals(0, ((ActiveMQServerImpl) server).getActivateCallbacks().size()); - server.start(); - assertEquals(initialCallbackCount, ((ActiveMQServerImpl) server).getActivateCallbacks().size()); - } -} From 56be281aafdbfaea2059c0ed5bd1e065f7710124 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 24 Jul 2018 21:54:18 -0400 Subject: [PATCH 089/207] ARTEMIS-1989 Replication catch up leaking files Related commit that broke this at https://github.com/hornetq/hornetq/commit/837694e70573069cf78d1911975bef95925b6f29 --- .../core/io/nio/NIOSequentialFile.java | 2 +- .../core/io/nio/NIOSequentialFileFactory.java | 2 +- .../core/paging/impl/PagingManagerImpl.java | 13 +- .../paging/impl/PagingStoreFactoryNIO.java | 38 ++++- .../core/replication/ReplicationEndpoint.java | 9 +- .../artemis/core/server/ActiveMQServer.java | 2 + .../core/server/impl/ActiveMQServerImpl.java | 3 +- ...aredNothingReplicationFlowControlTest.java | 139 +++++++++++++++++- 8 files changed, 193 insertions(+), 15 deletions(-) diff --git a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/nio/NIOSequentialFile.java b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/nio/NIOSequentialFile.java index 891bd5cce27..55654b7d5ee 100644 --- a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/nio/NIOSequentialFile.java +++ b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/nio/NIOSequentialFile.java @@ -37,7 +37,7 @@ import org.apache.activemq.artemis.journal.ActiveMQJournalLogger; import org.apache.activemq.artemis.utils.Env; -public final class NIOSequentialFile extends AbstractSequentialFile { +public class NIOSequentialFile extends AbstractSequentialFile { private FileChannel channel; diff --git a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/nio/NIOSequentialFileFactory.java b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/nio/NIOSequentialFileFactory.java index b585b246676..c14237771ac 100644 --- a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/nio/NIOSequentialFileFactory.java +++ b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/nio/NIOSequentialFileFactory.java @@ -29,7 +29,7 @@ import org.apache.activemq.artemis.utils.Env; import org.apache.activemq.artemis.utils.critical.CriticalAnalyzer; -public final class NIOSequentialFileFactory extends AbstractSequentialFileFactory { +public class NIOSequentialFileFactory extends AbstractSequentialFileFactory { private static final int DEFAULT_CAPACITY_ALIGNMENT = Env.osPageSize(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java index 44e806742ff..bca70cf170c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java @@ -62,7 +62,7 @@ public final class PagingManagerImpl implements PagingManager { private final HierarchicalRepository addressSettingsRepository; - private final PagingStoreFactory pagingStoreFactory; + private PagingStoreFactory pagingStoreFactory; private final AtomicLong globalSizeBytes = new AtomicLong(0); @@ -84,6 +84,17 @@ public final class PagingManagerImpl implements PagingManager { // Constructors // -------------------------------------------------------------------------------------------------------------------- + + // for tests.. not part of the API + public void replacePageStoreFactory(PagingStoreFactory factory) { + this.pagingStoreFactory = factory; + } + + // for tests.. not part of the API + public PagingStoreFactory getPagingStoreFactory() { + return pagingStoreFactory; + } + public PagingManagerImpl(final PagingStoreFactory pagingSPI, final HierarchicalRepository addressSettingsRepository, final long maxSize) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreFactoryNIO.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreFactoryNIO.java index b2e3d4f923d..aa71c0ecc86 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreFactoryNIO.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreFactoryNIO.java @@ -63,7 +63,7 @@ public class PagingStoreFactoryNIO implements PagingStoreFactory { private final ExecutorFactory executorFactory; - protected final boolean syncNonTransactional; + private final boolean syncNonTransactional; private PagingManager pagingManager; @@ -71,10 +71,38 @@ public class PagingStoreFactoryNIO implements PagingStoreFactory { private final long syncTimeout; - protected final StorageManager storageManager; + private final StorageManager storageManager; private final IOCriticalErrorListener critialErrorListener; + public File getDirectory() { + return directory; + } + + public ExecutorFactory getExecutorFactory() { + return executorFactory; + } + + public boolean isSyncNonTransactional() { + return syncNonTransactional; + } + + public PagingManager getPagingManager() { + return pagingManager; + } + + public long getSyncTimeout() { + return syncTimeout; + } + + public StorageManager getStorageManager() { + return storageManager; + } + + public IOCriticalErrorListener getCritialErrorListener() { + return critialErrorListener; + } + public PagingStoreFactoryNIO(final StorageManager storageManager, final File directory, final long syncTimeout, @@ -135,9 +163,7 @@ public synchronized SequentialFileFactory newFileFactory(final SimpleString addr factory.createDirs(); - File fileWithID = new File(directory, guid + - File.separatorChar + - PagingStoreFactoryNIO.ADDRESS_FILE); + File fileWithID = new File(directory, guid + File.separatorChar + PagingStoreFactoryNIO.ADDRESS_FILE); try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileWithID)))) { writer.write(address.toString()); @@ -197,7 +223,7 @@ public List reloadStores(final HierarchicalRepository liveServer.isStarted()); + + ServerLocator locator = ServerLocatorImpl.newLocator("tcp://localhost:61616"); + locator.setCallTimeout(60_000L); + locator.setConnectionTTL(60_000L); + + final ClientSessionFactory csf = locator.createSessionFactory(); + ClientSession sess = csf.createSession(); + sess.createQueue("flowcontrol", RoutingType.ANYCAST, "flowcontrol", true); + + PagingStore store = liveServer.getPagingManager().getPageStore(SimpleString.toSimpleString("flowcontrol")); + store.startPaging(); + + ClientProducer prod = sess.createProducer("flowcontrol"); + for (int i = 0; i < 100; i++) { + prod.send(sess.createMessage(true)); + + if (i % 10 == 0) { + sess.commit(); + store.forceAnotherPage(); + } + } + + sess.close(); + + openCount.set(0); + closeCount.set(0); + // start backup + Configuration backupConfiguration = createBackupConfiguration().setNetworkCheckURLList(null); + + ActiveMQServer backupServer = new ActiveMQServerImpl(backupConfiguration, ManagementFactory.getPlatformMBeanServer(), new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), new SecurityConfiguration())) { + @Override + public PagingManager createPagingManager() throws Exception { + PagingManagerImpl manager = (PagingManagerImpl) super.createPagingManager(); + PagingStoreFactoryNIO originalPageStore = (PagingStoreFactoryNIO) manager.getPagingStoreFactory(); + manager.replacePageStoreFactory(new PageStoreFactoryTestable(originalPageStore)); + return manager; + } + }; + + addServer(backupServer).start(); + + Wait.waitFor(() -> backupServer.isStarted()); + + Wait.waitFor(backupServer::isReplicaSync, 30000); + + PageStoreFactoryTestable testablePageStoreFactory = (PageStoreFactoryTestable) ((PagingManagerImpl) backupServer.getPagingManager()).getPagingStoreFactory(); + + Assert.assertEquals(openCount.get(), closeCount.get()); + } + + static AtomicInteger openCount = new AtomicInteger(0); + static AtomicInteger closeCount = new AtomicInteger(0); + + private static class PageStoreFactoryTestable extends PagingStoreFactoryNIO { + + PageStoreFactoryTestable(StorageManager storageManager, + File directory, + long syncTimeout, + ScheduledExecutorService scheduledExecutor, + ExecutorFactory executorFactory, + boolean syncNonTransactional, + IOCriticalErrorListener critialErrorListener) { + super(storageManager, directory, syncTimeout, scheduledExecutor, executorFactory, syncNonTransactional, critialErrorListener); + } + + PageStoreFactoryTestable(PagingStoreFactoryNIO other) { + this(other.getStorageManager(), other.getDirectory(), other.getSyncTimeout(), other.getScheduledExecutor(), other.getExecutorFactory(), other.isSyncNonTransactional(), other.getCritialErrorListener()); + } + + @Override + protected SequentialFileFactory newFileFactory(String directoryName) { + return new TestableNIOFactory(new File(getDirectory(), directoryName), false, getCritialErrorListener(), 1); + } + } + + public static class TestableNIOFactory extends NIOSequentialFileFactory { + + public TestableNIOFactory(File journalDir, boolean buffered, IOCriticalErrorListener listener, int maxIO) { + super(journalDir, buffered, listener, maxIO); + } + + @Override + public SequentialFile createSequentialFile(String fileName) { + return new TestableSequentialFile(this, journalDir, fileName, maxIO, writeExecutor); + } + } + + public static class TestableSequentialFile extends NIOSequentialFile { + + public TestableSequentialFile(SequentialFileFactory factory, + File directory, + String file, + int maxIO, + Executor writerExecutor) { + super(factory, directory, file, maxIO, writerExecutor); + } + + @Override + public void open(int maxIO, boolean useExecutor) throws IOException { + super.open(maxIO, useExecutor); + openCount.incrementAndGet(); + } + + @Override + public synchronized void close() throws IOException, InterruptedException, ActiveMQException { + super.close(); + closeCount.incrementAndGet(); + } + } + // Set a small call timeout and write buffer high water mark value to trigger replication flow control private Configuration createLiveConfiguration() throws Exception { Configuration conf = new ConfigurationImpl(); From 5987db820b5710002fea5c4f2e25576510faac9f Mon Sep 17 00:00:00 2001 From: Zachary Simon Date: Mon, 23 Jul 2018 16:26:07 -0700 Subject: [PATCH 090/207] ARTEMIS-1990 remove extra quotes in Windows script A double set of quotation marks were preventing successful Windows implementation. --- .../org/apache/activemq/artemis/cli/commands/bin/artemis.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/bin/artemis.cmd b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/bin/artemis.cmd index 6057a51bdd7..7d7c5863f68 100644 --- a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/bin/artemis.cmd +++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/bin/artemis.cmd @@ -59,7 +59,7 @@ set JVM_ARGS=%JVM_ARGS% -classpath %ARTEMIS_HOME%\lib\artemis-boot.jar set JVM_ARGS=%JVM_ARGS% -Dartemis.home=%ARTEMIS_HOME% set JVM_ARGS=%JVM_ARGS% -Dartemis.instance=%ARTEMIS_INSTANCE% set JVM_ARGS=%JVM_ARGS% -Ddata.dir=%ARTEMIS_DATA_DIR% -set JVM_ARGS=%JVM_ARGS% -Dartemis.instance.etc="%ARTEMIS_INSTANCE_ETC%" +set JVM_ARGS=%JVM_ARGS% -Dartemis.instance.etc=%ARTEMIS_INSTANCE_ETC% set JVM_ARGS=%JVM_ARGS% -Djava.util.logging.manager=%ARTEMIS_LOG_MANAGER% set JVM_ARGS=%JVM_ARGS% -Dlogging.configuration=%ARTEMIS_LOGGING_CONF% if not "%DEBUG_ARGS%"=="" set JVM_ARGS=%JVM_ARGS% %DEBUG_ARGS% From 8a9835a3947f99ea832fb31fd763174697c3570b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Fri, 6 Jul 2018 07:44:15 +0100 Subject: [PATCH 091/207] ARTEMIS-856 - Support consumersBeforeDispatch and delayBeforeDispatch https://issues.apache.org/jira/browse/ARTEMIS-856 This is equivalent to consumersBeforeDispatchStarts and timeBeforeDispatchStarts in ActiveMQ 5.x http://activemq.apache.org/message-groups.html This is addressing one of the items on the artemis roadmap: http://activemq.apache.org/activemq-artemis-roadmap.html --- .../activemq/artemis/utils/BooleanUtil.java | 28 +++ .../config/ActiveMQDefaultConfiguration.java | 12 + .../management/ActiveMQServerControl.java | 61 +++++ .../core/config/CoreQueueConfiguration.java | 52 +++- .../impl/FileConfigurationParser.java | 20 +- .../impl/ActiveMQServerControlImpl.java | 33 ++- .../core/persistence/QueueBindingInfo.java | 8 + .../AbstractJournalStorageManager.java | 2 +- .../codec/PersistentQueueBindingEncoding.java | 48 +++- .../artemis/core/postoffice/PostOffice.java | 2 + .../core/postoffice/impl/PostOfficeImpl.java | 10 + .../artemis/core/server/ActiveMQServer.java | 27 +++ .../activemq/artemis/core/server/Queue.java | 14 ++ .../artemis/core/server/QueueConfig.java | 39 ++- .../core/server/impl/ActiveMQServerImpl.java | 125 ++++++++-- .../core/server/impl/LastValueQueue.java | 4 +- .../server/impl/PostOfficeJournalLoader.java | 2 + .../core/server/impl/QueueFactoryImpl.java | 6 +- .../artemis/core/server/impl/QueueImpl.java | 174 +++++++++++--- .../core/settings/impl/AddressSettings.java | 70 +++++- .../schema/artemis-configuration.xsd | 20 ++ .../config/impl/FileConfigurationTest.java | 10 +- .../impl/ScheduledDeliveryHandlerTest.java | 35 +++ .../test/resources/artemis-configuration.xsd | 20 ++ .../jms/client/ConsumerDelayDispatchTest.java | 223 ++++++++++++++++++ .../ActiveMQServerControlUsingCoreTest.java | 10 + .../persistence/QueueConfigRestartTest.java | 46 ++++ .../unit/core/postoffice/impl/FakeQueue.java | 35 +++ .../server/impl/fakes/FakePostOffice.java | 2 + 29 files changed, 1064 insertions(+), 74 deletions(-) create mode 100644 artemis-commons/src/main/java/org/apache/activemq/artemis/utils/BooleanUtil.java create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ConsumerDelayDispatchTest.java diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/BooleanUtil.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/BooleanUtil.java new file mode 100644 index 00000000000..91c02adac51 --- /dev/null +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/BooleanUtil.java @@ -0,0 +1,28 @@ +/* + * 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.artemis.utils; + +public class BooleanUtil { + + public static int toInt(boolean value) { + return value ? 1 : 0; + } + + public static boolean toBoolean(int value) { + return value != 0; + } +} diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java index 70ba3149ddf..45397f386c9 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java @@ -473,6 +473,10 @@ public static String getDefaultHapolicyBackupStrategy() { public static final boolean DEFAULT_PURGE_ON_NO_CONSUMERS = false; + public static final int DEFAULT_CONSUMERS_BEFORE_DISPATCH = 0; + + public static final long DEFAULT_DELAY_BEFORE_DISPATCH = -1; + public static final RoutingType DEFAULT_ROUTING_TYPE = RoutingType.MULTICAST; public static final String DEFAULT_SYSTEM_PROPERTY_PREFIX = "brokerconfig."; @@ -1302,6 +1306,14 @@ public static boolean getDefaultPurgeOnNoConsumers() { return DEFAULT_PURGE_ON_NO_CONSUMERS; } + public static int getDefaultConsumersBeforeDispatch() { + return DEFAULT_CONSUMERS_BEFORE_DISPATCH; + } + + public static long getDefaultDelayBeforeDispatch() { + return DEFAULT_DELAY_BEFORE_DISPATCH; + } + public static String getInternalNamingPrefix() { return DEFAULT_INTERNAL_NAMING_PREFIX; } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java index 234a2d514c8..6ce945c4113 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java @@ -581,6 +581,10 @@ void createQueue(@Parameter(name = "address", desc = "Address of the queue") Str * @param durable is the queue durable? * @param maxConsumers the maximum number of consumers allowed on this queue at any one time * @param purgeOnNoConsumers delete this queue when the last consumer disconnects + * @param exclusive if the queue should route exclusively to one consumer + * @param lastValue use last-value semantics + * @param consumersBeforeDispatch number of consumers needed before dispatch can start + * @param delayBeforeDispatch delay to wait before dispatching if number of consumers before dispatch is not met * @param autoCreateAddress create an address with default values should a matching address not be found * @return a textual summary of the queue * @throws Exception @@ -593,8 +597,41 @@ String createQueue(@Parameter(name = "address", desc = "Address of the queue") S @Parameter(name = "durable", desc = "Is the queue durable?") boolean durable, @Parameter(name = "maxConsumers", desc = "The maximum number of consumers allowed on this queue at any one time") int maxConsumers, @Parameter(name = "purgeOnNoConsumers", desc = "Delete this queue when the last consumer disconnects") boolean purgeOnNoConsumers, + @Parameter(name = "exclusive", desc = "If the queue should route exclusively to one consumer") boolean exclusive, + @Parameter(name = "lastValue", desc = "Use last-value semantics") boolean lastValue, + @Parameter(name = "consumersBeforeDispatch", desc = "Number of consumers needed before dispatch can start") int consumersBeforeDispatch, + @Parameter(name = "delayBeforeDispatch", desc = "Delay to wait before dispatching if number of consumers before dispatch is not met") long delayBeforeDispatch, @Parameter(name = "autoCreateAddress", desc = "Create an address with default values should a matching address not be found") boolean autoCreateAddress) throws Exception; + /** + * Create a queue. + *
+ * If {@code address} is {@code null} it will be defaulted to {@code name}. + *
+ * This method throws a {@link org.apache.activemq.artemis.api.core.ActiveMQQueueExistsException}) exception if the queue already exits. + * + * @param address address to bind the queue to + * @param routingType the routing type used for this address, {@code MULTICAST} or {@code ANYCAST} + * @param name name of the queue + * @param filterStr filter of the queue + * @param durable is the queue durable? + * @param maxConsumers the maximum number of consumers allowed on this queue at any one time + * @param purgeOnNoConsumers delete this queue when the last consumer disconnects + * @param autoCreateAddress create an address with default values should a matching address not be found + * @return a textual summary of the queue + * @throws Exception + */ + @Operation(desc = "Create a queue", impact = MBeanOperationInfo.ACTION) + String createQueue(@Parameter(name = "address", desc = "Address of the queue") String address, + @Parameter(name = "routingType", desc = "The routing type used for this address, MULTICAST or ANYCAST") String routingType, + @Parameter(name = "name", desc = "Name of the queue") String name, + @Parameter(name = "filter", desc = "Filter of the queue") String filterStr, + @Parameter(name = "durable", desc = "Is the queue durable?") boolean durable, + @Parameter(name = "maxConsumers", desc = "The maximum number of consumers allowed on this queue at any one time") int maxConsumers, + @Parameter(name = "purgeOnNoConsumers", desc = "Delete this queue when the last consumer disconnects") boolean purgeOnNoConsumers, + @Parameter(name = "autoCreateAddress", desc = "Create an address with default values should a matching address not be found") boolean autoCreateAddress) throws Exception; + + /** * Update a queue. * @@ -651,6 +688,30 @@ String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String @Parameter(name = "exclusive", desc = "If the queue should route exclusively to one consumer") Boolean exclusive, @Parameter(name = "user", desc = "The user associated with this queue") String user) throws Exception; + /** + * Update a queue + * + * @param name name of the queue + * @param routingType the routing type used for this address, {@code MULTICAST} or {@code ANYCAST} + * @param maxConsumers the maximum number of consumers allowed on this queue at any one time + * @param purgeOnNoConsumers delete this queue when the last consumer disconnects + * @param exclusive if the queue should route exclusively to one consumer + * @param consumersBeforeDispatch number of consumers needed before dispatch can start + * @param delayBeforeDispatch delay to wait before dispatching if number of consumers before dispatch is not met + * @param user the user associated with this queue + * @return + * @throws Exception + */ + @Operation(desc = "Update a queue", impact = MBeanOperationInfo.ACTION) + String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String name, + @Parameter(name = "routingType", desc = "The routing type used for this address, MULTICAST or ANYCAST") String routingType, + @Parameter(name = "maxConsumers", desc = "The maximum number of consumers allowed on this queue at any one time") Integer maxConsumers, + @Parameter(name = "purgeOnNoConsumers", desc = "Delete this queue when the last consumer disconnects") Boolean purgeOnNoConsumers, + @Parameter(name = "exclusive", desc = "If the queue should route exclusively to one consumer") Boolean exclusive, + @Parameter(name = "consumersBeforeDispatch", desc = "Number of consumers needed before dispatch can start") Integer consumersBeforeDispatch, + @Parameter(name = "delayBeforeDispatch", desc = "Delay to wait before dispatching if number of consumers before dispatch is not met") Long delayBeforeDispatch, + @Parameter(name = "user", desc = "The user associated with this queue") String user) throws Exception; + /** * Deploy a durable queue. *
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java index f301b90e8e8..2ccae2df0a5 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java @@ -39,14 +39,16 @@ public class CoreQueueConfiguration implements Serializable { private Boolean lastValue; - private Integer maxConsumers = ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(); + private Integer maxConsumers; + + private Integer consumersBeforeDispatch; + + private Long delayBeforeDispatch; private Boolean purgeOnNoConsumers = ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(); private RoutingType routingType = ActiveMQDefaultConfiguration.getDefaultRoutingType(); - private boolean maxConsumerConfigured = false; - public CoreQueueConfiguration() { } @@ -78,13 +80,12 @@ public Boolean isLastValue() { return lastValue; } - public boolean isMaxConsumerConfigured() { - return maxConsumerConfigured; + public Integer getConsumersBeforeDispatch() { + return consumersBeforeDispatch; } - public CoreQueueConfiguration setMaxConsumerConfigured(boolean maxConsumerConfigured) { - this.maxConsumerConfigured = maxConsumerConfigured; - return this; + public Long getDelayBeforeDispatch() { + return delayBeforeDispatch; } /** @@ -127,6 +128,22 @@ public CoreQueueConfiguration setMaxConsumers(Integer maxConsumers) { return this; } + /** + * @param consumersBeforeDispatch for this queue, default is 0 (dispatch as soon as 1 consumer) + */ + public CoreQueueConfiguration setConsumersBeforeDispatch(Integer consumersBeforeDispatch) { + this.consumersBeforeDispatch = consumersBeforeDispatch; + return this; + } + + /** + * @param delayBeforeDispatch for this queue, default is 0 (start dispatch with no delay) + */ + public CoreQueueConfiguration setDelayBeforeDispatch(Long delayBeforeDispatch) { + this.delayBeforeDispatch = delayBeforeDispatch; + return this; + } + /** * @param purgeOnNoConsumers delete this queue when consumer count reaches 0, default is false */ @@ -157,7 +174,7 @@ public boolean getPurgeOnNoConsumers() { return purgeOnNoConsumers; } - public int getMaxConsumers() { + public Integer getMaxConsumers() { return maxConsumers; } @@ -182,7 +199,8 @@ public int hashCode() { result = prime * result + ((purgeOnNoConsumers == null) ? 0 : purgeOnNoConsumers.hashCode()); result = prime * result + ((exclusive == null) ? 0 : exclusive.hashCode()); result = prime * result + ((lastValue == null) ? 0 : lastValue.hashCode()); - result = prime * result + (maxConsumerConfigured ? 1331 : 1337); + result = prime * result + ((consumersBeforeDispatch == null) ? 0 : consumersBeforeDispatch.hashCode()); + result = prime * result + ((delayBeforeDispatch == null) ? 0 : delayBeforeDispatch.hashCode()); return result; } @@ -202,8 +220,6 @@ public boolean equals(Object obj) { return false; if (durable != other.durable) return false; - if (maxConsumerConfigured != other.maxConsumerConfigured) - return false; if (filterString == null) { if (other.filterString != null) return false; @@ -237,6 +253,18 @@ public boolean equals(Object obj) { } else if (!lastValue.equals(other.lastValue)) { return false; } + if (consumersBeforeDispatch == null) { + if (other.consumersBeforeDispatch != null) + return false; + } else if (!consumersBeforeDispatch.equals(other.consumersBeforeDispatch)) { + return false; + } + if (delayBeforeDispatch == null) { + if (other.delayBeforeDispatch != null) + return false; + } else if (!delayBeforeDispatch.equals(other.delayBeforeDispatch)) { + return false; + } return true; } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java index 27badac4d02..6c6a94cce87 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java @@ -183,6 +183,10 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { private static final String DEFAULT_EXCLUSIVE_NODE_NAME = "default-exclusive-queue"; + private static final String DEFAULT_CONSUMERS_BEFORE_DISPATCH = "default-consumers-before-dispatch"; + + private static final String DEFAULT_DELAY_BEFORE_DISPATCH = "default-delay-before-dispatch"; + private static final String REDISTRIBUTION_DELAY_NODE_NAME = "redistribution-delay"; private static final String SEND_TO_DLA_ON_NO_ROUTE = "send-to-dla-on-no-route"; @@ -1050,6 +1054,10 @@ protected Pair parseAddressSettings(final Node node) { addressSettings.setDefaultPurgeOnNoConsumers(XMLUtil.parseBoolean(child)); } else if (DEFAULT_MAX_CONSUMERS.equalsIgnoreCase(name)) { addressSettings.setDefaultMaxConsumers(XMLUtil.parseInt(child)); + } else if (DEFAULT_CONSUMERS_BEFORE_DISPATCH.equalsIgnoreCase(name)) { + addressSettings.setDefaultConsumersBeforeDispatch(XMLUtil.parseInt(child)); + } else if (DEFAULT_DELAY_BEFORE_DISPATCH.equalsIgnoreCase(name)) { + addressSettings.setDefaultDelayBeforeDispatch(XMLUtil.parseLong(child)); } else if (DEFAULT_QUEUE_ROUTING_TYPE.equalsIgnoreCase(name)) { String value = getTrimmedTextContent(child); Validators.ROUTING_TYPE.validate(DEFAULT_QUEUE_ROUTING_TYPE, value); @@ -1093,12 +1101,13 @@ protected CoreQueueConfiguration parseQueueConfiguration(final Node node) { String address = null; String filterString = null; boolean durable = true; - boolean maxConumserConfigured = false; - int maxConsumers = ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(); + Integer maxConsumers = null; boolean purgeOnNoConsumers = ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(); String user = null; Boolean exclusive = null; Boolean lastValue = null; + Integer consumersBeforeDispatch = null; + Long delayBeforeDispatch = null; NamedNodeMap attributes = node.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { @@ -1106,13 +1115,16 @@ protected CoreQueueConfiguration parseQueueConfiguration(final Node node) { if (item.getNodeName().equals("max-consumers")) { maxConsumers = Integer.parseInt(item.getNodeValue()); Validators.MAX_QUEUE_CONSUMERS.validate(name, maxConsumers); - maxConumserConfigured = true; } else if (item.getNodeName().equals("purge-on-no-consumers")) { purgeOnNoConsumers = Boolean.parseBoolean(item.getNodeValue()); } else if (item.getNodeName().equals("exclusive")) { exclusive = Boolean.parseBoolean(item.getNodeValue()); } else if (item.getNodeName().equals("last-value")) { lastValue = Boolean.parseBoolean(item.getNodeValue()); + } else if (item.getNodeName().equals("consumers-before-dispatch")) { + consumersBeforeDispatch = Integer.parseInt(item.getNodeValue()); + } else if (item.getNodeName().equals("delay-before-dispatch")) { + delayBeforeDispatch = Long.parseLong(item.getNodeValue()); } } @@ -1132,7 +1144,7 @@ protected CoreQueueConfiguration parseQueueConfiguration(final Node node) { } return new CoreQueueConfiguration().setAddress(address).setName(name).setFilterString(filterString).setDurable(durable).setMaxConsumers(maxConsumers).setPurgeOnNoConsumers(purgeOnNoConsumers).setUser(user) - .setExclusive(exclusive).setLastValue(lastValue).setMaxConsumerConfigured(maxConumserConfigured); + .setExclusive(exclusive).setLastValue(lastValue).setConsumersBeforeDispatch(consumersBeforeDispatch).setDelayBeforeDispatch(delayBeforeDispatch); } protected CoreAddressConfiguration parseAddressConfiguration(final Node node) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java index 9ed7acdf3de..ee190c62d42 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java @@ -806,6 +806,23 @@ public String createQueue(String address, int maxConsumers, boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception { + AddressSettings addressSettings = server.getAddressSettingsRepository().getMatch(address == null ? name : address); + return createQueue(address, routingType, name, filterStr, durable, addressSettings.getDefaultMaxConsumers(), addressSettings.isDefaultPurgeOnNoConsumers(), addressSettings.isDefaultExclusiveQueue(), addressSettings.isDefaultLastValueQueue(), addressSettings.getDefaultConsumersBeforeDispatch(), addressSettings.getDefaultDelayBeforeDispatch(), addressSettings.isAutoCreateAddresses()); + } + + @Override + public String createQueue(String address, + String routingType, + String name, + String filterStr, + boolean durable, + int maxConsumers, + boolean purgeOnNoConsumers, + boolean exclusive, + boolean lastValue, + int consumersBeforeDispatch, + long delayBeforeDispatch, + boolean autoCreateAddress) throws Exception { checkStarted(); clearIO(); @@ -816,7 +833,7 @@ public String createQueue(String address, filter = new SimpleString(filterStr); } - final Queue queue = server.createQueue(SimpleString.toSimpleString(address), RoutingType.valueOf(routingType.toUpperCase()), new SimpleString(name), filter, durable, false, maxConsumers, purgeOnNoConsumers, autoCreateAddress); + final Queue queue = server.createQueue(SimpleString.toSimpleString(address), RoutingType.valueOf(routingType.toUpperCase()), new SimpleString(name), filter, durable, false, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, consumersBeforeDispatch, delayBeforeDispatch, autoCreateAddress); return QueueTextFormatter.Long.format(queue, new StringBuilder()).toString(); } catch (ActiveMQException e) { throw new IllegalStateException(e.getMessage()); @@ -851,12 +868,24 @@ public String updateQueue(String name, Boolean purgeOnNoConsumers, Boolean exclusive, String user) throws Exception { + return updateQueue(name, routingType, maxConsumers, purgeOnNoConsumers, exclusive, null, null, user); + } + + @Override + public String updateQueue(String name, + String routingType, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive, + Integer consumersBeforeDispatch, + Long delayBeforeDispatch, + String user) throws Exception { checkStarted(); clearIO(); try { - final Queue queue = server.updateQueue(name, routingType != null ? RoutingType.valueOf(routingType) : null, maxConsumers, purgeOnNoConsumers, exclusive, user); + final Queue queue = server.updateQueue(name, routingType != null ? RoutingType.valueOf(routingType) : null, maxConsumers, purgeOnNoConsumers, exclusive, consumersBeforeDispatch, delayBeforeDispatch, user); if (queue == null) { throw ActiveMQMessageBundle.BUNDLE.noSuchQueue(new SimpleString(name)); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java index 7e18311d9d5..ebc86fc1bb7 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java @@ -62,6 +62,14 @@ public interface QueueBindingInfo { void setLastValue(boolean lastValue); + int getConsumersBeforeDispatch(); + + void setConsumersBeforeDispatch(int consumersBeforeDispatch); + + long getDelayBeforeDispatch(); + + void setDelayBeforeDispatch(long delayBeforeDispatch); + byte getRoutingType(); void setRoutingType(byte routingType); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java index dc2e20bfd9e..7c821a98f69 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java @@ -1275,7 +1275,7 @@ private void internalQueueBinding(boolean update, final long tx, final Binding b SimpleString filterString = filter == null ? null : filter.getFilterString(); - PersistentQueueBindingEncoding bindingEncoding = new PersistentQueueBindingEncoding(queue.getName(), binding.getAddress(), filterString, queue.getUser(), queue.isAutoCreated(), queue.getMaxConsumers(), queue.isPurgeOnNoConsumers(), queue.isExclusive(), queue.isLastValue(), queue.getRoutingType().getType()); + PersistentQueueBindingEncoding bindingEncoding = new PersistentQueueBindingEncoding(queue.getName(), binding.getAddress(), filterString, queue.getUser(), queue.isAutoCreated(), queue.getMaxConsumers(), queue.isPurgeOnNoConsumers(), queue.isExclusive(), queue.isLastValue(), queue.getConsumersBeforeDispatch(), queue.getDelayBeforeDispatch(), queue.getRoutingType().getType()); readLock(); try { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java index 2ab43965a4a..0cfe67c097c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java @@ -50,6 +50,10 @@ public class PersistentQueueBindingEncoding implements EncodingSupport, QueueBin public boolean lastValue; + public int consumersBeforeDispatch; + + public long delayBeforeDispatch; + public byte routingType; public PersistentQueueBindingEncoding() { @@ -76,6 +80,10 @@ public String toString() { exclusive + ", lastValue=" + lastValue + + ", consumersBeforeDispatch=" + + consumersBeforeDispatch + + ", delayBeforeDispatch=" + + delayBeforeDispatch + ", routingType=" + routingType + "]"; @@ -90,6 +98,8 @@ public PersistentQueueBindingEncoding(final SimpleString name, final boolean purgeOnNoConsumers, final boolean exclusive, final boolean lastValue, + final int consumersBeforeDispatch, + final long delayBeforeDispatch, final byte routingType) { this.name = name; this.address = address; @@ -100,6 +110,8 @@ public PersistentQueueBindingEncoding(final SimpleString name, this.purgeOnNoConsumers = purgeOnNoConsumers; this.exclusive = exclusive; this.lastValue = lastValue; + this.consumersBeforeDispatch = consumersBeforeDispatch; + this.delayBeforeDispatch = delayBeforeDispatch; this.routingType = routingType; } @@ -195,6 +207,26 @@ public void setLastValue(boolean lastValue) { this.lastValue = lastValue; } + @Override + public int getConsumersBeforeDispatch() { + return consumersBeforeDispatch; + } + + @Override + public void setConsumersBeforeDispatch(int consumersBeforeDispatch) { + this.consumersBeforeDispatch = consumersBeforeDispatch; + } + + @Override + public long getDelayBeforeDispatch() { + return delayBeforeDispatch; + } + + @Override + public void setDelayBeforeDispatch(long delayBeforeDispatch) { + this.delayBeforeDispatch = delayBeforeDispatch; + } + @Override public byte getRoutingType() { return routingType; @@ -246,6 +278,16 @@ public void decode(final ActiveMQBuffer buffer) { } else { lastValue = ActiveMQDefaultConfiguration.getDefaultLastValue(); } + if (buffer.readableBytes() > 0) { + consumersBeforeDispatch = buffer.readInt(); + } else { + consumersBeforeDispatch = ActiveMQDefaultConfiguration.getDefaultConsumersBeforeDispatch(); + } + if (buffer.readableBytes() > 0) { + delayBeforeDispatch = buffer.readLong(); + } else { + delayBeforeDispatch = ActiveMQDefaultConfiguration.getDefaultDelayBeforeDispatch(); + } } @Override @@ -260,6 +302,8 @@ public void encode(final ActiveMQBuffer buffer) { buffer.writeByte(routingType); buffer.writeBoolean(exclusive); buffer.writeBoolean(lastValue); + buffer.writeInt(consumersBeforeDispatch); + buffer.writeLong(delayBeforeDispatch); } @Override @@ -271,7 +315,9 @@ public int getEncodeSize() { DataConstants.SIZE_BOOLEAN + DataConstants.SIZE_BYTE + DataConstants.SIZE_BOOLEAN + - DataConstants.SIZE_BOOLEAN; + DataConstants.SIZE_BOOLEAN + + DataConstants.SIZE_INT + + DataConstants.SIZE_LONG; } private SimpleString createMetadata() { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java index d95526dcb2a..ce1fcfd172d 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java @@ -69,6 +69,8 @@ QueueBinding updateQueue(SimpleString name, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, + Integer consumersBeforeDispatch, + Long delayBeforeDispatch, SimpleString user) throws Exception; List listQueuesForAddress(SimpleString address) throws Exception; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index 21a75048c56..247b9ed9bfb 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -468,6 +468,8 @@ public QueueBinding updateQueue(SimpleString name, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, + Integer consumersBeforeDispatch, + Long delayBeforeDispatch, SimpleString user) throws Exception { synchronized (addressLock) { final QueueBinding queueBinding = (QueueBinding) addressManager.getBinding(name); @@ -512,6 +514,14 @@ public QueueBinding updateQueue(SimpleString name, changed = true; queue.setExclusive(exclusive); } + if (consumersBeforeDispatch != null && !consumersBeforeDispatch.equals(queue.getConsumersBeforeDispatch())) { + changed = true; + queue.setConsumersBeforeDispatch(consumersBeforeDispatch.intValue()); + } + if (delayBeforeDispatch != null && !delayBeforeDispatch.equals(queue.getDelayBeforeDispatch())) { + changed = true; + queue.setDelayBeforeDispatch(delayBeforeDispatch.longValue()); + } if (logger.isDebugEnabled()) { if (user == null && queue.getUser() != null) { logger.debug("Ignoring updating Queue to a NULL user"); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java index cfc5bb96e95..e15feb4a56f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java @@ -338,6 +338,10 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri void createSharedQueue(SimpleString address, RoutingType routingType, SimpleString name, SimpleString filterString, SimpleString user, boolean durable, int maxConsumers, boolean purgeOnNoConsumers, boolean exclusive, boolean lastValue) throws Exception; + void createSharedQueue(SimpleString address, RoutingType routingType, SimpleString name, SimpleString filterString, + SimpleString user, boolean durable, int maxConsumers, boolean purgeOnNoConsumers, boolean exclusive, boolean lastValue, + int consumersBeforeDispatch, long delayBeforeDispatch) throws Exception; + Queue createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, boolean durable, boolean temporary) throws Exception; @@ -348,6 +352,10 @@ Queue createQueue(SimpleString address, RoutingType routingType, SimpleString qu boolean durable, boolean temporary, int maxConsumers, boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception; + Queue createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, + boolean durable, boolean temporary, int maxConsumers, boolean purgeOnNoConsumers, boolean exclusive, + boolean lastValue, int consumersBeforeDispatch, long delayBeforeDispatch, boolean autoCreateAddress) throws Exception; + Queue createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, SimpleString user, boolean durable, boolean temporary, boolean autoCreated, Integer maxConsumers, Boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception; @@ -360,6 +368,11 @@ Queue createQueue(AddressInfo addressInfo, SimpleString queueName, SimpleString SimpleString user, boolean durable, boolean temporary, boolean autoCreated, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue, boolean autoCreateAddress) throws Exception; + Queue createQueue(AddressInfo addressInfo, SimpleString queueName, SimpleString filter, + SimpleString user, boolean durable, boolean temporary, boolean autoCreated, Integer maxConsumers, + Boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue, Integer consumersBeforeDispatch, + Long delayBeforeDispatch, boolean autoCreateAddress) throws Exception; + Queue createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, SimpleString user, boolean durable, boolean temporary, boolean ignoreIfExists, boolean transientQueue, boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception; @@ -368,6 +381,11 @@ Queue createQueue(SimpleString address, RoutingType routingType, SimpleString qu SimpleString user, boolean durable, boolean temporary, boolean ignoreIfExists, boolean transientQueue, boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, boolean exclusive, boolean lastValue, boolean autoCreateAddress) throws Exception; + Queue createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, + SimpleString user, boolean durable, boolean temporary, boolean ignoreIfExists, boolean transientQueue, + boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, boolean exclusive, boolean lastValue, int consumersBeforeDispatch, + long delayBeforeDispatch, boolean autoCreateAddress) throws Exception; + @Deprecated Queue createQueue(SimpleString address, SimpleString queueName, SimpleString filter, boolean durable, boolean temporary) throws Exception; @@ -454,6 +472,15 @@ Queue updateQueue(String name, Boolean exclusive, String user) throws Exception; + Queue updateQueue(String name, + RoutingType routingType, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive, + Integer consumersBeforeDispatch, + Long delayBeforeDispatch, + String user) throws Exception; + /* * add a ProtocolManagerFactory to be used. Note if @see Configuration#isResolveProtocols is tur then this factory will * replace any factories with the same protocol diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java index 0e16718af64..70051c0cb99 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java @@ -68,6 +68,20 @@ public interface Queue extends Bindable,CriticalComponent { void setPurgeOnNoConsumers(boolean value); + int getConsumersBeforeDispatch(); + + void setConsumersBeforeDispatch(int consumersBeforeDispatch); + + long getDelayBeforeDispatch(); + + void setDelayBeforeDispatch(long delayBeforeDispatch); + + long getDispatchStartTime(); + + boolean isDispatching(); + + void setDispatching(boolean dispatching); + boolean isExclusive(); void setExclusive(boolean value); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java index 75f859d409b..c83e08a7a9c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java @@ -40,6 +40,8 @@ public final class QueueConfig { private final boolean exclusive; private final boolean lastValue; private final boolean purgeOnNoConsumers; + private final int consumersBeforeDispatch; + private final long delayBeforeDispatch; public static final class Builder { @@ -57,6 +59,8 @@ public static final class Builder { private boolean exclusive; private boolean lastValue; private boolean purgeOnNoConsumers; + private int consumersBeforeDispatch; + private long delayBeforeDispatch; private Builder(final long id, final SimpleString name) { this(id, name, name); @@ -77,6 +81,8 @@ private Builder(final long id, final SimpleString name, final SimpleString addre this.exclusive = ActiveMQDefaultConfiguration.getDefaultExclusive(); this.lastValue = ActiveMQDefaultConfiguration.getDefaultLastValue(); this.purgeOnNoConsumers = ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(); + this.consumersBeforeDispatch = ActiveMQDefaultConfiguration.getDefaultConsumersBeforeDispatch(); + this.delayBeforeDispatch = ActiveMQDefaultConfiguration.getDefaultDelayBeforeDispatch(); validateState(); } @@ -138,6 +144,15 @@ public Builder lastValue(final boolean lastValue) { return this; } + public Builder consumersBeforeDispatch(final int consumersBeforeDispatch) { + this.consumersBeforeDispatch = consumersBeforeDispatch; + return this; + } + + public Builder delayBeforeDispatch(final long delayBeforeDispatch) { + this.delayBeforeDispatch = delayBeforeDispatch; + return this; + } public Builder purgeOnNoConsumers(final boolean purgeOnNoConsumers) { this.purgeOnNoConsumers = purgeOnNoConsumers; @@ -170,7 +185,7 @@ public QueueConfig build() { } else { pageSubscription = null; } - return new QueueConfig(id, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, lastValue, purgeOnNoConsumers); + return new QueueConfig(id, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, lastValue, consumersBeforeDispatch, delayBeforeDispatch, purgeOnNoConsumers); } } @@ -216,6 +231,8 @@ private QueueConfig(final long id, final int maxConsumers, final boolean exclusive, final boolean lastValue, + final int consumersBeforeDispatch, + final long delayBeforeDispatch, final boolean purgeOnNoConsumers) { this.id = id; this.address = address; @@ -231,6 +248,8 @@ private QueueConfig(final long id, this.exclusive = exclusive; this.lastValue = lastValue; this.maxConsumers = maxConsumers; + this.consumersBeforeDispatch = consumersBeforeDispatch; + this.delayBeforeDispatch = delayBeforeDispatch; } public long id() { @@ -289,6 +308,14 @@ public RoutingType deliveryMode() { return routingType; } + public int consumersBeforeDispatch() { + return consumersBeforeDispatch; + } + + public long delayBeforeDispatch() { + return delayBeforeDispatch; + } + @Override public boolean equals(Object o) { if (this == o) @@ -324,6 +351,12 @@ public boolean equals(Object o) { return false; if (purgeOnNoConsumers != that.purgeOnNoConsumers) return false; + if (consumersBeforeDispatch != that.consumersBeforeDispatch) + return false; + if (delayBeforeDispatch != that.delayBeforeDispatch) + return false; + if (purgeOnNoConsumers != that.purgeOnNoConsumers) + return false; return user != null ? user.equals(that.user) : that.user == null; } @@ -343,6 +376,8 @@ public int hashCode() { result = 31 * result + maxConsumers; result = 31 * result + (exclusive ? 1 : 0); result = 31 * result + (lastValue ? 1 : 0); + result = 31 * result + consumersBeforeDispatch; + result = 31 * result + Long.hashCode(delayBeforeDispatch); result = 31 * result + (purgeOnNoConsumers ? 1 : 0); return result; } @@ -363,6 +398,8 @@ public String toString() { + ", maxConsumers=" + maxConsumers + ", exclusive=" + exclusive + ", lastValue=" + lastValue + + ", consumersBeforeDispatch=" + consumersBeforeDispatch + + ", delayBeforeDispatch=" + delayBeforeDispatch + ", purgeOnNoConsumers=" + purgeOnNoConsumers + '}'; } } 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 4abda08fcfa..a2cdf55b999 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 @@ -1662,6 +1662,23 @@ public Queue createQueue(final SimpleString address, return createQueue(address, routingType, queueName, filter, null, durable, temporary, false, maxConsumers, purgeOnNoConsumers, autoCreateAddress); } + @Override + public Queue createQueue(final SimpleString address, + final RoutingType routingType, + final SimpleString queueName, + final SimpleString filter, + final boolean durable, + final boolean temporary, + final int maxConsumers, + final boolean purgeOnNoConsumers, + final boolean exclusive, + final boolean lastValue, + final int consumersBeforeDispatch, + final long delayBeforeDispatch, + final boolean autoCreateAddress) throws Exception { + return createQueue(address, routingType, queueName, filter, null, durable, temporary, false, false, false, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, consumersBeforeDispatch, delayBeforeDispatch, autoCreateAddress); + } + @Override @Deprecated public Queue createQueue(SimpleString address, @@ -1681,12 +1698,18 @@ public Queue createQueue(SimpleString address, @Override public Queue createQueue(AddressInfo addressInfo, SimpleString queueName, SimpleString filter, SimpleString user, boolean durable, boolean temporary, boolean autoCreated, Integer maxConsumers, Boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception { AddressSettings as = getAddressSettingsRepository().getMatch(addressInfo == null ? queueName.toString() : addressInfo.getName().toString()); - return createQueue(addressInfo, queueName, filter, user, durable, temporary, false, false, autoCreated, maxConsumers, purgeOnNoConsumers, as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), autoCreateAddress); + return createQueue(addressInfo, queueName, filter, user, durable, temporary, false, false, autoCreated, maxConsumers, purgeOnNoConsumers, as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), as.getDefaultConsumersBeforeDispatch(), as.getDefaultDelayBeforeDispatch(), autoCreateAddress); } @Override public Queue createQueue(AddressInfo addressInfo, SimpleString queueName, SimpleString filter, SimpleString user, boolean durable, boolean temporary, boolean autoCreated, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue, boolean autoCreateAddress) throws Exception { - return createQueue(addressInfo, queueName, filter, user, durable, temporary, false, false, autoCreated, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, autoCreateAddress); + AddressSettings as = getAddressSettingsRepository().getMatch(addressInfo == null ? queueName.toString() : addressInfo.getName().toString()); + return createQueue(addressInfo, queueName, filter, user, durable, temporary, false, false, autoCreated, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, as.getDefaultConsumersBeforeDispatch(), as.getDefaultDelayBeforeDispatch(), autoCreateAddress); + } + + @Override + public Queue createQueue(AddressInfo addressInfo, SimpleString queueName, SimpleString filter, SimpleString user, boolean durable, boolean temporary, boolean autoCreated, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue, Integer consumersBeforeDispatch, Long delayBeforeDispatch, boolean autoCreateAddress) throws Exception { + return createQueue(addressInfo, queueName, filter, user, durable, temporary, false, false, autoCreated, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, consumersBeforeDispatch, delayBeforeDispatch, autoCreateAddress); } @@ -1695,7 +1718,15 @@ public Queue createQueue(SimpleString address, RoutingType routingType, SimpleSt SimpleString user, boolean durable, boolean temporary, boolean ignoreIfExists, boolean transientQueue, boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception { AddressSettings as = getAddressSettingsRepository().getMatch(address == null ? queueName.toString() : address.toString()); - return createQueue(address, routingType, queueName, filter, user, durable, temporary, ignoreIfExists, transientQueue, autoCreated, maxConsumers, purgeOnNoConsumers, as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), autoCreateAddress); + return createQueue(address, routingType, queueName, filter, user, durable, temporary, ignoreIfExists, transientQueue, autoCreated, maxConsumers, purgeOnNoConsumers, as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), as.getDefaultConsumersBeforeDispatch(), as.getDefaultDelayBeforeDispatch(), autoCreateAddress); + } + + @Override + public Queue createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, + SimpleString user, boolean durable, boolean temporary, boolean ignoreIfExists, boolean transientQueue, + boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, boolean exclusive, boolean lastValue, boolean autoCreateAddress) throws Exception { + AddressSettings as = getAddressSettingsRepository().getMatch(address == null ? queueName.toString() : address.toString()); + return createQueue(address, routingType, queueName, filter, user, durable, temporary, ignoreIfExists, transientQueue, autoCreated, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, as.getDefaultConsumersBeforeDispatch(), as.getDefaultDelayBeforeDispatch(), autoCreateAddress); } @@ -1731,6 +1762,23 @@ public void createSharedQueue(final SimpleString address, boolean purgeOnNoConsumers, boolean exclusive, boolean lastValue) throws Exception { + AddressSettings as = getAddressSettingsRepository().getMatch(address == null ? name.toString() : address.toString()); + createSharedQueue(address, routingType, name, filterString, user, durable, as.getDefaultMaxConsumers(), as.isDefaultPurgeOnNoConsumers(), as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), as.getDefaultConsumersBeforeDispatch(), as.getDefaultDelayBeforeDispatch()); + } + + @Override + public void createSharedQueue(final SimpleString address, + RoutingType routingType, + final SimpleString name, + final SimpleString filterString, + final SimpleString user, + boolean durable, + int maxConsumers, + boolean purgeOnNoConsumers, + boolean exclusive, + boolean lastValue, + int consumersBeforeDispatch, + long delayBeforeDispatch) throws Exception { //force the old contract about address if (address == null) { throw new NullPointerException("address can't be null!"); @@ -1744,7 +1792,7 @@ public void createSharedQueue(final SimpleString address, } } - final Queue queue = createQueue(address, routingType, name, filterString, user, durable, !durable, true, !durable, false, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, true); + final Queue queue = createQueue(address, routingType, name, filterString, user, durable, !durable, true, !durable, false, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, consumersBeforeDispatch, delayBeforeDispatch, true); if (!queue.getAddress().equals(address)) { throw ActiveMQMessageBundle.BUNDLE.queueSubscriptionBelongsToDifferentAddress(name); @@ -2541,21 +2589,22 @@ private void deployQueuesFromListCoreQueueConfiguration(List plugin.beforeCreateQueue(queueConfig) : null); @@ -2852,6 +2917,8 @@ public Queue createQueue(final SimpleString address, final boolean purgeOnNoConsumers, final boolean exclusive, final boolean lastValue, + final int consumersBeforeDispatch, + final long delayBeforeDispatch, final boolean autoCreateAddress) throws Exception { final QueueBinding binding = (QueueBinding) postOffice.getBinding(queueName); @@ -2893,7 +2960,20 @@ public Queue createQueue(final SimpleString address, throw ActiveMQMessageBundle.BUNDLE.invalidRoutingTypeForAddress(routingType, info.getName().toString(), info.getRoutingTypes()); } - final QueueConfig queueConfig = queueConfigBuilder.filter(filter).pagingManager(pagingManager).user(user).durable(durable).temporary(temporary).autoCreated(autoCreated).routingType(routingType).maxConsumers(maxConsumers).purgeOnNoConsumers(purgeOnNoConsumers).exclusive(exclusive).lastValue(lastValue).build(); + final QueueConfig queueConfig = queueConfigBuilder + .filter(filter) + .pagingManager(pagingManager) + .user(user) + .durable(durable) + .temporary(temporary) + .autoCreated(autoCreated).routingType(routingType) + .maxConsumers(maxConsumers) + .purgeOnNoConsumers(purgeOnNoConsumers) + .exclusive(exclusive) + .lastValue(lastValue) + .consumersBeforeDispatch(consumersBeforeDispatch) + .delayBeforeDispatch(delayBeforeDispatch) + .build(); callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.beforeCreateQueue(queueConfig) : null); @@ -2963,14 +3043,27 @@ public Queue updateQueue(String name, return updateQueue(name, routingType, maxConsumers, purgeOnNoConsumers, null, null); } + @Deprecated + @Override + public Queue updateQueue(String name, + RoutingType routingType, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive, + String user) throws Exception { + return updateQueue(name, routingType, maxConsumers, purgeOnNoConsumers, exclusive, null, null, user); + } + @Override public Queue updateQueue(String name, RoutingType routingType, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, + Integer consumersBeforeDispatch, + Long delayBeforeDispatch, String user) throws Exception { - final QueueBinding queueBinding = this.postOffice.updateQueue(new SimpleString(name), routingType, maxConsumers, purgeOnNoConsumers, exclusive, SimpleString.toSimpleString(user)); + final QueueBinding queueBinding = this.postOffice.updateQueue(new SimpleString(name), routingType, maxConsumers, purgeOnNoConsumers, exclusive, consumersBeforeDispatch, delayBeforeDispatch, SimpleString.toSimpleString(user)); if (queueBinding != null) { final Queue queue = queueBinding.getQueue(); return queue; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LastValueQueue.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LastValueQueue.java index fc965912f0c..7c2ffeedd99 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LastValueQueue.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LastValueQueue.java @@ -63,6 +63,8 @@ public LastValueQueue(final long persistenceID, final RoutingType routingType, final Integer maxConsumers, final Boolean exclusive, + final Integer consumersBeforeDispatch, + final Long delayBeforeDispatch, final Boolean purgeOnNoConsumers, final ScheduledExecutorService scheduledExecutor, final PostOffice postOffice, @@ -71,7 +73,7 @@ public LastValueQueue(final long persistenceID, final ArtemisExecutor executor, final ActiveMQServer server, final QueueFactory factory) { - super(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, purgeOnNoConsumers, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executor, server, factory); + super(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, consumersBeforeDispatch, delayBeforeDispatch, purgeOnNoConsumers, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executor, server, factory); } @Override diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PostOfficeJournalLoader.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PostOfficeJournalLoader.java index f9ec9647aa9..59a318c0cfb 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PostOfficeJournalLoader.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PostOfficeJournalLoader.java @@ -153,6 +153,8 @@ public void initQueues(Map queueBindingInfosMap, .maxConsumers(queueBindingInfo.getMaxConsumers()) .exclusive(queueBindingInfo.isExclusive()) .lastValue(queueBindingInfo.isLastValue()) + .consumersBeforeDispatch(queueBindingInfo.getConsumersBeforeDispatch()) + .delayBeforeDispatch(queueBindingInfo.getDelayBeforeDispatch()) .routingType(RoutingType.getType(queueBindingInfo.getRoutingType())); final Queue queue = queueFactory.createQueueWith(queueConfigBuilder.build()); queue.setConsumersRefCount(new QueueManagerImpl(((PostOfficeImpl)postOffice).getServer(), queueBindingInfo.getQueueName())); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java index 7f23d099dfe..24b36e6111b 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java @@ -74,9 +74,9 @@ public void setPostOffice(final PostOffice postOffice) { public Queue createQueueWith(final QueueConfig config) { final Queue queue; if (config.isLastValue()) { - queue = new LastValueQueue(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isExclusive(), config.isPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); + queue = new LastValueQueue(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isExclusive(), config.consumersBeforeDispatch(), config.delayBeforeDispatch(), config.isPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } else { - queue = new QueueImpl(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isExclusive(), config.isPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); + queue = new QueueImpl(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isExclusive(), config.consumersBeforeDispatch(), config.delayBeforeDispatch(), config.isPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } server.getCriticalAnalyzer().add(queue); @@ -102,7 +102,7 @@ public Queue createQueue(final long persistenceID, Queue queue; if (addressSettings.isDefaultLastValueQueue()) { - queue = new LastValueQueue(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, ActiveMQDefaultConfiguration.getDefaultRoutingType(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultExclusive(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); + queue = new LastValueQueue(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, ActiveMQDefaultConfiguration.getDefaultRoutingType(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultExclusive(), ActiveMQDefaultConfiguration.getDefaultConsumersBeforeDispatch(), ActiveMQDefaultConfiguration.getDefaultDelayBeforeDispatch(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } else { queue = new QueueImpl(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java index bc5c0c98fe9..265621741da 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java @@ -37,8 +37,10 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQNullRefException; @@ -87,6 +89,7 @@ import org.apache.activemq.artemis.core.transaction.impl.BindingsTransactionImpl; import org.apache.activemq.artemis.core.transaction.impl.TransactionImpl; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; +import org.apache.activemq.artemis.utils.BooleanUtil; import org.apache.activemq.artemis.utils.Env; import org.apache.activemq.artemis.utils.ReferenceCounter; import org.apache.activemq.artemis.utils.ReusableLatch; @@ -113,6 +116,8 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { protected static final int CRITICAL_CONSUMER = 3; private static final Logger logger = Logger.getLogger(QueueImpl.class); + private static final AtomicIntegerFieldUpdater dispatchingUpdater = AtomicIntegerFieldUpdater.newUpdater(QueueImpl.class, "dispatching"); + private static final AtomicLongFieldUpdater dispatchStartTimeUpdater = AtomicLongFieldUpdater.newUpdater(QueueImpl.class, "dispatchStartTime"); public static final int REDISTRIBUTOR_BATCH_SIZE = 100; @@ -216,7 +221,7 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { private final SimpleString address; - private Redistributor redistributor; + private ConsumerHolder redistributor; private ScheduledFuture redistributorFuture; @@ -268,6 +273,15 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { private final QueueFactory factory; + public volatile int dispatching = 0; + + public volatile long dispatchStartTime = -1; + + private int consumersBeforeDispatch = 0; + + private long delayBeforeDispatch = 0; + + /** * This is to avoid multi-thread races on calculating direct delivery, * to guarantee ordering will be always be correct @@ -413,6 +427,31 @@ public QueueImpl(final long id, final ArtemisExecutor executor, final ActiveMQServer server, final QueueFactory factory) { + this(id, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, null, null, purgeOnNoConsumers, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executor, server, factory); + } + + public QueueImpl(final long id, + final SimpleString address, + final SimpleString name, + final Filter filter, + final PageSubscription pageSubscription, + final SimpleString user, + final boolean durable, + final boolean temporary, + final boolean autoCreated, + final RoutingType routingType, + final Integer maxConsumers, + final Boolean exclusive, + final Integer consumersBeforeDispatch, + final Long delayBeforeDispatch, + final Boolean purgeOnNoConsumers, + final ScheduledExecutorService scheduledExecutor, + final PostOffice postOffice, + final StorageManager storageManager, + final HierarchicalRepository addressSettingsRepository, + final ArtemisExecutor executor, + final ActiveMQServer server, + final QueueFactory factory) { super(server == null ? EmptyCriticalAnalyzer.getInstance() : server.getCriticalAnalyzer(), CRITICAL_PATHS); this.id = id; @@ -441,6 +480,10 @@ public QueueImpl(final long id, this.purgeOnNoConsumers = purgeOnNoConsumers == null ? ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers() : purgeOnNoConsumers; + this.consumersBeforeDispatch = consumersBeforeDispatch == null ? ActiveMQDefaultConfiguration.getDefaultConsumersBeforeDispatch() : consumersBeforeDispatch; + + this.delayBeforeDispatch = delayBeforeDispatch == null ? ActiveMQDefaultConfiguration.getDefaultDelayBeforeDispatch() : delayBeforeDispatch; + this.postOffice = postOffice; this.storageManager = storageManager; @@ -504,6 +547,48 @@ public synchronized void setExclusive(boolean exclusive) { this.exclusive = exclusive; } + @Override + public int getConsumersBeforeDispatch() { + return consumersBeforeDispatch; + } + + @Override + public synchronized void setConsumersBeforeDispatch(int consumersBeforeDispatch) { + this.consumersBeforeDispatch = consumersBeforeDispatch; + } + + @Override + public long getDelayBeforeDispatch() { + return delayBeforeDispatch; + } + + @Override + public synchronized void setDelayBeforeDispatch(long delayBeforeDispatch) { + this.delayBeforeDispatch = delayBeforeDispatch; + } + + @Override + public long getDispatchStartTime() { + return dispatchStartTimeUpdater.get(this); + } + + @Override + public boolean isDispatching() { + return BooleanUtil.toBoolean(dispatchingUpdater.get(this)); + } + + @Override + public synchronized void setDispatching(boolean dispatching) { + if (dispatchingUpdater.compareAndSet(this, BooleanUtil.toInt(!dispatching), BooleanUtil.toInt(dispatching))) { + if (dispatching) { + dispatchStartTimeUpdater.set(this, System.currentTimeMillis()); + } else { + dispatchStartTimeUpdater.set(this, -1); + } + } + } + + @Override public boolean isLastValue() { return false; @@ -867,6 +952,21 @@ private boolean internalFlushExecutor(long timeout, boolean log) { } } + private boolean canDispatch() { + boolean canDispatch = BooleanUtil.toBoolean(dispatchingUpdater.get(this)); + if (canDispatch) { + return true; + } else { + long currentDispatchStartTime = dispatchStartTimeUpdater.get(this); + if (currentDispatchStartTime != -1 && currentDispatchStartTime < System.currentTimeMillis()) { + dispatchingUpdater.set(this, BooleanUtil.toInt(true)); + return true; + } else { + return false; + } + } + } + @Override public void addConsumer(final Consumer consumer) throws Exception { if (logger.isDebugEnabled()) { @@ -876,7 +976,6 @@ public void addConsumer(final Consumer consumer) throws Exception { enterCritical(CRITICAL_CONSUMER); try { synchronized (this) { - if (maxConsumers != MAX_CONSUMERS_UNLIMITED && consumersCount.get() >= maxConsumers) { throw ActiveMQMessageBundle.BUNDLE.maxConsumerLimitReachedForQueue(address, name); } @@ -892,7 +991,15 @@ public void addConsumer(final Consumer consumer) throws Exception { consumerList.add(new ConsumerHolder(consumer)); if (consumerSet.add(consumer)) { - consumersCount.incrementAndGet(); + int currentConsumerCount = consumersCount.incrementAndGet(); + if (delayBeforeDispatch >= 0) { + dispatchStartTimeUpdater.compareAndSet(this,-1, delayBeforeDispatch + System.currentTimeMillis()); + } + if (currentConsumerCount >= consumersBeforeDispatch) { + if (dispatchingUpdater.compareAndSet(this, BooleanUtil.toInt(false), BooleanUtil.toInt(true))) { + dispatchStartTimeUpdater.set(this, System.currentTimeMillis()); + } + } } if (refCountForConsumers != null) { @@ -931,7 +1038,11 @@ public void removeConsumer(final Consumer consumer) { } if (consumerSet.remove(consumer)) { - consumersCount.decrementAndGet(); + int currentConsumerCount = consumersCount.decrementAndGet(); + boolean stopped = dispatchingUpdater.compareAndSet(this, BooleanUtil.toInt(true), BooleanUtil.toInt(currentConsumerCount != 0)); + if (stopped) { + dispatchStartTimeUpdater.set(this, -1); + } } LinkedList groupsToRemove = null; @@ -1005,11 +1116,8 @@ private void clearRedistributorFuture() { @Override public synchronized void cancelRedistributor() throws Exception { if (redistributor != null) { - redistributor.stop(); - Redistributor redistributorToRemove = redistributor; + redistributor.consumer.stop(); redistributor = null; - - removeConsumer(redistributorToRemove); } clearRedistributorFuture(); @@ -2265,7 +2373,7 @@ private void deliver() { synchronized (this) { // Need to do these checks inside the synchronized - if (paused || consumerList.isEmpty()) { + if (paused || !canDispatch() && redistributor == null) { return; } @@ -2273,20 +2381,26 @@ private void deliver() { break; } - if (endPos < 0 || consumersChanged) { - consumersChanged = false; + ConsumerHolder holder; + if (redistributor == null) { + + if (endPos < 0 || consumersChanged) { + consumersChanged = false; - size = consumerList.size(); + size = consumerList.size(); - endPos = pos - 1; + endPos = pos - 1; - if (endPos < 0) { - endPos = size - 1; - noDelivery = 0; + if (endPos < 0) { + endPos = size - 1; + noDelivery = 0; + } } - } - ConsumerHolder holder = consumerList.get(pos); + holder = consumerList.get(pos); + } else { + holder = redistributor; + } Consumer consumer = holder.consumer; Consumer groupConsumer = null; @@ -2332,7 +2446,7 @@ private void deliver() { } } - if (exclusive) { + if (exclusive && redistributor == null) { consumer = consumerList.get(0).consumer; } @@ -2522,13 +2636,12 @@ private void internalAddRedistributor(final ArtemisExecutor executor) { if (logger.isTraceEnabled()) { logger.trace("QueueImpl::Adding redistributor on queue " + this.toString()); } - redistributor = new Redistributor(this, storageManager, postOffice, executor, QueueImpl.REDISTRIBUTOR_BATCH_SIZE); - consumerList.add(new ConsumerHolder(redistributor)); + redistributor = (new ConsumerHolder(new Redistributor(this, storageManager, postOffice, executor, QueueImpl.REDISTRIBUTOR_BATCH_SIZE))); consumersChanged = true; - redistributor.start(); + redistributor.consumer.start(); deliverAsync(); } @@ -2864,7 +2977,7 @@ private boolean deliverDirect(final MessageReference ref) { // this would protect any eventual bug return false; } - if (paused || consumerList.isEmpty()) { + if (paused || !canDispatch() && redistributor == null) { return false; } @@ -2877,7 +2990,12 @@ private boolean deliverDirect(final MessageReference ref) { int size = consumerList.size(); while (true) { - ConsumerHolder holder = consumerList.get(pos); + ConsumerHolder holder; + if (redistributor == null) { + holder = consumerList.get(pos); + } else { + holder = redistributor; + } Consumer consumer = holder.consumer; @@ -2895,7 +3013,7 @@ private boolean deliverDirect(final MessageReference ref) { } } - if (exclusive) { + if (exclusive && redistributor == null) { consumer = consumerList.get(0).consumer; } @@ -3158,13 +3276,13 @@ public void onError(int errorCode, String errorMessage) { // Inner classes // -------------------------------------------------------------------------- - private static class ConsumerHolder { + private static class ConsumerHolder { - ConsumerHolder(final Consumer consumer) { + ConsumerHolder(final T consumer) { this.consumer = consumer; } - final Consumer consumer; + final T consumer; LinkedListIterator iter; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java index c7021fdc4fb..6fc5019c17d 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java @@ -170,6 +170,10 @@ public class AddressSettings implements Mergeable, Serializable private Boolean defaultPurgeOnNoConsumers = null; + private Integer defaultConsumersBeforeDispatch = null; + + private Long defaultDelayBeforeDispatch = null; + private RoutingType defaultQueueRoutingType = null; private RoutingType defaultAddressRoutingType = null; @@ -214,6 +218,8 @@ public AddressSettings(AddressSettings other) { this.maxSizeBytesRejectThreshold = other.maxSizeBytesRejectThreshold; this.defaultMaxConsumers = other.defaultMaxConsumers; this.defaultPurgeOnNoConsumers = other.defaultPurgeOnNoConsumers; + this.defaultConsumersBeforeDispatch = other.defaultConsumersBeforeDispatch; + this.defaultDelayBeforeDispatch = other.defaultDelayBeforeDispatch; this.defaultQueueRoutingType = other.defaultQueueRoutingType; this.defaultAddressRoutingType = other.defaultAddressRoutingType; } @@ -328,6 +334,24 @@ public AddressSettings setDefaultMaxConsumers(Integer defaultMaxConsumers) { return this; } + public int getDefaultConsumersBeforeDispatch() { + return defaultConsumersBeforeDispatch != null ? defaultConsumersBeforeDispatch : ActiveMQDefaultConfiguration.getDefaultConsumersBeforeDispatch(); + } + + public AddressSettings setDefaultConsumersBeforeDispatch(Integer defaultConsumersBeforeDispatch) { + this.defaultConsumersBeforeDispatch = defaultConsumersBeforeDispatch; + return this; + } + + public long getDefaultDelayBeforeDispatch() { + return defaultDelayBeforeDispatch != null ? defaultDelayBeforeDispatch : ActiveMQDefaultConfiguration.getDefaultDelayBeforeDispatch(); + } + + public AddressSettings setDefaultDelayBeforeDispatch(Long defaultDelayBeforeDispatch) { + this.defaultDelayBeforeDispatch = defaultDelayBeforeDispatch; + return this; + } + public boolean isDefaultPurgeOnNoConsumers() { return defaultPurgeOnNoConsumers != null ? defaultPurgeOnNoConsumers : ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(); } @@ -667,6 +691,18 @@ public void merge(final AddressSettings merged) { if (defaultAddressRoutingType == null) { defaultAddressRoutingType = merged.defaultAddressRoutingType; } + if (defaultExclusiveQueue == null) { + defaultExclusiveQueue = merged.defaultExclusiveQueue; + } + if (defaultLastValueQueue == null) { + defaultLastValueQueue = merged.defaultLastValueQueue; + } + if (defaultConsumersBeforeDispatch == null) { + defaultConsumersBeforeDispatch = merged.defaultConsumersBeforeDispatch; + } + if (defaultDelayBeforeDispatch == null) { + defaultDelayBeforeDispatch = merged.defaultDelayBeforeDispatch; + } } @Override @@ -767,6 +803,14 @@ public void decode(ActiveMQBuffer buffer) { if (buffer.readableBytes() > 0) { defaultExclusiveQueue = BufferHelper.readNullableBoolean(buffer); } + + if (buffer.readableBytes() > 0) { + defaultConsumersBeforeDispatch = BufferHelper.readNullableInteger(buffer); + } + + if (buffer.readableBytes() > 0) { + defaultDelayBeforeDispatch = BufferHelper.readNullableLong(buffer); + } } @Override @@ -805,7 +849,9 @@ public int getEncodeSize() { BufferHelper.sizeOfNullableBoolean(defaultPurgeOnNoConsumers) + DataConstants.SIZE_BYTE + DataConstants.SIZE_BYTE + - BufferHelper.sizeOfNullableBoolean(defaultExclusiveQueue); + BufferHelper.sizeOfNullableBoolean(defaultExclusiveQueue) + + BufferHelper.sizeOfNullableInteger(defaultConsumersBeforeDispatch) + + BufferHelper.sizeOfNullableLong(defaultDelayBeforeDispatch); } @Override @@ -882,6 +928,10 @@ public void encode(ActiveMQBuffer buffer) { BufferHelper.writeNullableBoolean(buffer, defaultExclusiveQueue); + BufferHelper.writeNullableInteger(buffer, defaultConsumersBeforeDispatch); + + BufferHelper.writeNullableLong(buffer, defaultDelayBeforeDispatch); + } /* (non-Javadoc) @@ -928,6 +978,8 @@ public int hashCode() { result = prime * result + ((defaultPurgeOnNoConsumers == null) ? 0 : defaultPurgeOnNoConsumers.hashCode()); result = prime * result + ((defaultQueueRoutingType == null) ? 0 : defaultQueueRoutingType.hashCode()); result = prime * result + ((defaultAddressRoutingType == null) ? 0 : defaultAddressRoutingType.hashCode()); + result = prime * result + ((defaultConsumersBeforeDispatch == null) ? 0 : defaultConsumersBeforeDispatch.hashCode()); + result = prime * result + ((defaultDelayBeforeDispatch == null) ? 0 : defaultDelayBeforeDispatch.hashCode()); return result; } @@ -1133,6 +1185,18 @@ public boolean equals(Object obj) { return false; } else if (!defaultAddressRoutingType.equals(other.defaultAddressRoutingType)) return false; + + if (defaultConsumersBeforeDispatch == null) { + if (other.defaultConsumersBeforeDispatch != null) + return false; + } else if (!defaultConsumersBeforeDispatch.equals(other.defaultConsumersBeforeDispatch)) + return false; + + if (defaultDelayBeforeDispatch == null) { + if (other.defaultDelayBeforeDispatch != null) + return false; + } else if (!defaultDelayBeforeDispatch.equals(other.defaultDelayBeforeDispatch)) + return false; return true; } @@ -1212,6 +1276,10 @@ public String toString() { defaultQueueRoutingType + ", defaultAddressRoutingType=" + defaultAddressRoutingType + + ", defaultConsumersBeforeDispatch=" + + defaultConsumersBeforeDispatch + + ", defaultDelayBeforeDispatch=" + + defaultDelayBeforeDispatch + "]"; } } diff --git a/artemis-server/src/main/resources/schema/artemis-configuration.xsd b/artemis-server/src/main/resources/schema/artemis-configuration.xsd index 831b4cb8ddd..e96923d0f43 100644 --- a/artemis-server/src/main/resources/schema/artemis-configuration.xsd +++ b/artemis-server/src/main/resources/schema/artemis-configuration.xsd @@ -510,6 +510,8 @@ + + @@ -2802,6 +2804,22 @@ + + + + the default number of consumers needed before dispatch can start for queues under the address. + + + + + + + + the default delay to wait before dispatching if number of consumers before dispatch is not met for queues under the address. + + + + @@ -3119,6 +3137,8 @@ + + diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java index 4cdd11c477e..8fcac204cdb 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java @@ -423,7 +423,8 @@ private void verifyAddresses() { assertEquals("color='blue'", queueConfiguration.getFilterString()); assertEquals(ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), queueConfiguration.getPurgeOnNoConsumers()); assertEquals("addr1", queueConfiguration.getAddress()); - assertEquals(ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), queueConfiguration.getMaxConsumers()); + // If null, then default will be taken from address-settings (which defaults to ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()) + assertEquals(null, queueConfiguration.getMaxConsumers()); // Addr 1 Queue 2 queueConfiguration = addressConfiguration.getQueueConfigurations().get(1); @@ -431,7 +432,7 @@ private void verifyAddresses() { assertEquals("q2", queueConfiguration.getName()); assertTrue(queueConfiguration.isDurable()); assertEquals("color='green'", queueConfiguration.getFilterString()); - assertEquals(Queue.MAX_CONSUMERS_UNLIMITED, queueConfiguration.getMaxConsumers()); + assertEquals(Queue.MAX_CONSUMERS_UNLIMITED, queueConfiguration.getMaxConsumers().intValue()); assertFalse(queueConfiguration.getPurgeOnNoConsumers()); assertEquals("addr1", queueConfiguration.getAddress()); @@ -449,7 +450,7 @@ private void verifyAddresses() { assertEquals("q3", queueConfiguration.getName()); assertTrue(queueConfiguration.isDurable()); assertEquals("color='red'", queueConfiguration.getFilterString()); - assertEquals(10, queueConfiguration.getMaxConsumers()); + assertEquals(10, queueConfiguration.getMaxConsumers().intValue()); assertEquals(ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), queueConfiguration.getPurgeOnNoConsumers()); assertEquals("addr2", queueConfiguration.getAddress()); @@ -459,7 +460,8 @@ private void verifyAddresses() { assertEquals("q4", queueConfiguration.getName()); assertTrue(queueConfiguration.isDurable()); assertNull(queueConfiguration.getFilterString()); - assertEquals(ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), queueConfiguration.getMaxConsumers()); + // If null, then default will be taken from address-settings (which defaults to ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()) + assertEquals(null, queueConfiguration.getMaxConsumers()); assertTrue(queueConfiguration.getPurgeOnNoConsumers()); assertEquals("addr2", queueConfiguration.getAddress()); diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java index f45a1ddce2a..96de8c761be 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java @@ -793,6 +793,41 @@ public void setPurgeOnNoConsumers(boolean value) { } + @Override + public int getConsumersBeforeDispatch() { + return 0; + } + + @Override + public void setConsumersBeforeDispatch(int consumersBeforeDispatch) { + + } + + @Override + public long getDelayBeforeDispatch() { + return 0; + } + + @Override + public void setDelayBeforeDispatch(long delayBeforeDispatch) { + + } + + @Override + public long getDispatchStartTime() { + return 0; + } + + @Override + public boolean isDispatching() { + return false; + } + + @Override + public void setDispatching(boolean dispatching) { + + } + @Override public void setMaxConsumer(int maxConsumers) { diff --git a/artemis-tools/src/test/resources/artemis-configuration.xsd b/artemis-tools/src/test/resources/artemis-configuration.xsd index 41c881e6de2..30de90ba417 100644 --- a/artemis-tools/src/test/resources/artemis-configuration.xsd +++ b/artemis-tools/src/test/resources/artemis-configuration.xsd @@ -491,6 +491,8 @@ + + @@ -2498,6 +2500,22 @@ + + + + the default number of consumers needed before dispatch can start for queues under the address. + + + + + + + + the default delay to wait before dispatching if number of consumers before dispatch is not met for queues under the address. + + + + @@ -2769,6 +2787,8 @@ + + diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ConsumerDelayDispatchTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ConsumerDelayDispatchTest.java new file mode 100644 index 00000000000..4d2d195c5ec --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ConsumerDelayDispatchTest.java @@ -0,0 +1,223 @@ +/* + * 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.artemis.tests.integration.jms.client; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.tests.util.JMSTestBase; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * Exclusive Test + */ +public class ConsumerDelayDispatchTest extends JMSTestBase { + + private SimpleString queueName = SimpleString.toSimpleString("jms.consumer.delay.queue"); + private SimpleString normalQueueName = SimpleString.toSimpleString("jms.noraml.queue"); + + private static final long DELAY_BEFORE_DISPATCH = 10000L; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + server.createQueue(queueName, RoutingType.ANYCAST, queueName, null, null, true, false, false, false, false, -1, false, true, false, 2, DELAY_BEFORE_DISPATCH, true); + server.createQueue(normalQueueName, RoutingType.ANYCAST, normalQueueName, null, null, true, false, false, false, false, -1, false, true, false, 0, -1, true); + + } + + + protected ConnectionFactory getCF() throws Exception { + return cf; + } + + @Test + public void testNoDelayOnDefault() throws Exception { + sendMessage(normalQueueName); + + ConnectionFactory fact = getCF(); + Connection connection = fact.createConnection(); + + try { + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + connection.start(); + + Destination queue = session.createQueue(normalQueueName.toString()); + MessageConsumer consumer1 = session.createConsumer(queue); + + Assert.assertNotNull(receive(consumer1)); + } finally { + connection.close(); + } + } + + @Test + public void testDelayBeforeDispatch() throws Exception { + sendMessage(queueName); + + ConnectionFactory fact = getCF(); + Connection connection = fact.createConnection(); + + try { + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + connection.start(); + + Destination queue = session.createQueue(queueName.toString()); + MessageConsumer consumer1 = session.createConsumer(queue); + + Assert.assertNull(receive(consumer1)); + Thread.sleep(DELAY_BEFORE_DISPATCH); + + Assert.assertNotNull(receive(consumer1)); + } finally { + connection.close(); + } + } + + @Test + public void testConsumersBeforeDispatch() throws Exception { + sendMessage(queueName); + + + ConnectionFactory fact = getCF(); + Connection connection = fact.createConnection(); + + try { + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + connection.start(); + Destination queue = session.createQueue(queueName.toString()); + + MessageConsumer consumer1 = session.createConsumer(queue); + + Assert.assertNull(receive(consumer1)); + + MessageConsumer consumer2 = session.createConsumer(queue); + + Assert.assertNotNull(receive(consumer1, consumer2)); + } finally { + connection.close(); + } + } + + + @Test + public void testContinueAndResetConsumer() throws Exception { + sendMessage(queueName); + + ConnectionFactory fact = getCF(); + Connection connection = fact.createConnection(); + + try { + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + connection.start(); + Destination queue = session.createQueue(queueName.toString()); + + MessageConsumer consumer1 = session.createConsumer(queue); + + Assert.assertNull(receive(consumer1)); + + MessageConsumer consumer2 = session.createConsumer(queue); + + Assert.assertNotNull(receive(consumer1, consumer2)); + + consumer2.close(); + + //Ensure that now dispatch is active, if we close a consumer, dispatching continues. + sendMessage(queueName); + + Assert.assertNotNull(receive(consumer1)); + + //Stop all consumers, which should reset dispatch rules. + consumer1.close(); + + //Ensure that once all consumers are stopped, that dispatch rules reset and wait for min consumers. + sendMessage(queueName); + + MessageConsumer consumer3 = session.createConsumer(queue); + + Assert.assertNull(receive(consumer3)); + + MessageConsumer consumer4 = session.createConsumer(queue); + + Assert.assertNotNull(receive(consumer3, consumer4)); + + + //Stop all consumers, which should reset dispatch rules. + consumer3.close(); + consumer4.close(); + + //Ensure that once all consumers are stopped, that dispatch rules reset and wait for delay. + sendMessage(queueName); + + MessageConsumer consumer5 = session.createConsumer(queue); + + Assert.assertNull(receive(consumer5)); + + Thread.sleep(DELAY_BEFORE_DISPATCH); + + Assert.assertNotNull(receive(consumer5)); + + } finally { + connection.close(); + } + } + + private Message receive(MessageConsumer consumer1) throws JMSException { + return consumer1.receive(1000); + } + + private Message receive(MessageConsumer consumer1, MessageConsumer consumer2) throws JMSException { + Message receivedMessage = receive(consumer1); + if (receivedMessage == null) { + receivedMessage = receive(consumer2); + } + return receivedMessage; + } + + public void sendMessage(SimpleString queue) throws Exception { + ConnectionFactory fact = getCF(); + Connection connection = fact.createConnection(); + try { + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + connection.start(); + + Destination destination = session.createQueue(queue.toString()); + MessageProducer producer = session.createProducer(destination); + + TextMessage message = session.createTextMessage(); + message.setText("Message"); + producer.send(message); + } finally { + connection.close(); + } + } + +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java index ae24a45d2a3..2d78092a51c 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java @@ -155,6 +155,11 @@ public String updateQueue(@Parameter(name = "name", desc = "Name of the queue") return (String) proxy.invokeOperation("updateQueue", name, routingType, maxConsumers, purgeOnNoConsumers, exclusive, user); } + @Override + public String updateQueue(String name, String routingType, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, Integer consumersBeforeDispatch, Long delayBeforeDispatch, String user) throws Exception { + return null; + } + @Override public void deleteAddress(@Parameter(name = "name", desc = "The name of the address") String name) throws Exception { proxy.invokeOperation("deleteAddress", name); @@ -188,6 +193,11 @@ public void createQueue(String address,String name, String filter, boolean durab proxy.invokeOperation("createQueue", address, name, filter, durable, routingType); } + @Override + public String createQueue(String address, String routingType, String name, String filterStr, boolean durable, int maxConsumers, boolean purgeOnNoConsumers, boolean exclusive, boolean lastValue, int consumersBeforeDispatch, long delayBeforeDispatch, boolean autoCreateAddress) throws Exception { + return null; + } + @Override public void createQueue(final String address, final String name, final boolean durable) throws Exception { proxy.invokeOperation("createQueue", address, name, durable); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/QueueConfigRestartTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/QueueConfigRestartTest.java index e3b179b9106..ac2ed61669a 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/QueueConfigRestartTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/QueueConfigRestartTest.java @@ -105,6 +105,52 @@ public void testQueueConfigExclusiveAndRestart() throws Exception { Assert.assertTrue(queueBinding2.getQueue().isExclusive()); } + @Test + public void testQueueConfigConsumersBeforeDispatchAndRestart() throws Exception { + int consumersBeforeDispatch = 5; + ActiveMQServer server = createServer(true); + + server.start(); + + SimpleString address = new SimpleString("test.address"); + SimpleString queue = new SimpleString("test.queue"); + + server.createQueue(address, RoutingType.MULTICAST, queue, null, null, true, false, false, false,false, 10, true, true, true, consumersBeforeDispatch, -1, true); + + QueueBinding queueBinding1 = (QueueBinding)server.getPostOffice().getBinding(queue); + Assert.assertEquals(consumersBeforeDispatch, queueBinding1.getQueue().getConsumersBeforeDispatch()); + + server.stop(); + + server.start(); + + QueueBinding queueBinding2 = (QueueBinding)server.getPostOffice().getBinding(queue); + Assert.assertEquals(consumersBeforeDispatch, queueBinding1.getQueue().getConsumersBeforeDispatch()); + } + + @Test + public void testQueueConfigDelayBeforeDispatchAndRestart() throws Exception { + long delayBeforeDispatch = 5000L; + ActiveMQServer server = createServer(true); + + server.start(); + + SimpleString address = new SimpleString("test.address"); + SimpleString queue = new SimpleString("test.queue"); + + server.createQueue(address, RoutingType.MULTICAST, queue, null, null, true, false, false, false,false, 10, true, true, true, 0, delayBeforeDispatch, true); + + QueueBinding queueBinding1 = (QueueBinding)server.getPostOffice().getBinding(queue); + Assert.assertEquals(delayBeforeDispatch, queueBinding1.getQueue().getDelayBeforeDispatch()); + + server.stop(); + + server.start(); + + QueueBinding queueBinding2 = (QueueBinding)server.getPostOffice().getBinding(queue); + Assert.assertEquals(delayBeforeDispatch, queueBinding1.getQueue().getDelayBeforeDispatch()); + } + @Test public void testQueueConfigUserAndRestart() throws Exception { diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java index 192d7009d16..71ced7f35a8 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java @@ -48,6 +48,41 @@ public void setPurgeOnNoConsumers(boolean value) { } + @Override + public int getConsumersBeforeDispatch() { + return 0; + } + + @Override + public void setConsumersBeforeDispatch(int consumersBeforeDispatch) { + + } + + @Override + public long getDelayBeforeDispatch() { + return 0; + } + + @Override + public void setDelayBeforeDispatch(long delayBeforeDispatch) { + + } + + @Override + public long getDispatchStartTime() { + return 0; + } + + @Override + public boolean isDispatching() { + return false; + } + + @Override + public void setDispatching(boolean dispatching) { + + } + @Override public boolean isExclusive() { // no-op diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java index 3f350847d85..44e5823b2b7 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java @@ -49,6 +49,8 @@ public QueueBinding updateQueue(SimpleString name, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, + Integer consumersBeforeDispatch, + Long delayBeforeDispatch, SimpleString user) throws Exception { return null; } From 4ae8c7bab3ad27de53e63e62bbf6f892c90974e8 Mon Sep 17 00:00:00 2001 From: Benjamin Graf Date: Sat, 21 Jul 2018 16:57:12 +0200 Subject: [PATCH 092/207] ARTEMIS-1985: Switch from XA_RDONLY to XA_OK as return value for prepare transaction via OpenWire --- .../artemis/core/protocol/openwire/OpenWireConnection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java index b5adf346ff1..ced463b619a 100644 --- a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java +++ b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java @@ -1464,7 +1464,7 @@ public Response processPrepareTransaction(TransactionInfo info) throws Exception internalSession.resetTX(null); } - return new IntegerResponse(XAResource.XA_RDONLY); + return new IntegerResponse(XAResource.XA_OK); } @Override From 5fc60d7437dcc5aea217103e516e50e31fe7a467 Mon Sep 17 00:00:00 2001 From: "Christopher L. Shannon (cshannon)" Date: Wed, 25 Jul 2018 06:42:29 -0400 Subject: [PATCH 093/207] ARTEMIS-1987 - Add consumer window size to AddressSettings Support configuring a default consumer window size via AddressSettings which will allow sensible defaults to be used by address type --- .../api/core/client/ClientSession.java | 2 + .../core/client/impl/QueueQueryImpl.java | 14 +++- .../core/impl/ActiveMQSessionContext.java | 34 +++++++--- .../SessionQueueQueryResponseMessage_V3.java | 32 ++++++++-- .../artemis/core/server/QueueQueryResult.java | 11 +++- .../spi/core/remoting/SessionContext.java | 3 + .../impl/FileConfigurationParser.java | 4 ++ .../artemis/core/server/ServerSession.java | 2 + .../core/server/impl/ActiveMQServerImpl.java | 21 +++--- .../core/server/impl/ServerSessionImpl.java | 6 ++ .../core/settings/impl/AddressSettings.java | 40 +++++++++++- .../schema/artemis-configuration.xsd | 8 +++ .../config/impl/FileConfigurationTest.java | 1 + .../ConfigurationTest-full-config.xml | 1 + ...nTest-xinclude-config-address-settings.xml | 1 + docs/user-manual/en/address-model.md | 5 ++ docs/user-manual/en/flow-control.md | 2 +- .../client/ConsumerWindowSizeTest.java | 64 +++++++++++++++++++ 18 files changed, 222 insertions(+), 29 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java index dbc1e890df4..414def33dee 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java @@ -147,6 +147,8 @@ public interface QueueQuery { Boolean isExclusive(); Boolean isLastValue(); + + Integer getDefaultConsumerWindowSize(); } // Lifecycle operations ------------------------------------------ diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/QueueQueryImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/QueueQueryImpl.java index 8dc35a90f3a..d377d182d3a 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/QueueQueryImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/QueueQueryImpl.java @@ -16,9 +16,9 @@ */ package org.apache.activemq.artemis.core.client.impl; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.client.ClientSession; -import org.apache.activemq.artemis.api.core.RoutingType; public class QueueQueryImpl implements ClientSession.QueueQuery { @@ -52,6 +52,8 @@ public class QueueQueryImpl implements ClientSession.QueueQuery { private final Boolean lastValue; + private final Integer defaultConsumerWindowSize; + public QueueQueryImpl(final boolean durable, final boolean temporary, final int consumerCount, @@ -88,7 +90,7 @@ public QueueQueryImpl(final boolean durable, final boolean autoCreated, final boolean purgeOnNoConsumers, final RoutingType routingType) { - this(durable, temporary, consumerCount, messageCount, filterString, address, name, exists, autoCreateQueues, maxConsumers, autoCreated, purgeOnNoConsumers, routingType, null, null); + this(durable, temporary, consumerCount, messageCount, filterString, address, name, exists, autoCreateQueues, maxConsumers, autoCreated, purgeOnNoConsumers, routingType, null, null, null); } public QueueQueryImpl(final boolean durable, final boolean temporary, @@ -104,7 +106,8 @@ public QueueQueryImpl(final boolean durable, final boolean purgeOnNoConsumers, final RoutingType routingType, final Boolean exclusive, - final Boolean lastValue) { + final Boolean lastValue, + final Integer defaultConsumerWindowSize) { this.durable = durable; this.temporary = temporary; this.consumerCount = consumerCount; @@ -120,6 +123,7 @@ public QueueQueryImpl(final boolean durable, this.routingType = routingType; this.exclusive = exclusive; this.lastValue = lastValue; + this.defaultConsumerWindowSize = defaultConsumerWindowSize; } @Override @@ -197,5 +201,9 @@ public Boolean isLastValue() { return lastValue; } + @Override + public Integer getDefaultConsumerWindowSize() { + return defaultConsumerWindowSize; + } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java index fccb041143c..1268bbac401 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java @@ -16,9 +16,12 @@ */ package org.apache.activemq.artemis.core.protocol.core.impl; -import javax.transaction.xa.XAException; -import javax.transaction.xa.XAResource; -import javax.transaction.xa.Xid; +import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.DISCONNECT_CONSUMER; +import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.EXCEPTION; +import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.SESS_RECEIVE_CONTINUATION; +import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.SESS_RECEIVE_LARGE_MSG; +import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.SESS_RECEIVE_MSG; + import java.security.AccessController; import java.security.PrivilegedAction; import java.util.EnumSet; @@ -29,6 +32,10 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQException; @@ -37,6 +44,7 @@ import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.api.core.client.ClientConsumer; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.core.client.SendAcknowledgementHandler; @@ -85,6 +93,7 @@ import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionProducerCreditsMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionQueueQueryMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionQueueQueryResponseMessage; +import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionQueueQueryResponseMessage_V3; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionReceiveContinuationMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionReceiveLargeMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionReceiveMessage; @@ -115,12 +124,6 @@ import org.apache.activemq.artemis.utils.TokenBucketLimiterImpl; import org.jboss.logging.Logger; -import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.DISCONNECT_CONSUMER; -import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.EXCEPTION; -import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.SESS_RECEIVE_CONTINUATION; -import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.SESS_RECEIVE_LARGE_MSG; -import static org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl.SESS_RECEIVE_MSG; - public class ActiveMQSessionContext extends SessionContext { private static final Logger logger = Logger.getLogger(ActiveMQSessionContext.class); @@ -324,8 +327,9 @@ public ClientConsumerInternal createConsumer(SimpleString queueName, // The actual windows size that gets used is determined by the user since // could be overridden on the queue settings // The value we send is just a hint + final int consumerWindowSize = windowSize == ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE ? this.getDefaultConsumerWindowSize(queueInfo) : windowSize; - return new ClientConsumerImpl(session, consumerContext, queueName, filterString, browseOnly, calcWindowSize(windowSize), ackBatchSize, maxRate > 0 ? new TokenBucketLimiterImpl(maxRate, false) : null, executor, flowControlExecutor, this, queueInfo.toQueueQuery(), lookupTCCL()); + return new ClientConsumerImpl(session, consumerContext, queueName, filterString, browseOnly, calcWindowSize(consumerWindowSize), ackBatchSize, maxRate > 0 ? new TokenBucketLimiterImpl(maxRate, false) : null, executor, flowControlExecutor, this, queueInfo.toQueueQuery(), lookupTCCL()); } @Override @@ -822,6 +826,16 @@ public void resetMetadata(HashMap metaDataToSend) { } } + @Override + public int getDefaultConsumerWindowSize(SessionQueueQueryResponseMessage response) throws ActiveMQException { + if (response instanceof SessionQueueQueryResponseMessage_V3) { + final Integer defaultConsumerWindowSize = ((SessionQueueQueryResponseMessage_V3) response).getDefaultConsumerWindowSize(); + return defaultConsumerWindowSize != null ? defaultConsumerWindowSize : ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE; + } else { + return ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE; + } + } + private Channel getCreateChannel() { return getCoreConnection().getChannel(1, -1); } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionQueueQueryResponseMessage_V3.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionQueueQueryResponseMessage_V3.java index b982744e049..0c4c40fd0a2 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionQueueQueryResponseMessage_V3.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionQueueQueryResponseMessage_V3.java @@ -38,12 +38,14 @@ public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryRespon protected Boolean lastValue; + protected Integer defaultConsumerWindowSize; + public SessionQueueQueryResponseMessage_V3(final QueueQueryResult result) { - this(result.getName(), result.getAddress(), result.isDurable(), result.isTemporary(), result.getFilterString(), result.getConsumerCount(), result.getMessageCount(), result.isExists(), result.isAutoCreateQueues(), result.isAutoCreated(), result.isPurgeOnNoConsumers(), result.getRoutingType(), result.getMaxConsumers(), result.isExclusive(), result.isLastValue()); + this(result.getName(), result.getAddress(), result.isDurable(), result.isTemporary(), result.getFilterString(), result.getConsumerCount(), result.getMessageCount(), result.isExists(), result.isAutoCreateQueues(), result.isAutoCreated(), result.isPurgeOnNoConsumers(), result.getRoutingType(), result.getMaxConsumers(), result.isExclusive(), result.isLastValue(), result.getDefaultConsumerWindowSize()); } public SessionQueueQueryResponseMessage_V3() { - this(null, null, false, false, null, 0, 0, false, false, false, false, RoutingType.MULTICAST, -1, null, null); + this(null, null, false, false, null, 0, 0, false, false, false, false, RoutingType.MULTICAST, -1, null, null, null); } private SessionQueueQueryResponseMessage_V3(final SimpleString name, @@ -60,7 +62,8 @@ private SessionQueueQueryResponseMessage_V3(final SimpleString name, final RoutingType routingType, final int maxConsumers, final Boolean exclusive, - final Boolean lastValue) { + final Boolean lastValue, + final Integer defaultConsumerWindowSize) { super(SESS_QUEUEQUERY_RESP_V3); this.durable = durable; @@ -92,6 +95,8 @@ private SessionQueueQueryResponseMessage_V3(final SimpleString name, this.exclusive = exclusive; this.lastValue = lastValue; + + this.defaultConsumerWindowSize = defaultConsumerWindowSize; } public boolean isAutoCreated() { @@ -142,6 +147,14 @@ public void setLastValue(Boolean lastValue) { this.lastValue = lastValue; } + public Integer getDefaultConsumerWindowSize() { + return defaultConsumerWindowSize; + } + + public void setDefaultConsumerWindowSize(Integer defaultConsumerWindowSize) { + this.defaultConsumerWindowSize = defaultConsumerWindowSize; + } + @Override public void encodeRest(final ActiveMQBuffer buffer) { super.encodeRest(buffer); @@ -151,6 +164,7 @@ public void encodeRest(final ActiveMQBuffer buffer) { buffer.writeInt(maxConsumers); BufferHelper.writeNullableBoolean(buffer, exclusive); BufferHelper.writeNullableBoolean(buffer, lastValue); + BufferHelper.writeNullableInteger(buffer, defaultConsumerWindowSize); } @Override @@ -164,6 +178,9 @@ public void decodeRest(final ActiveMQBuffer buffer) { exclusive = BufferHelper.readNullableBoolean(buffer); lastValue = BufferHelper.readNullableBoolean(buffer); } + if (buffer.readableBytes() > 0) { + defaultConsumerWindowSize = BufferHelper.readNullableInteger(buffer); + } } @Override @@ -176,6 +193,7 @@ public int hashCode() { result = prime * result + maxConsumers; result = prime * result + (exclusive == null ? 0 : exclusive ? 1231 : 1237); result = prime * result + (lastValue == null ? 0 : lastValue ? 1231 : 1237); + result = prime * result + ((defaultConsumerWindowSize == null) ? 0 : defaultConsumerWindowSize.hashCode()); return result; } @@ -195,12 +213,13 @@ public String getParentString() { buff.append(", maxConsumers=" + maxConsumers); buff.append(", exclusive=" + exclusive); buff.append(", lastValue=" + lastValue); + buff.append(", defaultConsumerWindowSize=" + defaultConsumerWindowSize); return buff.toString(); } @Override public ClientSession.QueueQuery toQueueQuery() { - return new QueueQueryImpl(isDurable(), isTemporary(), getConsumerCount(), getMessageCount(), getFilterString(), getAddress(), getName(), isExists(), isAutoCreateQueues(), getMaxConsumers(), isAutoCreated(), isPurgeOnNoConsumers(), getRoutingType(), isExclusive(), isLastValue()); + return new QueueQueryImpl(isDurable(), isTemporary(), getConsumerCount(), getMessageCount(), getFilterString(), getAddress(), getName(), isExists(), isAutoCreateQueues(), getMaxConsumers(), isAutoCreated(), isPurgeOnNoConsumers(), getRoutingType(), isExclusive(), isLastValue(), getDefaultConsumerWindowSize()); } @Override @@ -226,6 +245,11 @@ public boolean equals(Object obj) { return false; } else if (!lastValue.equals(other.lastValue)) return false; + if (defaultConsumerWindowSize == null) { + if (other.defaultConsumerWindowSize != null) + return false; + } else if (!defaultConsumerWindowSize.equals(other.defaultConsumerWindowSize)) + return false; if (routingType == null) { if (other.routingType != null) return false; diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/server/QueueQueryResult.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/server/QueueQueryResult.java index 6497e3f99ad..b09d3101420 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/server/QueueQueryResult.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/server/QueueQueryResult.java @@ -51,6 +51,8 @@ public class QueueQueryResult { private Boolean lastValue; + private Integer defaultConsumerWindowSize; + public QueueQueryResult(final SimpleString name, final SimpleString address, final boolean durable, @@ -65,7 +67,8 @@ public QueueQueryResult(final SimpleString name, final RoutingType routingType, final int maxConsumers, final Boolean exclusive, - final Boolean lastValue) { + final Boolean lastValue, + final Integer defaultConsumerWindowSize) { this.durable = durable; this.temporary = temporary; @@ -95,6 +98,8 @@ public QueueQueryResult(final SimpleString name, this.exclusive = exclusive; this.lastValue = lastValue; + + this.defaultConsumerWindowSize = defaultConsumerWindowSize; } public boolean isExists() { @@ -160,4 +165,8 @@ public Boolean isExclusive() { public Boolean isLastValue() { return lastValue; } + + public Integer getDefaultConsumerWindowSize() { + return defaultConsumerWindowSize; + } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/remoting/SessionContext.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/remoting/SessionContext.java index 65713358d6d..0d86354dd3f 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/remoting/SessionContext.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/remoting/SessionContext.java @@ -36,6 +36,7 @@ import org.apache.activemq.artemis.core.client.impl.ClientMessageInternal; import org.apache.activemq.artemis.core.client.impl.ClientProducerCredits; import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal; +import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionQueueQueryResponseMessage; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.utils.IDGenerator; import org.apache.activemq.artemis.utils.SimpleIDGenerator; @@ -325,6 +326,8 @@ public abstract void recreateSession(String username, public abstract void resetMetadata(HashMap metaDataToSend); + public abstract int getDefaultConsumerWindowSize(SessionQueueQueryResponseMessage response) throws ActiveMQException; + // Failover utility classes /** diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java index 6c6a94cce87..dc5ba16320f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java @@ -241,6 +241,8 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { private static final String AMQP_USE_CORE_SUBSCRIPTION_NAMING = "amqp-use-core-subscription-naming"; + private static final String DEFAULT_CONSUMER_WINDOW_SIZE = "default-consumer-window-size"; + // Attributes ---------------------------------------------------- @@ -1068,6 +1070,8 @@ protected Pair parseAddressSettings(final Node node) { Validators.ROUTING_TYPE.validate(DEFAULT_ADDRESS_ROUTING_TYPE, value); RoutingType routingType = RoutingType.valueOf(value); addressSettings.setDefaultAddressRoutingType(routingType); + } else if (DEFAULT_CONSUMER_WINDOW_SIZE.equalsIgnoreCase(name)) { + addressSettings.setDefaultConsumerWindowSize(XMLUtil.parseInt(child)); } } return setting; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java index 8c2f74885d0..6d720886ce2 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java @@ -358,4 +358,6 @@ Pair> getAddressAndRoutingTypes(SimpleString int getConsumerCount(); int getProducerCount(); + + int getDefaultConsumerWindowSize(SimpleString address); } \ No newline at end of file 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 a2cdf55b999..f01e0c7639a 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 @@ -865,11 +865,14 @@ public QueueQueryResult queueQuery(SimpleString name) { throw ActiveMQMessageBundle.BUNDLE.queueNameIsNull(); } - boolean autoCreateQueues = getAddressSettingsRepository().getMatch(name.toString()).isAutoCreateQueues(); - boolean defaultPurgeOnNoConsumers = getAddressSettingsRepository().getMatch(name.toString()).isDefaultPurgeOnNoConsumers(); - int defaultMaxConsumers = getAddressSettingsRepository().getMatch(name.toString()).getDefaultMaxConsumers(); - boolean defaultExclusiveQueue = getAddressSettingsRepository().getMatch(name.toString()).isDefaultExclusiveQueue(); - boolean defaultLastValueQueue = getAddressSettingsRepository().getMatch(name.toString()).isDefaultLastValueQueue(); + final AddressSettings addressSettings = getAddressSettingsRepository().getMatch(name.toString()); + + boolean autoCreateQueues = addressSettings.isAutoCreateQueues(); + boolean defaultPurgeOnNoConsumers = addressSettings.isDefaultPurgeOnNoConsumers(); + int defaultMaxConsumers = addressSettings.getDefaultMaxConsumers(); + boolean defaultExclusiveQueue = addressSettings.isDefaultExclusiveQueue(); + boolean defaultLastValueQueue = addressSettings.isDefaultLastValueQueue(); + int defaultConsumerWindowSize = addressSettings.getDefaultConsumerWindowSize(); QueueQueryResult response; @@ -884,14 +887,14 @@ public QueueQueryResult queueQuery(SimpleString name) { SimpleString filterString = filter == null ? null : filter.getFilterString(); - response = new QueueQueryResult(name, binding.getAddress(), queue.isDurable(), queue.isTemporary(), filterString, queue.getConsumerCount(), queue.getMessageCount(), autoCreateQueues, true, queue.isAutoCreated(), queue.isPurgeOnNoConsumers(), queue.getRoutingType(), queue.getMaxConsumers(), queue.isExclusive(), queue.isLastValue()); + response = new QueueQueryResult(name, binding.getAddress(), queue.isDurable(), queue.isTemporary(), filterString, queue.getConsumerCount(), queue.getMessageCount(), autoCreateQueues, true, queue.isAutoCreated(), queue.isPurgeOnNoConsumers(), queue.getRoutingType(), queue.getMaxConsumers(), queue.isExclusive(), queue.isLastValue(), defaultConsumerWindowSize); } else if (name.equals(managementAddress)) { // make an exception for the management address (see HORNETQ-29) - response = new QueueQueryResult(name, managementAddress, true, false, null, -1, -1, autoCreateQueues, true, false, false, RoutingType.MULTICAST, -1, false, false); + response = new QueueQueryResult(name, managementAddress, true, false, null, -1, -1, autoCreateQueues, true, false, false, RoutingType.MULTICAST, -1, false, false, defaultConsumerWindowSize); } else if (autoCreateQueues) { - response = new QueueQueryResult(name, name, true, false, null, 0, 0, true, false, false, defaultPurgeOnNoConsumers, RoutingType.MULTICAST, defaultMaxConsumers, defaultExclusiveQueue, defaultLastValueQueue); + response = new QueueQueryResult(name, name, true, false, null, 0, 0, true, false, false, defaultPurgeOnNoConsumers, RoutingType.MULTICAST, defaultMaxConsumers, defaultExclusiveQueue, defaultLastValueQueue, defaultConsumerWindowSize); } else { - response = new QueueQueryResult(null, null, false, false, null, 0, 0, false, false, false, false, RoutingType.MULTICAST, 0, null, null); + response = new QueueQueryResult(null, null, false, false, null, 0, 0, false, false, false, false, RoutingType.MULTICAST, 0, null, null, defaultConsumerWindowSize); } return response; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java index 7388e568e28..c0bca6b666f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java @@ -1904,4 +1904,10 @@ public int getConsumerCount() { public int getProducerCount() { return getServerProducers().size(); } + + @Override + public int getDefaultConsumerWindowSize(SimpleString address) { + AddressSettings as = server.getAddressSettingsRepository().getMatch(address.toString()); + return as.getDefaultConsumerWindowSize(); + } } \ No newline at end of file diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java index 6fc5019c17d..f2eb488ae84 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java @@ -21,6 +21,7 @@ import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.core.journal.EncodingSupport; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.core.settings.Mergeable; @@ -178,6 +179,8 @@ public class AddressSettings implements Mergeable, Serializable private RoutingType defaultAddressRoutingType = null; + private Integer defaultConsumerWindowSize = null; + //from amq5 //make it transient private transient Integer queuePrefetch = null; @@ -222,6 +225,7 @@ public AddressSettings(AddressSettings other) { this.defaultDelayBeforeDispatch = other.defaultDelayBeforeDispatch; this.defaultQueueRoutingType = other.defaultQueueRoutingType; this.defaultAddressRoutingType = other.defaultAddressRoutingType; + this.defaultConsumerWindowSize = other.defaultConsumerWindowSize; } public AddressSettings() { @@ -579,6 +583,21 @@ public AddressSettings setMaxSizeBytesRejectThreshold(long maxSizeBytesRejectThr return this; } + /** + * @return the defaultConsumerWindowSize + */ + public int getDefaultConsumerWindowSize() { + return defaultConsumerWindowSize != null ? defaultConsumerWindowSize : ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE; + } + + /** + * @param defaultConsumerWindowSize the defaultConsumerWindowSize to set + */ + public AddressSettings setDefaultConsumerWindowSize(int defaultConsumerWindowSize) { + this.defaultConsumerWindowSize = defaultConsumerWindowSize; + return this; + } + /** * merge 2 objects in to 1 * @@ -694,6 +713,9 @@ public void merge(final AddressSettings merged) { if (defaultExclusiveQueue == null) { defaultExclusiveQueue = merged.defaultExclusiveQueue; } + if (defaultConsumerWindowSize == null) { + defaultConsumerWindowSize = merged.defaultConsumerWindowSize; + } if (defaultLastValueQueue == null) { defaultLastValueQueue = merged.defaultLastValueQueue; } @@ -811,6 +833,10 @@ public void decode(ActiveMQBuffer buffer) { if (buffer.readableBytes() > 0) { defaultDelayBeforeDispatch = BufferHelper.readNullableLong(buffer); } + + if (buffer.readableBytes() > 0) { + defaultConsumerWindowSize = BufferHelper.readNullableInteger(buffer); + } } @Override @@ -851,7 +877,8 @@ public int getEncodeSize() { DataConstants.SIZE_BYTE + BufferHelper.sizeOfNullableBoolean(defaultExclusiveQueue) + BufferHelper.sizeOfNullableInteger(defaultConsumersBeforeDispatch) + - BufferHelper.sizeOfNullableLong(defaultDelayBeforeDispatch); + BufferHelper.sizeOfNullableLong(defaultDelayBeforeDispatch) + + BufferHelper.sizeOfNullableInteger(defaultConsumerWindowSize); } @Override @@ -932,6 +959,8 @@ public void encode(ActiveMQBuffer buffer) { BufferHelper.writeNullableLong(buffer, defaultDelayBeforeDispatch); + BufferHelper.writeNullableInteger(buffer, defaultConsumerWindowSize); + } /* (non-Javadoc) @@ -980,6 +1009,7 @@ public int hashCode() { result = prime * result + ((defaultAddressRoutingType == null) ? 0 : defaultAddressRoutingType.hashCode()); result = prime * result + ((defaultConsumersBeforeDispatch == null) ? 0 : defaultConsumersBeforeDispatch.hashCode()); result = prime * result + ((defaultDelayBeforeDispatch == null) ? 0 : defaultDelayBeforeDispatch.hashCode()); + result = prime * result + ((defaultConsumerWindowSize == null) ? 0 : defaultConsumerWindowSize.hashCode()); return result; } @@ -1197,6 +1227,12 @@ public boolean equals(Object obj) { return false; } else if (!defaultDelayBeforeDispatch.equals(other.defaultDelayBeforeDispatch)) return false; + + if (defaultConsumerWindowSize == null) { + if (other.defaultConsumerWindowSize != null) + return false; + } else if (!defaultConsumerWindowSize.equals(other.defaultConsumerWindowSize)) + return false; return true; } @@ -1280,6 +1316,8 @@ public String toString() { defaultConsumersBeforeDispatch + ", defaultDelayBeforeDispatch=" + defaultDelayBeforeDispatch + + ", defaultClientWindowSize=" + + defaultConsumerWindowSize + "]"; } } diff --git a/artemis-server/src/main/resources/schema/artemis-configuration.xsd b/artemis-server/src/main/resources/schema/artemis-configuration.xsd index e96923d0f43..04e29316aac 100644 --- a/artemis-server/src/main/resources/schema/artemis-configuration.xsd +++ b/artemis-server/src/main/resources/schema/artemis-configuration.xsd @@ -3011,6 +3011,14 @@ + + + + the default window size for a consumer + + + + diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java index 8fcac204cdb..114779d318f 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java @@ -346,6 +346,7 @@ public void testDefaults() { assertEquals(15, conf.getAddressesSettings().get("a2").getDefaultMaxConsumers()); assertEquals(RoutingType.MULTICAST, conf.getAddressesSettings().get("a2").getDefaultQueueRoutingType()); assertEquals(RoutingType.ANYCAST, conf.getAddressesSettings().get("a2").getDefaultAddressRoutingType()); + assertEquals(10000, conf.getAddressesSettings().get("a2").getDefaultConsumerWindowSize()); assertTrue(conf.getResourceLimitSettings().containsKey("myUser")); assertEquals(104, conf.getResourceLimitSettings().get("myUser").getMaxConnections()); diff --git a/artemis-server/src/test/resources/ConfigurationTest-full-config.xml b/artemis-server/src/test/resources/ConfigurationTest-full-config.xml index eb561fcdfe6..186a712cb88 100644 --- a/artemis-server/src/test/resources/ConfigurationTest-full-config.xml +++ b/artemis-server/src/test/resources/ConfigurationTest-full-config.xml @@ -337,6 +337,7 @@ 15 MULTICAST ANYCAST + 10000 diff --git a/artemis-server/src/test/resources/ConfigurationTest-xinclude-config-address-settings.xml b/artemis-server/src/test/resources/ConfigurationTest-xinclude-config-address-settings.xml index 9e1ca40944f..443958e1682 100644 --- a/artemis-server/src/test/resources/ConfigurationTest-xinclude-config-address-settings.xml +++ b/artemis-server/src/test/resources/ConfigurationTest-xinclude-config-address-settings.xml @@ -62,5 +62,6 @@ 15 MULTICAST ANYCAST + 10000 \ No newline at end of file diff --git a/docs/user-manual/en/address-model.md b/docs/user-manual/en/address-model.md index f73adf24a1b..ac5e49bf2ab 100644 --- a/docs/user-manual/en/address-model.md +++ b/docs/user-manual/en/address-model.md @@ -790,3 +790,8 @@ types](#routing-type). address if the broker is unable to determine the routing-type based on the client and/or protocol semantics. Default is `MULTICAST`. Read more about [routing types](#routing-type). + +`default-consumer-window-size` defines the default `consumerWindowSize` value +for a `CORE` protocol consumer, if not defined the default will be set to +1 MiB (1024 * 1024 bytes). The consumer will use this value as the window size +if the value is not set on the client. Read more about [flow control](#flow-control). diff --git a/docs/user-manual/en/flow-control.md b/docs/user-manual/en/flow-control.md index 44fbee3f166..015107973db 100644 --- a/docs/user-manual/en/flow-control.md +++ b/docs/user-manual/en/flow-control.md @@ -33,7 +33,7 @@ buffered on each consumer is determined by the `consumerWindowSize` parameter. By default, the `consumerWindowSize` is set to 1 MiB (1024 \* 1024 -bytes). +bytes) unless overridden via ([Address Settings](address-model.md#configuring-addresses-and-queues-via-address-settings)) The value can be: diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java index d3adee0fe90..d4298e5087d 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java @@ -24,7 +24,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.api.core.client.ClientConsumer; import org.apache.activemq.artemis.api.core.client.ClientMessage; import org.apache.activemq.artemis.api.core.client.ClientProducer; @@ -32,6 +34,7 @@ import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; import org.apache.activemq.artemis.api.core.client.MessageHandler; import org.apache.activemq.artemis.api.core.client.ServerLocator; +import org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl; import org.apache.activemq.artemis.core.client.impl.ClientConsumerInternal; import org.apache.activemq.artemis.core.postoffice.Binding; import org.apache.activemq.artemis.core.postoffice.Bindings; @@ -1386,4 +1389,65 @@ private void testNoWindowRoundRobin(final boolean largeMessages) throws Exceptio } } + @Test + public void testDefaultConsumerWindowSize() throws Exception { + ActiveMQServer messagingService = createServer(false, isNetty()); + + messagingService.start(); + messagingService.createQueue(queueA, RoutingType.ANYCAST, queueA, null, true, false); + + ClientSessionFactory cf = createSessionFactory(locator); + ClientSession session = cf.createSession(false, true, true); + ClientConsumerImpl consumer = (ClientConsumerImpl) session.createConsumer(queueA); + + consumer.start(); + + assertEquals(ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE / 2, consumer.getClientWindowSize()); + } + + @Test + public void testConsumerWindowSizeAddressSettings() throws Exception { + ActiveMQServer messagingService = createServer(false, isNetty()); + + final int defaultConsumerWindowSize = 1024 * 5; + final AddressSettings settings = new AddressSettings(); + settings.setDefaultConsumerWindowSize(defaultConsumerWindowSize); + messagingService.getConfiguration() + .getAddressesSettings().put(queueA.toString(), settings); + + messagingService.start(); + messagingService.createQueue(queueA, RoutingType.ANYCAST, queueA, null, true, false); + + ClientSessionFactory cf = createSessionFactory(locator); + ClientSession session = cf.createSession(false, true, true); + ClientConsumerImpl consumer = (ClientConsumerImpl) session.createConsumer(queueA); + + session.start(); + + assertEquals(defaultConsumerWindowSize / 2, consumer.getClientWindowSize()); + } + + @Test + public void testConsumerWindowSizeAddressSettingsWildCard() throws Exception { + ActiveMQServer messagingService = createServer(false, isNetty()); + + final int defaultConsumerWindowSize = 1024 * 5; + final AddressSettings settings = new AddressSettings(); + settings.setDefaultConsumerWindowSize(defaultConsumerWindowSize); + messagingService.getConfiguration() + .getAddressesSettings().put("#", settings); + + messagingService.start(); + messagingService.createQueue(queueA, RoutingType.ANYCAST, queueA, null, true, false); + + ClientSessionFactory cf = createSessionFactory(locator); + ClientSession session = cf.createSession(false, true, true); + ClientConsumerImpl consumer = (ClientConsumerImpl) session.createConsumer(queueA); + ClientConsumerImpl consumer2 = (ClientConsumerImpl) session.createConsumer(queueA); + + session.start(); + + assertEquals(defaultConsumerWindowSize / 2, consumer.getClientWindowSize()); + assertEquals(defaultConsumerWindowSize / 2, consumer2.getClientWindowSize()); + } } From 332cee8e663e729e2af274715cf0e1911a320297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Sun, 22 Jul 2018 04:17:50 +0100 Subject: [PATCH 094/207] ARTEMIS-1997 - un-needed SimpleString creation on hotpath with Filters Create the SimpleString on construction of PropertyExpression so it can be re-used instead of creating it every time its filtered in the FilterImpl --- artemis-selector/pom.xml | 5 +++++ .../activemq/artemis/selector/filter/Filterable.java | 4 +++- .../artemis/selector/filter/PropertyExpression.java | 12 +++++++++--- .../activemq/artemis/selector/SelectorTest.java | 10 ++++++---- .../artemis/core/filter/impl/FilterImpl.java | 10 +++++----- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/artemis-selector/pom.xml b/artemis-selector/pom.xml index 1fc51b3c028..7d9b644ca6d 100644 --- a/artemis-selector/pom.xml +++ b/artemis-selector/pom.xml @@ -32,6 +32,11 @@ + + org.apache.activemq + artemis-commons + ${project.version} + xml-apis xml-apis diff --git a/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/Filterable.java b/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/Filterable.java index 53396137d75..4ac7620ef92 100755 --- a/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/Filterable.java +++ b/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/Filterable.java @@ -16,6 +16,8 @@ */ package org.apache.activemq.artemis.selector.filter; +import org.apache.activemq.artemis.api.core.SimpleString; + /** * A Filterable is the object being evaluated by the filters. It provides * access to filtered properties. @@ -41,7 +43,7 @@ public interface Filterable { * @param name * @return */ - Object getProperty(String name); + Object getProperty(SimpleString name); /** * Used by the NoLocal filter. diff --git a/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/PropertyExpression.java b/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/PropertyExpression.java index 4c8c8c502fc..9fa62672bbd 100755 --- a/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/PropertyExpression.java +++ b/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/PropertyExpression.java @@ -16,6 +16,8 @@ */ package org.apache.activemq.artemis.selector.filter; +import org.apache.activemq.artemis.api.core.SimpleString; + /** * Represents a property expression * @@ -23,9 +25,13 @@ */ public class PropertyExpression implements Expression { - private final String name; + private final SimpleString name; public PropertyExpression(String name) { + this(SimpleString.toSimpleString(name)); + } + + public PropertyExpression(SimpleString name) { this.name = name; } @@ -35,7 +41,7 @@ public Object evaluate(Filterable message) throws FilterException { } public String getName() { - return name; + return name.toString(); } /** @@ -43,7 +49,7 @@ public String getName() { */ @Override public String toString() { - return name; + return name.toString(); } /** diff --git a/artemis-selector/src/test/java/org/apache/activemq/artemis/selector/SelectorTest.java b/artemis-selector/src/test/java/org/apache/activemq/artemis/selector/SelectorTest.java index a540ed748b7..1be9d475a24 100755 --- a/artemis-selector/src/test/java/org/apache/activemq/artemis/selector/SelectorTest.java +++ b/artemis-selector/src/test/java/org/apache/activemq/artemis/selector/SelectorTest.java @@ -18,6 +18,7 @@ import java.util.HashMap; +import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.selector.filter.BooleanExpression; import org.apache.activemq.artemis.selector.filter.FilterException; import org.apache.activemq.artemis.selector.filter.Filterable; @@ -100,14 +101,15 @@ public T getBodyAs(Class type) throws FilterException { } @Override - public Object getProperty(String name) { - if ("JMSType".equals(name)) { + public Object getProperty(SimpleString name) { + String stringName = name.toString(); + if ("JMSType".equals(stringName)) { return type; } - if ("JMSMessageID".equals(name)) { + if ("JMSMessageID".equals(stringName)) { return messageId; } - return properties.get(name); + return properties.get(stringName); } public Object getDestination() { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/filter/impl/FilterImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/filter/impl/FilterImpl.java index 33a11870fdb..7ee7b6bf812 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/filter/impl/FilterImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/filter/impl/FilterImpl.java @@ -154,7 +154,7 @@ private static Object getHeaderFieldValue(final Message msg, final SimpleString // Proton stores JMSMessageID as NATIVE_MESSAGE_ID that is an arbitrary string String amqpNativeID = msg.getStringProperty(NATIVE_MESSAGE_ID); if (amqpNativeID != null) { - return new SimpleString(amqpNativeID); + return SimpleString.toSimpleString(amqpNativeID); } } // It's the stringified (hex) representation of a user id that can be used in a selector expression @@ -162,7 +162,7 @@ private static Object getHeaderFieldValue(final Message msg, final SimpleString if (userID.startsWith("ID:")) { return SimpleString.toSimpleString(userID); } else { - return new SimpleString("ID:" + msg.getUserID()); + return SimpleString.toSimpleString("ID:" + msg.getUserID()); } } else if (FilterConstants.ACTIVEMQ_PRIORITY.equals(fieldName)) { return Integer.valueOf(msg.getPriority()); @@ -190,10 +190,10 @@ private FilterableServerMessage(Message message) { } @Override - public Object getProperty(String id) { + public Object getProperty(SimpleString id) { Object result = null; - if (id.startsWith(FilterConstants.ACTIVEMQ_PREFIX.toString())) { - result = getHeaderFieldValue(message, new SimpleString(id)); + if (id.startsWith(FilterConstants.ACTIVEMQ_PREFIX)) { + result = getHeaderFieldValue(message, id); } if (result == null) { result = message.getObjectProperty(id); From 7764072c49bb0c70d74a8e828363089994acecb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Tue, 31 Jul 2018 14:49:39 +0100 Subject: [PATCH 095/207] ARTEMIS-2001 - JMSXGroupID and JMSXUserID in getPropertyNames Ensure JMSXGroupID and JMSXUserID is correctly returned by JMS getPropertyNames when set. --- .../activemq/artemis/reader/MessageUtil.java | 8 ++- .../client/ActiveMQMessagePropertiesTest.java | 68 +++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 artemis-jms-client/src/test/java/org/apache/activemq/artemis/jms/client/ActiveMQMessagePropertiesTest.java diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/reader/MessageUtil.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/reader/MessageUtil.java index e8f59204914..db1f3dc335a 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/reader/MessageUtil.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/reader/MessageUtil.java @@ -152,9 +152,11 @@ public static Set getPropertyNames(Message message) { HashSet set = new HashSet<>(); for (SimpleString propName : message.getPropertyNames()) { - if ((!propName.startsWith(JMS) || propName.startsWith(JMSX) || - propName.startsWith(JMS_)) && !propName.startsWith(CONNECTION_ID_PROPERTY_NAME) && !propName.equals(Message.HDR_ROUTING_TYPE) && - !propName.startsWith(Message.HDR_ROUTE_TO_IDS)) { + if (propName.equals(Message.HDR_GROUP_ID)) { + set.add(MessageUtil.JMSXGROUPID); + } else if (propName.equals(Message.HDR_VALIDATED_USER)) { + set.add(MessageUtil.JMSXUSERID); + } else if ((!propName.startsWith(JMS) || propName.startsWith(JMSX) || propName.startsWith(JMS_)) && !propName.startsWith(CONNECTION_ID_PROPERTY_NAME) && !propName.equals(Message.HDR_ROUTING_TYPE) && !propName.startsWith(Message.HDR_ROUTE_TO_IDS)) { set.add(propName.toString()); } } diff --git a/artemis-jms-client/src/test/java/org/apache/activemq/artemis/jms/client/ActiveMQMessagePropertiesTest.java b/artemis-jms-client/src/test/java/org/apache/activemq/artemis/jms/client/ActiveMQMessagePropertiesTest.java new file mode 100644 index 00000000000..cf9bc56876d --- /dev/null +++ b/artemis-jms-client/src/test/java/org/apache/activemq/artemis/jms/client/ActiveMQMessagePropertiesTest.java @@ -0,0 +1,68 @@ +/* + * 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.artemis.jms.client; + +import java.util.Enumeration; +import javax.jms.JMSException; +import org.apache.activemq.artemis.core.client.impl.ClientMessageImpl; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class ActiveMQMessagePropertiesTest { + + @Test + public void testJMSXGroupID() throws JMSException { + ActiveMQMessage activeMQMessage = newBlankActiveMQMessage(); + + assertFalse(contains(activeMQMessage.getPropertyNames(), "JMSXGroupID")); + + activeMQMessage.setStringProperty("JMSXGroupID", "Bob"); + assertEquals("Bob", activeMQMessage.getStringProperty("JMSXGroupID")); + + assertTrue(contains(activeMQMessage.getPropertyNames(), "JMSXGroupID")); + } + + @Test + public void testJMSXUserID() throws JMSException { + ActiveMQMessage activeMQMessage = newBlankActiveMQMessage(); + + assertFalse(contains(activeMQMessage.getPropertyNames(), "JMSXUserID")); + + activeMQMessage.setStringProperty("JMSXUserID", "Bob"); + assertEquals("Bob", activeMQMessage.getStringProperty("JMSXUserID")); + + assertTrue(contains(activeMQMessage.getPropertyNames(), "JMSXUserID")); + } + + private boolean contains(Enumeration enumeration, String key) { + while (enumeration.hasMoreElements()) { + if (enumeration.nextElement().equals(key)) { + return true; + } + } + return false; + } + + private ActiveMQMessage newBlankActiveMQMessage() throws JMSException { + ActiveMQMessage activeMQMessage = new ActiveMQMessage(new ClientMessageImpl(), null); + activeMQMessage.clearBody(); + activeMQMessage.clearProperties(); + return activeMQMessage; + } +} From 983232d27330515a50a3cdb9d7cd664ebf3ca6f2 Mon Sep 17 00:00:00 2001 From: Howard Gao Date: Mon, 30 Jul 2018 14:48:09 +0800 Subject: [PATCH 096/207] ARTEMIS-1995 Client fail over fails when live shut down too soon In a live-backup scenario, if the live is restarted and shutdown too soon, the client have a chance to fail on failover because it's internal topology is inconsistent with the final status. The client keeps connecting to live already shut down, never trying to connect to the backup. It's a porting from HORNETQ-1572. --- .../client/impl/ClientSessionFactoryImpl.java | 25 +++-- .../cluster/failover/FailoverTest.java | 106 ++++++++++++++++++ .../failover/LiveToLiveFailoverTest.java | 5 + 3 files changed, 125 insertions(+), 11 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java index 5c972e3ef14..81b6c44f4e4 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java @@ -77,11 +77,11 @@ public class ClientSessionFactoryImpl implements ClientSessionFactoryInternal, C private final ClientProtocolManager clientProtocolManager; - private final TransportConfiguration connectorConfig; + private TransportConfiguration connectorConfig; private TransportConfiguration currentConnectorConfig; - private TransportConfiguration backupConfig; + private volatile TransportConfiguration backupConfig; private ConnectorFactory connectorFactory; @@ -175,8 +175,6 @@ public ClientSessionFactoryImpl(final ServerLocatorInternal serverLocator, this.clientProtocolManager.setSessionFactory(this); - this.connectorConfig = connectorConfig; - this.currentConnectorConfig = connectorConfig; connectorFactory = instantiateConnectorFactory(connectorConfig.getFactoryClassName()); @@ -881,6 +879,10 @@ private void checkCloseConnection() { } } + //The order of connector configs to try to get a connection: + //currentConnectorConfig, backupConfig and then lastConnectorConfig. + //On each successful connect, the current and last will be + //updated properly. @Override public RemotingConnection getConnection() { if (closed) @@ -1101,8 +1103,8 @@ protected Connection createTransportConnection() { // Switching backup as live connector = backupConnector; + connectorConfig = currentConnectorConfig; currentConnectorConfig = backupConfig; - backupConfig = null; connectorFactory = backupConnectorFactory; return transportConnection; } @@ -1113,23 +1115,24 @@ protected Connection createTransportConnection() { } - if (currentConnectorConfig.equals(connectorConfig)) { + if (currentConnectorConfig.equals(connectorConfig) || connectorConfig == null) { // There was no changes on current and original connectors, just return null here and let the retry happen at the first portion of this method on the next retry return null; } - ConnectorFactory originalConnectorFactory = instantiateConnectorFactory(connectorConfig.getFactoryClassName()); + ConnectorFactory lastConnectorFactory = instantiateConnectorFactory(connectorConfig.getFactoryClassName()); - Connector originalConnector = createConnector(originalConnectorFactory, connectorConfig); + Connector lastConnector = createConnector(lastConnectorFactory, connectorConfig); - transportConnection = openTransportConnection(originalConnector); + transportConnection = openTransportConnection(lastConnector); if (transportConnection != null) { logger.debug("Returning into original connector"); - connector = originalConnector; - backupConfig = null; + connector = lastConnector; + TransportConfiguration temp = currentConnectorConfig; currentConnectorConfig = connectorConfig; + connectorConfig = temp; return transportConnection; } else { logger.debug("no connection been made, returning null"); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/FailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/FailoverTest.java index 20f5fdaa898..0a37abf1627 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/FailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/FailoverTest.java @@ -19,6 +19,7 @@ import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -45,6 +46,7 @@ import org.apache.activemq.artemis.api.core.client.MessageHandler; import org.apache.activemq.artemis.api.core.client.ServerLocator; import org.apache.activemq.artemis.api.core.client.SessionFailureListener; +import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryImpl; import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.core.server.cluster.ha.BackupPolicy; @@ -651,6 +653,110 @@ public void testFailBothRestartLive() throws Exception { Assert.assertEquals(0, sf.numConnections()); } + @Test(timeout = 10000) + public void testFailLiveTooSoon() throws Exception { + ServerLocator locator = getServerLocator(); + + locator.setReconnectAttempts(-1); + locator.setRetryInterval(10); + + sf = (ClientSessionFactoryInternal)locator.createSessionFactory(); + + waitForBackupConfig(sf); + + TransportConfiguration initialLive = getFieldFromSF(sf, "currentConnectorConfig"); + TransportConfiguration initialBackup = getFieldFromSF(sf, "backupConfig"); + + System.out.println("initlive: " + initialLive); + System.out.println("initback: " + initialBackup); + + TransportConfiguration last = getFieldFromSF(sf, "connectorConfig"); + TransportConfiguration current = getFieldFromSF(sf, "currentConnectorConfig"); + + System.out.println("now last: " + last); + System.out.println("now current: " + current); + assertTrue(current.equals(initialLive)); + + ClientSession session = createSession(sf, true, true); + + session.createQueue(FailoverTestBase.ADDRESS, FailoverTestBase.ADDRESS, true); + + //crash 1 + crash(); + + //make sure failover is ok + createSession(sf, true, true).close(); + + last = getFieldFromSF(sf, "connectorConfig"); + current = getFieldFromSF(sf, "currentConnectorConfig"); + + System.out.println("now after live crashed last: " + last); + System.out.println("now current: " + current); + + assertTrue(current.equals(initialBackup)); + + //fail back + beforeRestart(liveServer); + adaptLiveConfigForReplicatedFailBack(liveServer); + liveServer.getServer().start(); + + Assert.assertTrue("live initialized...", liveServer.getServer().waitForActivation(40, TimeUnit.SECONDS)); + int i = 0; + while (!backupServer.isStarted() && i++ < 100) { + Thread.sleep(100); + } + liveServer.getServer().waitForActivation(5, TimeUnit.SECONDS); + Assert.assertTrue(backupServer.isStarted()); + + //make sure failover is ok + createSession(sf, true, true).close(); + + last = getFieldFromSF(sf, "connectorConfig"); + current = getFieldFromSF(sf, "currentConnectorConfig"); + + System.out.println("now after live back again last: " + last); + System.out.println("now current: " + current); + + //cannot use equals here because the config's name (uuid) changes + //after failover + assertTrue(current.isSameParams(initialLive)); + + //now manually corrupt the backup in sf + setSFFieldValue(sf, "backupConfig", null); + + //crash 2 + crash(); + + beforeRestart(backupServer); + createSession(sf, true, true).close(); + + sf.close(); + Assert.assertEquals(0, sf.numSessions()); + Assert.assertEquals(0, sf.numConnections()); + } + + protected void waitForBackupConfig(ClientSessionFactoryInternal sf) throws NoSuchFieldException, IllegalAccessException, InterruptedException { + TransportConfiguration initialBackup = getFieldFromSF(sf, "backupConfig"); + int cnt = 50; + while (initialBackup == null && cnt > 0) { + cnt--; + Thread.sleep(200); + initialBackup = getFieldFromSF(sf, "backupConfig"); + } + } + + protected void setSFFieldValue(ClientSessionFactoryInternal sf, String tcName, Object value) throws NoSuchFieldException, IllegalAccessException { + Field tcField = ClientSessionFactoryImpl.class.getDeclaredField(tcName); + tcField.setAccessible(true); + tcField.set(sf, value); + } + + protected TransportConfiguration getFieldFromSF(ClientSessionFactoryInternal sf, String tcName) throws NoSuchFieldException, IllegalAccessException { + Field tcField = ClientSessionFactoryImpl.class.getDeclaredField(tcName); + tcField.setAccessible(true); + return (TransportConfiguration) tcField.get(sf); + } + /** * Basic fail-back test. * diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/LiveToLiveFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/LiveToLiveFailoverTest.java index e65602f00c5..101a85ebef4 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/LiveToLiveFailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/LiveToLiveFailoverTest.java @@ -362,6 +362,11 @@ public void testSimpleSendAfterFailoverDurableNonTemporary() throws Exception { @Ignore public void testCommitOccurredUnblockedAndResendNoDuplicates() throws Exception { } + + @Override + @Ignore + public void testFailLiveTooSoon() throws Exception { + } } From 06f689245427e9584410d88ccbc99f44bad4cc01 Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Thu, 26 Jul 2018 16:37:08 +0100 Subject: [PATCH 097/207] ARTEMIS-1992 Make JDBC File Lock map thread safe --- .../artemis/jdbc/store/file/JDBCSequentialFileFactory.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactory.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactory.java index 862bdbeb2d9..8f520f207df 100644 --- a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactory.java +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactory.java @@ -21,10 +21,10 @@ import java.nio.ByteBuffer; import java.sql.Connection; import java.sql.SQLException; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import org.apache.activemq.artemis.core.io.IOCriticalErrorListener; @@ -47,7 +47,7 @@ public class JDBCSequentialFileFactory implements SequentialFileFactory, ActiveM private final Executor executor; - private final Map fileLocks = new HashMap<>(); + private final Map fileLocks = new ConcurrentHashMap<>(); private JDBCSequentialFileFactoryDriver dbDriver; @@ -66,7 +66,6 @@ public JDBCSequentialFileFactory(final DataSource dataSource, } catch (SQLException e) { criticalErrorListener.onIOException(e, "Failed to start JDBC Driver", null); } - } public JDBCSequentialFileFactory(final String connectionUrl, From 29f39631dc226a64e89344602f27ae09ab3cdb66 Mon Sep 17 00:00:00 2001 From: Shailendra Kumar Singh Date: Mon, 30 Jul 2018 09:24:13 +0530 Subject: [PATCH 098/207] [ARTEMIS-1994]Include global-size-bytes in WARN message AMQ222038/39 for paging [ARTEMIS-1994]Minor changes --- .../artemis/core/paging/impl/PagingStoreImpl.java | 4 ++-- .../artemis/core/server/ActiveMQServerLogger.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java index 6a07ffcc96d..39687b0e6cd 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java @@ -739,7 +739,7 @@ public void addSize(final int size) { if (size > 0) { if (maxSize != -1 && newSize > maxSize || globalFull) { if (startPaging()) { - ActiveMQServerLogger.LOGGER.pageStoreStart(storeName, newSize, maxSize); + ActiveMQServerLogger.LOGGER.pageStoreStart(storeName, newSize, maxSize, pagingManager.getGlobalSize()); } } } @@ -785,7 +785,7 @@ public boolean page(Message message, if (!printedDropMessagesWarning) { printedDropMessagesWarning = true; - ActiveMQServerLogger.LOGGER.pageStoreDropMessages(storeName, sizeInBytes.get(), maxSize); + ActiveMQServerLogger.LOGGER.pageStoreDropMessages(storeName, sizeInBytes.get(), maxSize, pagingManager.getGlobalSize()); } if (message.isLargeMessage()) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java index 50e89a2f3ac..b10d652cd31 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java @@ -580,12 +580,12 @@ void slowConsumerDetected(String sessionID, void pageStoreStartIOError(@Cause Exception e); @LogMessage(level = Logger.Level.WARN) - @Message(id = 222038, value = "Starting paging on address ''{0}''; size is currently: {1} bytes; max-size-bytes: {2}", format = Message.Format.MESSAGE_FORMAT) - void pageStoreStart(SimpleString storeName, long addressSize, long maxSize); + @Message(id = 222038, value = "Starting paging on address ''{0}''; size is currently: {1} bytes; max-size-bytes: {2}; global-size-bytes: {3}", format = Message.Format.MESSAGE_FORMAT) + void pageStoreStart(SimpleString storeName, long addressSize, long maxSize, long globalMaxSize); @LogMessage(level = Logger.Level.WARN) - @Message(id = 222039, value = "Messages sent to address ''{0}'' are being dropped; size is currently: {1} bytes; max-size-bytes: {2}", format = Message.Format.MESSAGE_FORMAT) - void pageStoreDropMessages(SimpleString storeName, long addressSize, long maxSize); + @Message(id = 222039, value = "Messages sent to address ''{0}'' are being dropped; size is currently: {1} bytes; max-size-bytes: {2}; global-size-bytes: {3}", format = Message.Format.MESSAGE_FORMAT) + void pageStoreDropMessages(SimpleString storeName, long addressSize, long maxSize, long globalMaxSize); @LogMessage(level = Logger.Level.WARN) @Message(id = 222040, value = "Server is stopped", format = Message.Format.MESSAGE_FORMAT) From 53e1d601601204dc2aa587fcb3046d5c1d6d026d Mon Sep 17 00:00:00 2001 From: Howard Gao Date: Mon, 12 Mar 2018 10:33:09 +0800 Subject: [PATCH 099/207] ARTEMIS-1732 AMQP anonymous producer not blocked on max-disk-usage Anonymous senders (those created without a target address) are not blocked when max-disk-usage is reached. The cause is that when such a sender is created on the broker, the broker doesn't check the disk/memory usage and gives out the credit immediately. --- .../amqp/broker/AMQPSessionCallback.java | 27 +++--- .../artemis/core/paging/PagingManager.java | 55 +++++++++++- .../artemis/core/paging/PagingStore.java | 7 +- .../core/paging/impl/PagingManagerImpl.java | 44 ++++++++- .../core/paging/impl/PagingStoreImpl.java | 44 +-------- .../core/server/ActiveMQServerLogger.java | 13 +++ .../core/server/files/FileStoreMonitor.java | 9 +- .../server/files/FileStoreMonitorTest.java | 10 --- .../integration/amqp/GlobalDiskFullTest.java | 89 +++++++++++++++++++ .../tests/unit/util/FakePagingManager.java | 7 +- 10 files changed, 224 insertions(+), 81 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/GlobalDiskFullTest.java diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java index 6461bb20111..1f5ccbc30f5 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java @@ -31,6 +31,7 @@ import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.core.io.IOCallback; import org.apache.activemq.artemis.core.message.impl.CoreMessageObjectPools; +import org.apache.activemq.artemis.core.paging.PagingManager; import org.apache.activemq.artemis.core.paging.PagingStore; import org.apache.activemq.artemis.core.persistence.OperationContext; import org.apache.activemq.artemis.core.persistence.StorageManager; @@ -576,31 +577,25 @@ public void offerProducerCredit(final SimpleString address, final int threshold, final Receiver receiver) { try { - if (address == null) { + PagingManager pagingManager = manager.getServer().getPagingManager(); + Runnable creditRunnable = () -> { connection.lock(); try { - receiver.flow(credits); + if (receiver.getRemoteCredit() <= threshold) { + receiver.flow(credits); + } } finally { connection.unlock(); } connection.flush(); + }; + + if (address == null) { + pagingManager.checkMemory(creditRunnable); return; } final PagingStore store = manager.getServer().getPagingManager().getPageStore(address); - store.checkMemory(new Runnable() { - @Override - public void run() { - connection.lock(); - try { - if (receiver.getRemoteCredit() <= threshold) { - receiver.flow(credits); - } - } finally { - connection.unlock(); - } - connection.flush(); - } - }); + store.checkMemory(creditRunnable); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingManager.java index 4d472e182cf..c8eb2ec5eac 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingManager.java @@ -17,6 +17,9 @@ package org.apache.activemq.artemis.core.paging; import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.server.ActiveMQComponent; @@ -79,7 +82,7 @@ public interface PagingManager extends ActiveMQComponent, HierarchicalRepository void resumeCleanup(); - void addBlockedStore(PagingStore store); + void addBlockedStore(Blockable store); void injectMonitor(FileStoreMonitor monitor) throws Exception; @@ -111,4 +114,54 @@ default long getGlobalSize() { return 0; } + boolean checkMemory(Runnable runnable); + + // To be used when the memory is oversized either by local settings or global settings on blocking addresses + final class OverSizedRunnable implements Runnable { + + private final AtomicBoolean ran = new AtomicBoolean(false); + + private final Runnable runnable; + + public OverSizedRunnable(final Runnable runnable) { + this.runnable = runnable; + } + + @Override + public void run() { + if (ran.compareAndSet(false, true)) { + runnable.run(); + } + } + } + + interface Blockable { + /** + * It will return true if the destination is leaving blocking. + */ + boolean checkReleasedMemory(); + } + + final class MemoryFreedRunnablesExecutor implements Runnable { + + private final Queue onMemoryFreedRunnables = new ConcurrentLinkedQueue<>(); + + public void addRunnable(PagingManager.OverSizedRunnable runnable) { + onMemoryFreedRunnables.add(runnable); + } + + @Override + public void run() { + Runnable runnable; + + while ((runnable = onMemoryFreedRunnables.poll()) != null) { + runnable.run(); + } + } + + public boolean isEmpty() { + return onMemoryFreedRunnables.isEmpty(); + } + } + } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingStore.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingStore.java index 4dd8bf832a5..27e8c0fe583 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingStore.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingStore.java @@ -42,7 +42,7 @@ * * @see PagingManager */ -public interface PagingStore extends ActiveMQComponent, RefCountMessageListener { +public interface PagingStore extends ActiveMQComponent, RefCountMessageListener, PagingManager.Blockable { SimpleString getAddress(); @@ -131,11 +131,6 @@ public interface PagingStore extends ActiveMQComponent, RefCountMessageListener boolean isRejectingMessages(); - /** - * It will return true if the destination is leaving blocking. - */ - boolean checkReleasedMemory(); - /** * Write lock the PagingStore. * diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java index bca70cf170c..878f918760d 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java @@ -23,6 +23,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -56,7 +57,7 @@ public final class PagingManagerImpl implements PagingManager { */ private final ReentrantReadWriteLock syncLock = new ReentrantReadWriteLock(); - private final Set blockedStored = new ConcurrentHashSet<>(); + private final Set blockedStored = new ConcurrentHashSet<>(); private final ConcurrentMap stores = new ConcurrentHashMap<>(); @@ -78,6 +79,9 @@ public final class PagingManagerImpl implements PagingManager { private ActiveMQScheduledComponent scheduledComponent = null; + private final PagingManager.MemoryFreedRunnablesExecutor memoryFreedRunnablesExecutor = new PagingManager.MemoryFreedRunnablesExecutor(); + + private final Executor executor; // Static // -------------------------------------------------------------------------------------------------------------------------- @@ -102,6 +106,7 @@ public PagingManagerImpl(final PagingStoreFactory pagingSPI, this.addressSettingsRepository = addressSettingsRepository; addressSettingsRepository.registerListener(this); this.maxSize = maxSize; + executor = pagingStoreFactory.newExecutor(); } public PagingManagerImpl(final PagingStoreFactory pagingSPI, @@ -110,7 +115,7 @@ public PagingManagerImpl(final PagingStoreFactory pagingSPI, } @Override - public void addBlockedStore(PagingStore store) { + public void addBlockedStore(Blockable store) { blockedStored.add(store); } @@ -152,11 +157,42 @@ public long getGlobalSize() { return globalSizeBytes.get(); } + @Override + public boolean checkMemory(final Runnable runWhenAvailable) { + if (isGlobalFull()) { + OverSizedRunnable ourRunnable = new OverSizedRunnable(runWhenAvailable); + + memoryFreedRunnablesExecutor.addRunnable(ourRunnable); + addBlockedStore(() -> { + if (!isGlobalFull()) { + if (!memoryFreedRunnablesExecutor.isEmpty()) { + executor.execute(memoryFreedRunnablesExecutor); + ActiveMQServerLogger.LOGGER.unblockingGlobalMessageProduction(getGlobalSize()); + return true; + } + } + return false; + }); + + if (isDiskFull()) { + ActiveMQServerLogger.LOGGER.blockingGlobalDiskFull(); + } else { + ActiveMQServerLogger.LOGGER.blockingGlobalMessageProduction(getGlobalSize()); + } + + return true; + } + + runWhenAvailable.run(); + + return true; + } + protected void checkMemoryRelease() { if (!diskFull && (maxSize < 0 || globalSizeBytes.get() < maxSize) && !blockedStored.isEmpty()) { - Iterator storeIterator = blockedStored.iterator(); + Iterator storeIterator = blockedStored.iterator(); while (storeIterator.hasNext()) { - PagingStore store = storeIterator.next(); + Blockable store = storeIterator.next(); if (store.checkReleasedMemory()) { storeIterator.remove(); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java index 39687b0e6cd..74212ce9dea 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java @@ -23,9 +23,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Queue; import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -641,40 +639,7 @@ public Page depage() throws Exception { } - private final Queue onMemoryFreedRunnables = new ConcurrentLinkedQueue<>(); - - private class MemoryFreedRunnablesExecutor implements Runnable { - - @Override - public void run() { - Runnable runnable; - - while ((runnable = onMemoryFreedRunnables.poll()) != null) { - runnable.run(); - } - } - } - - private final Runnable memoryFreedRunnablesExecutor = new MemoryFreedRunnablesExecutor(); - - // To be used when the memory is oversized either by local settings or global settings on blocking addresses - private static final class OverSizedRunnable implements Runnable { - - private final AtomicBoolean ran = new AtomicBoolean(false); - - private final Runnable runnable; - - private OverSizedRunnable(final Runnable runnable) { - this.runnable = runnable; - } - - @Override - public void run() { - if (ran.compareAndSet(false, true)) { - runnable.run(); - } - } - } + private final PagingManager.MemoryFreedRunnablesExecutor memoryFreedRunnablesExecutor = new PagingManager.MemoryFreedRunnablesExecutor(); @Override public boolean checkMemory(final Runnable runWhenAvailable) { @@ -685,9 +650,9 @@ public boolean checkMemory(final Runnable runWhenAvailable) { } } else if (pagingManager.isDiskFull() || addressFullMessagePolicy == AddressFullMessagePolicy.BLOCK && (maxSize != -1 || usingGlobalMaxSize)) { if (pagingManager.isDiskFull() || maxSize > 0 && sizeInBytes.get() > maxSize || pagingManager.isGlobalFull()) { - OverSizedRunnable ourRunnable = new OverSizedRunnable(runWhenAvailable); + PagingManager.OverSizedRunnable ourRunnable = new PagingManager.OverSizedRunnable(runWhenAvailable); - onMemoryFreedRunnables.add(ourRunnable); + memoryFreedRunnablesExecutor.addRunnable(ourRunnable); // We check again to avoid a race condition where the size can come down just after the element // has been added, but the check to execute was done before the element was added @@ -710,7 +675,6 @@ public boolean checkMemory(final Runnable runWhenAvailable) { blocking.set(true); } } - return true; } } @@ -755,7 +719,7 @@ public boolean checkReleasedMemory() { public boolean checkReleaseMemory(boolean globalOversized, long newSize) { if (!globalOversized && (newSize <= maxSize || maxSize < 0)) { - if (!onMemoryFreedRunnables.isEmpty()) { + if (!memoryFreedRunnablesExecutor.isEmpty()) { executor.execute(memoryFreedRunnablesExecutor); if (blocking.get()) { ActiveMQServerLogger.LOGGER.unblockingMessageProduction(address, sizeInBytes.get(), maxSize); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java index b10d652cd31..96fffe594bf 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java @@ -1950,4 +1950,17 @@ void slowConsumerDetected(String sessionID, @LogMessage(level = Logger.Level.ERROR) @Message(id = 224095, value = "Error updating Consumer Count: {0}", format = Message.Format.MESSAGE_FORMAT) void consumerCountError(String reason); + + @LogMessage(level = Logger.Level.WARN) + @Message(id = 224096, value = "Disk Full! Blocking message production. Clients will report blocked.", format = Message.Format.MESSAGE_FORMAT) + void blockingGlobalDiskFull(); + + @LogMessage(level = Logger.Level.WARN) + @Message(id = 224097, value = "Blocking message production; size is currently: {0} bytes;", format = Message.Format.MESSAGE_FORMAT) + void blockingGlobalMessageProduction(long globalSize); + + @LogMessage(level = Logger.Level.INFO) + @Message(id = 224098, value = "Unblocking message production; size is currently: {0} bytes;", format = Message.Format.MESSAGE_FORMAT) + void unblockingGlobalMessageProduction(long globalSize); + } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitor.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitor.java index ad591172d43..957661c4b01 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitor.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitor.java @@ -150,11 +150,14 @@ private double getTotalSpace(FileStore store) throws IOException { public interface Callback { - void tick(FileStore store, double usage); + default void tick(FileStore store, double usage) { + } - void over(FileStore store, double usage); + default void over(FileStore store, double usage) { + } - void under(FileStore store, double usage); + default void under(FileStore store, double usage) { + } } } diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitorTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitorTest.java index b91d3de260a..e4f27c32b7b 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitorTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitorTest.java @@ -137,16 +137,6 @@ public void tick(FileStore store, double usage) { System.out.println("TickS::" + usage); latch.countDown(); } - - @Override - public void over(FileStore store, double usage) { - - } - - @Override - public void under(FileStore store, double usage) { - - } }); storeMonitor.start(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/GlobalDiskFullTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/GlobalDiskFullTest.java new file mode 100644 index 00000000000..d664013d4d0 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/GlobalDiskFullTest.java @@ -0,0 +1,89 @@ +/* + * 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.artemis.tests.integration.amqp; + +import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.core.server.files.FileStoreMonitor; +import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; +import org.apache.activemq.transport.amqp.client.AmqpClient; +import org.apache.activemq.transport.amqp.client.AmqpConnection; +import org.apache.activemq.transport.amqp.client.AmqpMessage; +import org.apache.activemq.transport.amqp.client.AmqpSender; +import org.apache.activemq.transport.amqp.client.AmqpSession; +import org.junit.Test; + +import java.net.URI; +import java.nio.file.FileStore; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class GlobalDiskFullTest extends AmqpClientTestSupport { + + @Override + protected void addConfiguration(ActiveMQServer server) { + Configuration serverConfig = server.getConfiguration(); + serverConfig.setDiskScanPeriod(100); + } + + @Test + public void testProducerOnDiskFull() throws Exception { + FileStoreMonitor monitor = ((ActiveMQServerImpl)server).getMonitor().setMaxUsage(0.0); + final CountDownLatch latch = new CountDownLatch(1); + monitor.addCallback(new FileStoreMonitor.Callback() { + @Override + public void over(FileStore store, double usage) { + latch.countDown(); + } + @Override + public void under(FileStore store, double usage) { + } + }); + latch.await(2, TimeUnit.SECONDS); + + AmqpClient client = createAmqpClient(new URI("tcp://localhost:" + AMQP_PORT)); + AmqpConnection connection = addConnection(client.connect()); + + try { + AmqpSession session = connection.createSession(); + AmqpSender sender = session.createSender(getQueueName()); + final AmqpMessage message = new AmqpMessage(); + byte[] payload = new byte[1000]; + message.setBytes(payload); + + sender.setSendTimeout(1000); + sender.send(message); + + org.apache.activemq.artemis.core.server.Queue queueView = getProxyToQueue(getQueueName()); + assertEquals("shouldn't receive any messages", 0, queueView.getMessageCount()); + + AmqpSender anonSender = session.createSender(); + final AmqpMessage message1 = new AmqpMessage(); + message1.setBytes(payload); + message1.setAddress(getQueueName()); + + anonSender.setSendTimeout(1000); + anonSender.send(message1); + + queueView = getProxyToQueue(getQueueName()); + assertEquals("shouldn't receive any messages", 0, queueView.getMessageCount()); + + } finally { + connection.close(); + } + } +} diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/util/FakePagingManager.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/util/FakePagingManager.java index d1012a60df8..34316557e5b 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/util/FakePagingManager.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/util/FakePagingManager.java @@ -30,7 +30,7 @@ public final class FakePagingManager implements PagingManager { @Override - public void addBlockedStore(PagingStore store) { + public void addBlockedStore(Blockable store) { } @@ -115,6 +115,11 @@ public boolean isDiskFull() { return false; } + @Override + public boolean checkMemory(Runnable runnable) { + return false; + } + /* * (non-Javadoc) * @see org.apache.activemq.artemis.core.paging.PagingManager#isGlobalFull() From 0e36e072bdf0c4636623aacbd15912857770c73f Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 31 Jul 2018 21:08:46 -0400 Subject: [PATCH 100/207] ARTEMIS-1732 I simplified some of the changes performed at the previous commit. Also I changed GlobalDiskFullTest to actually block the senders. I moved the Runnables from PagingManager into the Util as AtomicRunnable. --- .../utils/runnables/AtomicRunnable.java | 47 +++++++++++ .../runnables/AtomicRunnableWithDelegate.java | 32 ++++++++ .../amqp/broker/AMQPSessionCallback.java | 10 +-- .../artemis/core/paging/PagingManager.java | 59 ++------------ .../artemis/core/paging/PagingStore.java | 7 +- .../core/paging/impl/PagingManagerImpl.java | 81 ++++++++++--------- .../core/paging/impl/PagingStoreImpl.java | 23 ++++-- .../core/server/ActiveMQServerLogger.java | 13 --- .../core/server/files/FileStoreMonitor.java | 9 +-- .../server/files/FileStoreMonitorTest.java | 10 +++ .../integration/amqp/GlobalDiskFullTest.java | 75 +++++++++++++---- .../tests/unit/util/FakePagingManager.java | 12 +-- 12 files changed, 232 insertions(+), 146 deletions(-) create mode 100644 artemis-commons/src/main/java/org/apache/activemq/artemis/utils/runnables/AtomicRunnable.java create mode 100644 artemis-commons/src/main/java/org/apache/activemq/artemis/utils/runnables/AtomicRunnableWithDelegate.java diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/runnables/AtomicRunnable.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/runnables/AtomicRunnable.java new file mode 100644 index 00000000000..f1f53ce11fe --- /dev/null +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/runnables/AtomicRunnable.java @@ -0,0 +1,47 @@ +/* + * 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.artemis.utils.runnables; + +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +public abstract class AtomicRunnable implements Runnable { + + + public static Runnable checkAtomic(Runnable run) { + if (run instanceof AtomicRunnable) { + return run; + } else { + return new AtomicRunnableWithDelegate(run); + } + } + + private volatile int ran; + + private static final AtomicIntegerFieldUpdater RAN_UPDATE = + AtomicIntegerFieldUpdater.newUpdater(AtomicRunnable.class, "ran"); + + @Override + public void run() { + if (RAN_UPDATE.compareAndSet(this, 0, 1)) { + atomicRun(); + } + } + + public abstract void atomicRun(); +} + diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/runnables/AtomicRunnableWithDelegate.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/runnables/AtomicRunnableWithDelegate.java new file mode 100644 index 00000000000..d1583de1e1a --- /dev/null +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/runnables/AtomicRunnableWithDelegate.java @@ -0,0 +1,32 @@ +/* + * 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.artemis.utils.runnables; + +public class AtomicRunnableWithDelegate extends AtomicRunnable { + + private final Runnable runnable; + + public AtomicRunnableWithDelegate(Runnable runnable) { + this.runnable = runnable; + } + + @Override + public void atomicRun() { + runnable.run(); + } +} diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java index 1f5ccbc30f5..86c0687344b 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java @@ -581,9 +581,7 @@ public void offerProducerCredit(final SimpleString address, Runnable creditRunnable = () -> { connection.lock(); try { - if (receiver.getRemoteCredit() <= threshold) { - receiver.flow(credits); - } + receiver.flow(credits); } finally { connection.unlock(); } @@ -592,10 +590,10 @@ public void offerProducerCredit(final SimpleString address, if (address == null) { pagingManager.checkMemory(creditRunnable); - return; + } else { + final PagingStore store = manager.getServer().getPagingManager().getPageStore(address); + store.checkMemory(creditRunnable); } - final PagingStore store = manager.getServer().getPagingManager().getPageStore(address); - store.checkMemory(creditRunnable); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingManager.java index c8eb2ec5eac..5d8461e577d 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingManager.java @@ -17,9 +17,6 @@ package org.apache.activemq.artemis.core.paging; import java.util.Map; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.server.ActiveMQComponent; @@ -82,7 +79,7 @@ public interface PagingManager extends ActiveMQComponent, HierarchicalRepository void resumeCleanup(); - void addBlockedStore(Blockable store); + void addBlockedStore(PagingStore store); void injectMonitor(FileStoreMonitor monitor) throws Exception; @@ -114,54 +111,10 @@ default long getGlobalSize() { return 0; } - boolean checkMemory(Runnable runnable); - - // To be used when the memory is oversized either by local settings or global settings on blocking addresses - final class OverSizedRunnable implements Runnable { - - private final AtomicBoolean ran = new AtomicBoolean(false); - - private final Runnable runnable; - - public OverSizedRunnable(final Runnable runnable) { - this.runnable = runnable; - } - - @Override - public void run() { - if (ran.compareAndSet(false, true)) { - runnable.run(); - } - } - } - - interface Blockable { - /** - * It will return true if the destination is leaving blocking. - */ - boolean checkReleasedMemory(); - } - - final class MemoryFreedRunnablesExecutor implements Runnable { - - private final Queue onMemoryFreedRunnables = new ConcurrentLinkedQueue<>(); - - public void addRunnable(PagingManager.OverSizedRunnable runnable) { - onMemoryFreedRunnables.add(runnable); - } - - @Override - public void run() { - Runnable runnable; - - while ((runnable = onMemoryFreedRunnables.poll()) != null) { - runnable.run(); - } - } - - public boolean isEmpty() { - return onMemoryFreedRunnables.isEmpty(); - } - } + /** + * Use this when you have no refernce of an address. (anonymous AMQP Producers for example) + * @param runWhenAvailable + */ + void checkMemory(Runnable runWhenAvailable); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingStore.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingStore.java index 27e8c0fe583..4dd8bf832a5 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingStore.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/PagingStore.java @@ -42,7 +42,7 @@ * * @see PagingManager */ -public interface PagingStore extends ActiveMQComponent, RefCountMessageListener, PagingManager.Blockable { +public interface PagingStore extends ActiveMQComponent, RefCountMessageListener { SimpleString getAddress(); @@ -131,6 +131,11 @@ public interface PagingStore extends ActiveMQComponent, RefCountMessageListener, boolean isRejectingMessages(); + /** + * It will return true if the destination is leaving blocking. + */ + boolean checkReleasedMemory(); + /** * Write lock the PagingStore. * diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java index 878f918760d..88939840f17 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingManagerImpl.java @@ -20,8 +20,10 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @@ -39,6 +41,7 @@ import org.apache.activemq.artemis.core.settings.HierarchicalRepository; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet; +import org.apache.activemq.artemis.utils.runnables.AtomicRunnable; import org.jboss.logging.Logger; public final class PagingManagerImpl implements PagingManager { @@ -57,7 +60,7 @@ public final class PagingManagerImpl implements PagingManager { */ private final ReentrantReadWriteLock syncLock = new ReentrantReadWriteLock(); - private final Set blockedStored = new ConcurrentHashSet<>(); + private final Set blockedStored = new ConcurrentHashSet<>(); private final ConcurrentMap stores = new ConcurrentHashMap<>(); @@ -75,13 +78,14 @@ public final class PagingManagerImpl implements PagingManager { private volatile boolean diskFull = false; + private final Executor memoryExecutor; + + private final Queue memoryCallback = new ConcurrentLinkedQueue<>(); + private final ConcurrentMap transactions = new ConcurrentHashMap<>(); private ActiveMQScheduledComponent scheduledComponent = null; - private final PagingManager.MemoryFreedRunnablesExecutor memoryFreedRunnablesExecutor = new PagingManager.MemoryFreedRunnablesExecutor(); - - private final Executor executor; // Static // -------------------------------------------------------------------------------------------------------------------------- @@ -106,7 +110,7 @@ public PagingManagerImpl(final PagingStoreFactory pagingSPI, this.addressSettingsRepository = addressSettingsRepository; addressSettingsRepository.registerListener(this); this.maxSize = maxSize; - executor = pagingStoreFactory.newExecutor(); + this.memoryExecutor = pagingSPI.newExecutor(); } public PagingManagerImpl(final PagingStoreFactory pagingSPI, @@ -115,7 +119,7 @@ public PagingManagerImpl(final PagingStoreFactory pagingSPI, } @Override - public void addBlockedStore(Blockable store) { + public void addBlockedStore(PagingStore store) { blockedStored.add(store); } @@ -157,42 +161,18 @@ public long getGlobalSize() { return globalSizeBytes.get(); } - @Override - public boolean checkMemory(final Runnable runWhenAvailable) { - if (isGlobalFull()) { - OverSizedRunnable ourRunnable = new OverSizedRunnable(runWhenAvailable); - - memoryFreedRunnablesExecutor.addRunnable(ourRunnable); - addBlockedStore(() -> { - if (!isGlobalFull()) { - if (!memoryFreedRunnablesExecutor.isEmpty()) { - executor.execute(memoryFreedRunnablesExecutor); - ActiveMQServerLogger.LOGGER.unblockingGlobalMessageProduction(getGlobalSize()); - return true; - } - } - return false; - }); - - if (isDiskFull()) { - ActiveMQServerLogger.LOGGER.blockingGlobalDiskFull(); - } else { - ActiveMQServerLogger.LOGGER.blockingGlobalMessageProduction(getGlobalSize()); - } - - return true; - } - - runWhenAvailable.run(); - - return true; - } - protected void checkMemoryRelease() { if (!diskFull && (maxSize < 0 || globalSizeBytes.get() < maxSize) && !blockedStored.isEmpty()) { - Iterator storeIterator = blockedStored.iterator(); + if (!memoryCallback.isEmpty()) { + if (memoryExecutor != null) { + memoryExecutor.execute(this::memoryReleased); + } else { + memoryReleased(); + } + } + Iterator storeIterator = blockedStored.iterator(); while (storeIterator.hasNext()) { - Blockable store = storeIterator.next(); + PagingStore store = storeIterator.next(); if (store.checkReleasedMemory()) { storeIterator.remove(); } @@ -223,7 +203,7 @@ public void over(FileStore store, double usage) { @Override public void under(FileStore store, double usage) { - if (diskFull) { + if (diskFull || !blockedStored.isEmpty() || !memoryCallback.isEmpty()) { ActiveMQServerLogger.LOGGER.diskCapacityRestored(); diskFull = false; checkMemoryRelease(); @@ -241,6 +221,27 @@ public boolean isUsingGlobalSize() { return maxSize > 0; } + @Override + public void checkMemory(final Runnable runWhenAvailable) { + + if (isGlobalFull()) { + memoryCallback.add(AtomicRunnable.checkAtomic(runWhenAvailable)); + return; + } + runWhenAvailable.run(); + } + + + private void memoryReleased() { + Runnable runnable; + + while ((runnable = memoryCallback.poll()) != null) { + runnable.run(); + } + } + + + @Override public boolean isGlobalFull() { return diskFull || maxSize > 0 && globalSizeBytes.get() > maxSize; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java index 74212ce9dea..5f0d3c8f946 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java @@ -23,7 +23,9 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Queue; import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -60,6 +62,7 @@ import org.apache.activemq.artemis.core.transaction.TransactionPropertyIndexes; import org.apache.activemq.artemis.utils.FutureLatch; import org.apache.activemq.artemis.utils.actors.ArtemisExecutor; +import org.apache.activemq.artemis.utils.runnables.AtomicRunnable; import org.jboss.logging.Logger; /** @@ -639,7 +642,16 @@ public Page depage() throws Exception { } - private final PagingManager.MemoryFreedRunnablesExecutor memoryFreedRunnablesExecutor = new PagingManager.MemoryFreedRunnablesExecutor(); + private final Queue onMemoryFreedRunnables = new ConcurrentLinkedQueue<>(); + + private void memoryReleased() { + Runnable runnable; + + while ((runnable = onMemoryFreedRunnables.poll()) != null) { + runnable.run(); + } + } + @Override public boolean checkMemory(final Runnable runWhenAvailable) { @@ -650,9 +662,8 @@ public boolean checkMemory(final Runnable runWhenAvailable) { } } else if (pagingManager.isDiskFull() || addressFullMessagePolicy == AddressFullMessagePolicy.BLOCK && (maxSize != -1 || usingGlobalMaxSize)) { if (pagingManager.isDiskFull() || maxSize > 0 && sizeInBytes.get() > maxSize || pagingManager.isGlobalFull()) { - PagingManager.OverSizedRunnable ourRunnable = new PagingManager.OverSizedRunnable(runWhenAvailable); - memoryFreedRunnablesExecutor.addRunnable(ourRunnable); + onMemoryFreedRunnables.add(AtomicRunnable.checkAtomic(runWhenAvailable)); // We check again to avoid a race condition where the size can come down just after the element // has been added, but the check to execute was done before the element was added @@ -660,7 +671,7 @@ public boolean checkMemory(final Runnable runWhenAvailable) { // MUCH better performance in a highly concurrent environment if (!pagingManager.isGlobalFull() && (sizeInBytes.get() <= maxSize || maxSize < 0)) { // run it now - ourRunnable.run(); + runWhenAvailable.run(); } else { if (usingGlobalMaxSize || pagingManager.isDiskFull()) { pagingManager.addBlockedStore(this); @@ -719,8 +730,8 @@ public boolean checkReleasedMemory() { public boolean checkReleaseMemory(boolean globalOversized, long newSize) { if (!globalOversized && (newSize <= maxSize || maxSize < 0)) { - if (!memoryFreedRunnablesExecutor.isEmpty()) { - executor.execute(memoryFreedRunnablesExecutor); + if (!onMemoryFreedRunnables.isEmpty()) { + executor.execute(this::memoryReleased); if (blocking.get()) { ActiveMQServerLogger.LOGGER.unblockingMessageProduction(address, sizeInBytes.get(), maxSize); blocking.set(false); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java index 96fffe594bf..b10d652cd31 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java @@ -1950,17 +1950,4 @@ void slowConsumerDetected(String sessionID, @LogMessage(level = Logger.Level.ERROR) @Message(id = 224095, value = "Error updating Consumer Count: {0}", format = Message.Format.MESSAGE_FORMAT) void consumerCountError(String reason); - - @LogMessage(level = Logger.Level.WARN) - @Message(id = 224096, value = "Disk Full! Blocking message production. Clients will report blocked.", format = Message.Format.MESSAGE_FORMAT) - void blockingGlobalDiskFull(); - - @LogMessage(level = Logger.Level.WARN) - @Message(id = 224097, value = "Blocking message production; size is currently: {0} bytes;", format = Message.Format.MESSAGE_FORMAT) - void blockingGlobalMessageProduction(long globalSize); - - @LogMessage(level = Logger.Level.INFO) - @Message(id = 224098, value = "Unblocking message production; size is currently: {0} bytes;", format = Message.Format.MESSAGE_FORMAT) - void unblockingGlobalMessageProduction(long globalSize); - } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitor.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitor.java index 957661c4b01..ad591172d43 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitor.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitor.java @@ -150,14 +150,11 @@ private double getTotalSpace(FileStore store) throws IOException { public interface Callback { - default void tick(FileStore store, double usage) { - } + void tick(FileStore store, double usage); - default void over(FileStore store, double usage) { - } + void over(FileStore store, double usage); - default void under(FileStore store, double usage) { - } + void under(FileStore store, double usage); } } diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitorTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitorTest.java index e4f27c32b7b..b91d3de260a 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitorTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/files/FileStoreMonitorTest.java @@ -137,6 +137,16 @@ public void tick(FileStore store, double usage) { System.out.println("TickS::" + usage); latch.countDown(); } + + @Override + public void over(FileStore store, double usage) { + + } + + @Override + public void under(FileStore store, double usage) { + + } }); storeMonitor.start(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/GlobalDiskFullTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/GlobalDiskFullTest.java index d664013d4d0..0e0f86d42f6 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/GlobalDiskFullTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/GlobalDiskFullTest.java @@ -25,6 +25,7 @@ import org.apache.activemq.transport.amqp.client.AmqpMessage; import org.apache.activemq.transport.amqp.client.AmqpSender; import org.apache.activemq.transport.amqp.client.AmqpSession; +import org.junit.Assert; import org.junit.Test; import java.net.URI; @@ -45,6 +46,11 @@ public void testProducerOnDiskFull() throws Exception { FileStoreMonitor monitor = ((ActiveMQServerImpl)server).getMonitor().setMaxUsage(0.0); final CountDownLatch latch = new CountDownLatch(1); monitor.addCallback(new FileStoreMonitor.Callback() { + + @Override + public void tick(FileStore store, double usage) { + } + @Override public void over(FileStore store, double usage) { latch.countDown(); @@ -53,7 +59,8 @@ public void over(FileStore store, double usage) { public void under(FileStore store, double usage) { } }); - latch.await(2, TimeUnit.SECONDS); + + Assert.assertTrue(latch.await(1, TimeUnit.MINUTES)); AmqpClient client = createAmqpClient(new URI("tcp://localhost:" + AMQP_PORT)); AmqpConnection connection = addConnection(client.connect()); @@ -61,27 +68,65 @@ public void under(FileStore store, double usage) { try { AmqpSession session = connection.createSession(); AmqpSender sender = session.createSender(getQueueName()); - final AmqpMessage message = new AmqpMessage(); byte[] payload = new byte[1000]; - message.setBytes(payload); - - sender.setSendTimeout(1000); - sender.send(message); - org.apache.activemq.artemis.core.server.Queue queueView = getProxyToQueue(getQueueName()); - assertEquals("shouldn't receive any messages", 0, queueView.getMessageCount()); AmqpSender anonSender = session.createSender(); - final AmqpMessage message1 = new AmqpMessage(); - message1.setBytes(payload); - message1.setAddress(getQueueName()); - anonSender.setSendTimeout(1000); - anonSender.send(message1); + CountDownLatch sentWithName = new CountDownLatch(1); + CountDownLatch sentAnon = new CountDownLatch(1); + + Thread threadWithName = new Thread() { + @Override + public void run() { + + try { + final AmqpMessage message = new AmqpMessage(); + message.setBytes(payload); + sender.setSendTimeout(-1); + sender.send(message); + } catch (Exception e) { + e.printStackTrace(); + } finally { + sentWithName.countDown(); + } + } + }; + + threadWithName.start(); + + + Thread threadWithAnon = new Thread() { + @Override + public void run() { + try { + final AmqpMessage message = new AmqpMessage(); + message.setBytes(payload); + anonSender.setSendTimeout(-1); + message.setAddress(getQueueName()); + anonSender.send(message); + } catch (Exception e) { + e.printStackTrace(); + } finally { + sentAnon.countDown(); + } + } + }; + + threadWithAnon.start(); + + Assert.assertFalse("Thread sender should be blocked", sentWithName.await(500, TimeUnit.MILLISECONDS)); + Assert.assertFalse("Thread sender anonymous should be blocked", sentAnon.await(500, TimeUnit.MILLISECONDS)); + + monitor.setMaxUsage(100.0); - queueView = getProxyToQueue(getQueueName()); - assertEquals("shouldn't receive any messages", 0, queueView.getMessageCount()); + Assert.assertTrue("Thread sender should be released", sentWithName.await(30, TimeUnit.SECONDS)); + Assert.assertTrue("Thread sender anonymous should be released", sentAnon.await(30, TimeUnit.SECONDS)); + threadWithName.join(TimeUnit.SECONDS.toMillis(30)); + threadWithAnon.join(TimeUnit.SECONDS.toMillis(30)); + Assert.assertFalse(threadWithName.isAlive()); + Assert.assertFalse(threadWithAnon.isAlive()); } finally { connection.close(); } diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/util/FakePagingManager.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/util/FakePagingManager.java index 34316557e5b..94a9d79c793 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/util/FakePagingManager.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/util/FakePagingManager.java @@ -30,7 +30,12 @@ public final class FakePagingManager implements PagingManager { @Override - public void addBlockedStore(Blockable store) { + public void addBlockedStore(PagingStore store) { + + } + + @Override + public void checkMemory(Runnable runWhenAvailable) { } @@ -115,11 +120,6 @@ public boolean isDiskFull() { return false; } - @Override - public boolean checkMemory(Runnable runnable) { - return false; - } - /* * (non-Javadoc) * @see org.apache.activemq.artemis.core.paging.PagingManager#isGlobalFull() From e290071ceddd3ac66f23ab537e3e400ddd25bdf1 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 1 Aug 2018 22:14:15 -0400 Subject: [PATCH 101/207] ARTEMIS-1732 Fixing AMQPSenderTest --- .../artemis/protocol/amqp/broker/AMQPSessionCallback.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java index 86c0687344b..f940c5aa59a 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java @@ -581,7 +581,9 @@ public void offerProducerCredit(final SimpleString address, Runnable creditRunnable = () -> { connection.lock(); try { - receiver.flow(credits); + if (receiver.getRemoteCredit() <= threshold) { + receiver.flow(credits); + } } finally { connection.unlock(); } From 59520b9018d4a44fffa0bf05c49970d50ed69ab5 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 1 Aug 2018 22:04:31 -0400 Subject: [PATCH 102/207] ARTEMIS-856 Fixing QueueCommandTest --- .../artemis/core/management/impl/ActiveMQServerControlImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java index ee190c62d42..b0b3cc8f4e1 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java @@ -807,7 +807,7 @@ public String createQueue(String address, boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception { AddressSettings addressSettings = server.getAddressSettingsRepository().getMatch(address == null ? name : address); - return createQueue(address, routingType, name, filterStr, durable, addressSettings.getDefaultMaxConsumers(), addressSettings.isDefaultPurgeOnNoConsumers(), addressSettings.isDefaultExclusiveQueue(), addressSettings.isDefaultLastValueQueue(), addressSettings.getDefaultConsumersBeforeDispatch(), addressSettings.getDefaultDelayBeforeDispatch(), addressSettings.isAutoCreateAddresses()); + return createQueue(address, routingType, name, filterStr, durable, maxConsumers, purgeOnNoConsumers, addressSettings.isDefaultExclusiveQueue(), addressSettings.isDefaultLastValueQueue(), addressSettings.getDefaultConsumersBeforeDispatch(), addressSettings.getDefaultDelayBeforeDispatch(), autoCreateAddress); } @Override From ec742cb8899bd1b7d7cb28b7e64c87f75681f6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Thu, 2 Aug 2018 10:51:27 +0100 Subject: [PATCH 103/207] ARTEMIS-856 Fixing MessageRedistributionTest --- .../artemis/core/server/impl/QueueImpl.java | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java index 265621741da..09ff21051dd 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java @@ -21,6 +21,7 @@ import java.math.BigDecimal; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -1082,6 +1083,11 @@ private boolean checkConsumerDirectDeliver() { supports = false; } } + if (redistributor != null) { + if (!redistributor.consumer.supportsDirectDelivery()) { + supports = false; + } + } return supports; } @@ -2158,10 +2164,10 @@ public synchronized int changeReferencesPriority(final Filter filter, final byte @Override public synchronized void resetAllIterators() { for (ConsumerHolder holder : this.consumerList) { - if (holder.iter != null) { - holder.iter.close(); - } - holder.iter = null; + holder.resetIterator(); + } + if (redistributor != null) { + redistributor.resetIterator(); } } @@ -2369,7 +2375,7 @@ private void deliver() { MessageReference ref; Consumer handledconsumer = null; - + SimpleString groupID; synchronized (this) { // Need to do these checks inside the synchronized @@ -2436,7 +2442,7 @@ private void deliver() { // If a group id is set, then this overrides the consumer chosen round-robin - SimpleString groupID = extractGroupID(ref); + groupID = extractGroupID(ref); if (groupID != null) { groupConsumer = groups.get(groupID); @@ -2484,10 +2490,10 @@ private void deliver() { } } - if (pos == endPos) { + if (pos == endPos || (redistributor != null || groupConsumer != null || exclusive)) { // Round robin'd all - if (noDelivery == size) { + if (noDelivery == size && redistributor == null || ((redistributor != null || groupConsumer != null || exclusive) && noDelivery > 0)) { if (handledconsumer != null) { // this shouldn't really happen, // however I'm keeping this as an assertion case future developers ever change the logic here on this class @@ -3040,7 +3046,7 @@ private boolean deliverDirect(final MessageReference ref) { return true; } - if (pos == startPos) { + if (pos == startPos || redistributor != null || groupConsumer != null || exclusive) { // Tried them all break; } @@ -3122,7 +3128,11 @@ private List cloneConsumersList() { List consumerListClone; synchronized (this) { - consumerListClone = new ArrayList<>(consumerList); + if (redistributor == null) { + consumerListClone = new ArrayList<>(consumerList); + } else { + consumerListClone = Collections.singletonList(redistributor); + } } return consumerListClone; } @@ -3286,6 +3296,13 @@ private static class ConsumerHolder { LinkedListIterator iter; + private void resetIterator() { + if (iter != null) { + iter.close(); + } + iter = null; + } + } private class DelayedAddRedistributor implements Runnable { From ddd554f43fedbabf490b67ce6c2b85a48a2ac954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Thu, 2 Aug 2018 13:53:22 +0100 Subject: [PATCH 104/207] ARTEMIS-856 Fixing ExclusiveTest --- .../activemq/artemis/core/server/impl/ActiveMQServerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f01e0c7639a..a71a862b5ab 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 @@ -1766,7 +1766,7 @@ public void createSharedQueue(final SimpleString address, boolean exclusive, boolean lastValue) throws Exception { AddressSettings as = getAddressSettingsRepository().getMatch(address == null ? name.toString() : address.toString()); - createSharedQueue(address, routingType, name, filterString, user, durable, as.getDefaultMaxConsumers(), as.isDefaultPurgeOnNoConsumers(), as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), as.getDefaultConsumersBeforeDispatch(), as.getDefaultDelayBeforeDispatch()); + createSharedQueue(address, routingType, name, filterString, user, durable, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, as.getDefaultConsumersBeforeDispatch(), as.getDefaultDelayBeforeDispatch()); } @Override From de465179e5b0eecc87e6865250e771ce1f603e1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Thu, 2 Aug 2018 14:04:59 +0100 Subject: [PATCH 105/207] ARTEMIS-856 Fixing MessageRedistributionTest Cleaner code. --- .../artemis/core/server/impl/QueueImpl.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java index 09ff21051dd..1f0c7afd203 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java @@ -2375,7 +2375,7 @@ private void deliver() { MessageReference ref; Consumer handledconsumer = null; - SimpleString groupID; + synchronized (this) { // Need to do these checks inside the synchronized @@ -2442,7 +2442,7 @@ private void deliver() { // If a group id is set, then this overrides the consumer chosen round-robin - groupID = extractGroupID(ref); + SimpleString groupID = extractGroupID(ref); if (groupID != null) { groupConsumer = groups.get(groupID); @@ -2490,10 +2490,15 @@ private void deliver() { } } - if (pos == endPos || (redistributor != null || groupConsumer != null || exclusive)) { + if (redistributor != null || groupConsumer != null || exclusive) { + if (noDelivery > 0) { + break; + } + noDelivery = 0; + } else if (pos == endPos) { // Round robin'd all - if (noDelivery == size && redistributor == null || ((redistributor != null || groupConsumer != null || exclusive) && noDelivery > 0)) { + if (noDelivery == size) { if (handledconsumer != null) { // this shouldn't really happen, // however I'm keeping this as an assertion case future developers ever change the logic here on this class From 523e86ce9bb909514d4b3d4bc983ada2fd4ef877 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Wed, 1 Aug 2018 10:37:06 -0500 Subject: [PATCH 106/207] ARTEMIS-2006 Remove setDeadLetterAddress & setExpiryAddress from docs --- docs/user-manual/en/management.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/user-manual/en/management.md b/docs/user-manual/en/management.md index e27794cd165..82bf14251cd 100644 --- a/docs/user-manual/en/management.md +++ b/docs/user-manual/en/management.md @@ -153,14 +153,12 @@ a given property.) Messages can be expired from a queue by using the `expireMessages()` method. If an expiry address is defined, messages will be sent to it, otherwise they - are discarded. The queue's expiry address can be set with the - `setExpiryAddress()` method. + are discarded. Messages can also be sent to a dead letter address with the `sendMessagesToDeadLetterAddress()` method. It returns the number of messages which are sent to the dead letter address. If a dead letter address is not - defined, message are removed from the queue and discarded. The queue's dead - letter address can be set with the `setDeadLetterAddress()` method. + defined, message are removed from the queue and discarded. Messages can also be moved from a queue to another queue by using the `moveMessages()` method. From 77989c3763c477de4bc45d2d0a8824201735235a Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Thu, 2 Aug 2018 10:57:14 -0400 Subject: [PATCH 107/207] NO-JIRA avoiding a NPE if the server is stopped --- .../core/server/management/impl/ManagementServiceImpl.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImpl.java index 48b96c8263a..ad888d00773 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImpl.java @@ -550,7 +550,10 @@ public void start() throws Exception { @Override public void activated() { try { - messagingServer.addAddressInfo(new AddressInfo(managementNotificationAddress, RoutingType.MULTICAST)); + ActiveMQServer usedServer = messagingServer; + if (usedServer != null) { + usedServer.addAddressInfo(new AddressInfo(managementNotificationAddress, RoutingType.MULTICAST)); + } } catch (Exception e) { ActiveMQServerLogger.LOGGER.unableToCreateManagementNotificationAddress(managementNotificationAddress, e); } From 825081cfc095d52541ebc5631f031059618d871d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Fri, 3 Aug 2018 08:52:47 +0100 Subject: [PATCH 108/207] ARTEMIS-856 Fixing ScaleDownTest Don't increment the pos if redistributor. causes pos to be > size thus index out of bounds when getting the consumer on next loop. --- .../apache/activemq/artemis/core/server/impl/QueueImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java index 1f0c7afd203..0d4c6865b5b 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java @@ -2516,7 +2516,7 @@ private void deliver() { // Only move onto the next position if the consumer on the current position was used. // When using group we don't need to load balance to the next position - if (!exclusive && groupConsumer == null) { + if (redistributor == null && !exclusive && groupConsumer == null) { pos++; } @@ -3029,7 +3029,7 @@ private boolean deliverDirect(final MessageReference ref) { } // Only move onto the next position if the consumer on the current position was used. - if (!exclusive && groupConsumer == null) { + if (redistributor == null && !exclusive && groupConsumer == null) { pos++; } From a92324be35affdece16e1c18bf0aef1e63dd4d14 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Fri, 3 Aug 2018 13:09:37 -0400 Subject: [PATCH 109/207] NO-JIRA Fixing MultiThreadAsynchronousFileTest on limited servers This test was initializing a libaio of 21K, that would fail on limited servers. This is decreasing maxIO so it would requires less resources to run it. --- .../unit/core/asyncio/MultiThreadAsynchronousFileTest.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/asyncio/MultiThreadAsynchronousFileTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/asyncio/MultiThreadAsynchronousFileTest.java index e9f51f9b2ab..95e94cfe7d0 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/asyncio/MultiThreadAsynchronousFileTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/asyncio/MultiThreadAsynchronousFileTest.java @@ -94,7 +94,7 @@ public void testMultipleSynchronousWrites() throws Throwable { private void executeTest(final boolean sync) throws Throwable { MultiThreadAsynchronousFileTest.debug(sync ? "Sync test:" : "Async test"); - AIOSequentialFileFactory factory = new AIOSequentialFileFactory(getTestDirfile(), 21000); + AIOSequentialFileFactory factory = new AIOSequentialFileFactory(getTestDirfile(), 100); factory.start(); factory.disableBufferReuse(); @@ -144,10 +144,6 @@ private void executeTest(final boolean sync) throws Throwable { } - private int getNewPosition() { - return position.addAndGet(1); - } - class ThreadProducer extends Thread { Throwable failed = null; From cb99b4a6663e631fae8048dad8ffca427fceda15 Mon Sep 17 00:00:00 2001 From: Jiri Danek Date: Sun, 22 Jul 2018 10:57:14 +0200 Subject: [PATCH 110/207] NO-JIRA fix flaky test HeuristicXATest.testRecoverHeuristicCommitWithRestart The below error is prevented by adding Wait.assertEquals, where Assert.assertEquals was used previously. Timeout is set to small increments, since we rarely need to wait more than 100 ms for the condition to become true. java.lang.AssertionError: expected:<1> but was:<0> at org.junit.Assert.fail(Assert.java:88) at org.junit.Assert.failNotEquals(Assert.java:743) at org.junit.Assert.assertEquals(Assert.java:118) at org.junit.Assert.assertEquals(Assert.java:555) at org.junit.Assert.assertEquals(Assert.java:542) at org.apache.activemq.artemis.tests.integration.client.HeuristicXATest.doRecoverHeuristicCompletedTxWithRestart(HeuristicXATest.java:306) at org.apache.activemq.artemis.tests.integration.client.HeuristicXATest.testRecoverHeuristicCommitWithRestart(HeuristicXATest.java:251) --- .../integration/client/HeuristicXATest.java | 59 +++++++------------ 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/HeuristicXATest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/HeuristicXATest.java index 8f089c3e263..e32c0142212 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/HeuristicXATest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/HeuristicXATest.java @@ -35,6 +35,7 @@ import org.apache.activemq.artemis.core.transaction.impl.XidImpl; import org.apache.activemq.artemis.tests.integration.management.ManagementControlHelper; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.tests.util.Wait; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -138,19 +139,7 @@ private void internalTest(final boolean isCommit) throws Exception { } if (isCommit) { - Assert.assertEquals(1, getMessageCount(((Queue) server.getPostOffice().getBinding(ADDRESS).getBindable()))); - - session = sf.createSession(false, false, false); - - session.start(); - ClientConsumer consumer = session.createConsumer(ADDRESS); - msg = consumer.receive(1000); - Assert.assertNotNull(msg); - msg.acknowledge(); - Assert.assertEquals(body, msg.getBodyBuffer().readString()); - - session.commit(); - session.close(); + assertMessageInQueueThenReceiveAndCheckContent(server, sf); } Assert.assertEquals(0, getMessageCount(((Queue) server.getPostOffice().getBinding(ADDRESS).getBindable()))); @@ -213,19 +202,7 @@ private void doHeuristicCompletionWithRestart(final boolean isCommit) throws Exc Assert.assertEquals(0, preparedTransactions.length); if (isCommit) { - Assert.assertEquals(1, getMessageCount(((Queue) server.getPostOffice().getBinding(ADDRESS).getBindable()))); - - session = sf.createSession(false, false, false); - - session.start(); - ClientConsumer consumer = session.createConsumer(ADDRESS); - msg = consumer.receive(1000); - Assert.assertNotNull(msg); - msg.acknowledge(); - Assert.assertEquals(body, msg.getBodyBuffer().readString()); - - session.commit(); - session.close(); + assertMessageInQueueThenReceiveAndCheckContent(server, sf); } Assert.assertEquals(0, getMessageCount(((Queue) server.getPostOffice().getBinding(ADDRESS).getBindable()))); @@ -303,19 +280,7 @@ private void doRecoverHeuristicCompletedTxWithRestart(final boolean heuristicCom Assert.assertEquals(0, preparedTransactions.length); if (heuristicCommit) { - Assert.assertEquals(1, getMessageCount(((Queue) server.getPostOffice().getBinding(ADDRESS).getBindable()))); - - session = sf.createSession(false, false, false); - - session.start(); - ClientConsumer consumer = session.createConsumer(ADDRESS); - msg = consumer.receive(1000); - Assert.assertNotNull(msg); - msg.acknowledge(); - Assert.assertEquals(body, msg.getBodyBuffer().readString()); - - session.commit(); - session.close(); + assertMessageInQueueThenReceiveAndCheckContent(server, sf); } Assert.assertEquals(0, getMessageCount(((Queue) server.getPostOffice().getBinding(ADDRESS).getBindable()))); @@ -345,6 +310,22 @@ private void doRecoverHeuristicCompletedTxWithRestart(final boolean heuristicCom session.close(); } + private void assertMessageInQueueThenReceiveAndCheckContent(ActiveMQServer server, ClientSessionFactory sf) throws Exception { + Wait.assertEquals(1, () -> getMessageCount(((Queue) server.getPostOffice().getBinding(ADDRESS).getBindable())), 5 * 1000, 100); + + ClientSession session = sf.createSession(false, false, false); + + session.start(); + ClientConsumer consumer = session.createConsumer(ADDRESS); + ClientMessage msg = consumer.receive(1000); + Assert.assertNotNull(msg); + msg.acknowledge(); + Assert.assertEquals(body, msg.getBodyBuffer().readString()); + + session.commit(); + session.close(); + } + @Test public void testForgetHeuristicCommitAndRestart() throws Exception { doForgetHeuristicCompletedTxAndRestart(true); From e629ac4538988cbb957a9a2599b2430d4266c943 Mon Sep 17 00:00:00 2001 From: "Christopher L. Shannon (cshannon)" Date: Tue, 31 Jul 2018 13:01:32 -0400 Subject: [PATCH 111/207] ARTEMIS-2003 - Add bridge metrics This commit adds support for tracking metrics for bridges for both normal bridges and bridges that are part of a cluster. The two statistics added in this commit are messages pending acknowledgement and messages acknowledged but more can be added later. --- .../api/core/management/BridgeControl.java | 29 ++++++++ .../management/ClusterConnectionControl.java | 48 +++++++++++++ .../management/impl/BridgeControlImpl.java | 35 +++++++++- .../impl/ClusterConnectionControlImpl.java | 48 ++++++++++++- .../artemis/core/server/cluster/Bridge.java | 3 + .../server/cluster/ClusterConnection.java | 16 +++++ .../core/server/cluster/impl/BridgeImpl.java | 22 +++++- .../server/cluster/impl/BridgeMetrics.java | 69 +++++++++++++++++++ .../cluster/impl/ClusterConnectionImpl.java | 20 ++++++ .../impl/ClusterConnectionMetrics.java | 64 +++++++++++++++++ .../cluster/bridge/BridgeTest.java | 6 ++ .../cluster/distribution/ClusterTestBase.java | 18 +++++ .../distribution/OneWayChainClusterTest.java | 9 +++ .../OnewayTwoNodeClusterTest.java | 18 +++++ .../management/BridgeControlTest.java | 6 ++ .../BridgeControlUsingCoreTest.java | 8 +++ .../ClusterConnectionControlTest.java | 14 +++- ...ClusterConnectionControlUsingCoreTest.java | 22 ++++++ 18 files changed, 445 insertions(+), 10 deletions(-) create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeMetrics.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/ClusterConnectionMetrics.java diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/BridgeControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/BridgeControl.java index 3bf4554daf0..9dd7dc87c1e 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/BridgeControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/BridgeControl.java @@ -106,4 +106,33 @@ public interface BridgeControl extends ActiveMQComponentControl { */ @Attribute(desc = "whether this bridge is using high availability") boolean isHA(); + + /** + * The messagesPendingAcknowledgement counter is incremented when the bridge is has forwarded a message but + * is waiting acknowledgement from the other broker. This is a cumulative total and the number of outstanding + * pending messages can be computed by subtracting messagesAcknowledged from messagesPendingAcknowledgement. + * + */ + @Attribute(desc = "The messagesPendingAcknowledgement counter is incremented when the bridge is has forwarded a message but is waiting acknowledgement from the remote broker.") + long getMessagesPendingAcknowledgement(); + + /** + * The messagesAcknowledged counter is the number of messages actually received by the remote broker. + * This is a cumulative total and the number of outstanding pending messages can be computed by subtracting + * messagesAcknowledged from messagesPendingAcknowledgement. + * + */ + @Attribute(desc = "The messagesAcknowledged counter is the number of messages actually received by the remote broker.") + long getMessagesAcknowledged(); + + /** + * The bridge metrics for this bridge + * + * The messagesPendingAcknowledgement counter is incremented when the bridge is has forwarded a message but is waiting acknowledgement from the other broker. + * The messagesAcknowledged counter is the number of messages actually received by the remote broker. + * + */ + @Attribute(desc = "The metrics for this bridge. The messagesPendingAcknowledgement counter is incremented when the bridge is has forwarded a message but is waiting acknowledgement from the remote broker. The messagesAcknowledged counter is the number of messages actually received by the remote broker.") + Map getMetrics(); + } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ClusterConnectionControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ClusterConnectionControl.java index 194afade191..39f0825759a 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ClusterConnectionControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ClusterConnectionControl.java @@ -96,4 +96,52 @@ public interface ClusterConnectionControl extends ActiveMQComponentControl { */ @Attribute(desc = "map of the nodes connected to this cluster connection (keys are node IDs, values are the addresses used to connect to the nodes)") Map getNodes() throws Exception; + + /** + * The messagesPendingAcknowledgement counter is incremented when any bridge in the cluster connection has + * forwarded a message and is waiting acknowledgement from the other broker. (aggregate over all bridges) + * + * This is a cumulative total and the number of outstanding pending messages for the cluster connection + * can be computed by subtracting messagesAcknowledged from messagesPendingAcknowledgement. + * + */ + @Attribute(desc = "The messagesPendingAcknowledgement counter is incremented when any bridge in the cluster connection has forwarded a message and is waiting acknowledgement from the other broker. (aggregate over all bridges)") + long getMessagesPendingAcknowledgement(); + + /** + * The messagesAcknowledged counter is the number of messages actually received by a remote broker for all + * bridges in this cluster connection + * + * This is a cumulative total and the number of outstanding pending messages for the cluster connection + * can be computed by subtracting messagesAcknowledged from messagesPendingAcknowledgement. + * + */ + @Attribute(desc = "The messagesAcknowledged counter is the number of messages actually received by a remote broker for all bridges in this cluster connection") + long getMessagesAcknowledged(); + + /** + * The current metrics for this cluster connection (aggregate over all bridges to other nodes) + * + * The messagesPendingAcknowledgement counter is incremented when any bridge in the cluster connection has + * forwarded a message and is waiting acknowledgement from the other broker. + * + * The messagesAcknowledged counter is the number of messages actually received by a remote broker for all + * bridges in this cluster connection + * + * @return + */ + @Attribute(desc = "The metrics for this cluster connection. The messagesPendingAcknowledgement counter is incremented when any bridge in the cluster connection has forwarded a message and is waiting acknowledgement from the other broker. The messagesAcknowledged counter is the number of messages actually received by a remote broker for all bridges in this cluster connection") + Map getMetrics(); + + /** + * The bridge metrics for the given node in the cluster connection + * + * The messagesPendingAcknowledgement counter is incremented when the bridge is has forwarded a message but is waiting acknowledgement from the other broker. + * The messagesAcknowledged counter is the number of messages actually received by the remote broker for this bridge. + * + * @throws Exception + */ + @Attribute(desc = "The metrics for the bridge by nodeId. The messagesPendingAcknowledgement counter is incremented when the bridge is has forwarded a message but is waiting acknowledgement from the other broker. The messagesAcknowledged counter is the number of messages actually received by the remote broker for this bridge.") + Map getBridgeMetrics(String nodeId) throws Exception; + } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/BridgeControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/BridgeControlImpl.java index 6e0e055b865..d0e55238e3c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/BridgeControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/BridgeControlImpl.java @@ -16,11 +16,12 @@ */ package org.apache.activemq.artemis.core.management.impl; -import javax.management.MBeanAttributeInfo; -import javax.management.MBeanOperationInfo; import java.util.List; import java.util.Map; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanOperationInfo; + import org.apache.activemq.artemis.api.core.JsonUtil; import org.apache.activemq.artemis.api.core.management.BridgeControl; import org.apache.activemq.artemis.core.config.BridgeConfiguration; @@ -228,6 +229,36 @@ protected MBeanAttributeInfo[] fillMBeanAttributeInfo() { return MBeanInfoHelper.getMBeanAttributesInfo(BridgeControl.class); } + @Override + public long getMessagesPendingAcknowledgement() { + clearIO(); + try { + return bridge.getMetrics().getMessagesPendingAcknowledgement(); + } finally { + blockOnIO(); + } + } + + @Override + public long getMessagesAcknowledged() { + clearIO(); + try { + return bridge.getMetrics().getMessagesAcknowledged(); + } finally { + blockOnIO(); + } + } + + @Override + public Map getMetrics() { + clearIO(); + try { + return bridge.getMetrics().convertToMap(); + } finally { + blockOnIO(); + } + } + // Public -------------------------------------------------------- // Package protected --------------------------------------------- diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ClusterConnectionControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ClusterConnectionControlImpl.java index 9186dbbf132..24c7dccf6ea 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ClusterConnectionControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ClusterConnectionControlImpl.java @@ -16,16 +16,18 @@ */ package org.apache.activemq.artemis.core.management.impl; -import javax.management.MBeanAttributeInfo; -import javax.management.MBeanOperationInfo; import java.util.List; import java.util.Map; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanOperationInfo; + import org.apache.activemq.artemis.api.core.JsonUtil; import org.apache.activemq.artemis.api.core.management.ClusterConnectionControl; import org.apache.activemq.artemis.core.config.ClusterConnectionConfiguration; import org.apache.activemq.artemis.core.persistence.StorageManager; import org.apache.activemq.artemis.core.server.cluster.ClusterConnection; +import org.apache.activemq.artemis.core.server.cluster.impl.BridgeMetrics; public class ClusterConnectionControlImpl extends AbstractControl implements ClusterConnectionControl { @@ -223,6 +225,48 @@ protected MBeanAttributeInfo[] fillMBeanAttributeInfo() { return MBeanInfoHelper.getMBeanAttributesInfo(ClusterConnectionControl.class); } + @Override + public long getMessagesPendingAcknowledgement() { + clearIO(); + try { + return clusterConnection.getMetrics().getMessagesPendingAcknowledgement(); + } finally { + blockOnIO(); + } + } + + @Override + public long getMessagesAcknowledged() { + clearIO(); + try { + return clusterConnection.getMetrics().getMessagesAcknowledged(); + } finally { + blockOnIO(); + } + } + + @Override + public Map getMetrics() { + clearIO(); + try { + return clusterConnection.getMetrics().convertToMap(); + } finally { + blockOnIO(); + } + } + + @Override + public Map getBridgeMetrics(String nodeId) { + clearIO(); + try { + final BridgeMetrics bridgeMetrics = clusterConnection.getBridgeMetrics(nodeId); + return bridgeMetrics != null ? bridgeMetrics.convertToMap() : null; + } finally { + blockOnIO(); + } + + } + // Public -------------------------------------------------------- // Package protected --------------------------------------------- diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/Bridge.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/Bridge.java index 7e8cacb41b5..28fbc7c2941 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/Bridge.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/Bridge.java @@ -20,6 +20,7 @@ import org.apache.activemq.artemis.core.server.ActiveMQComponent; import org.apache.activemq.artemis.core.server.Consumer; import org.apache.activemq.artemis.core.server.Queue; +import org.apache.activemq.artemis.core.server.cluster.impl.BridgeMetrics; import org.apache.activemq.artemis.core.server.management.NotificationService; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; @@ -52,4 +53,6 @@ public interface Bridge extends Consumer, ActiveMQComponent { void disconnect(); boolean isConnected(); + + BridgeMetrics getMetrics(); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ClusterConnection.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ClusterConnection.java index 9392ed59e2f..6171476188d 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ClusterConnection.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ClusterConnection.java @@ -25,6 +25,8 @@ import org.apache.activemq.artemis.core.client.impl.Topology; import org.apache.activemq.artemis.core.server.ActiveMQComponent; import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.core.server.cluster.impl.BridgeMetrics; +import org.apache.activemq.artemis.core.server.cluster.impl.ClusterConnectionMetrics; public interface ClusterConnection extends ActiveMQComponent, ClusterTopologyListener { @@ -80,4 +82,18 @@ void nodeAnnounced(long eventUID, long getCallTimeout(); + /** + * The metric for this cluster connection + * + * @return + */ + ClusterConnectionMetrics getMetrics(); + + /** + * Returns the BridgeMetrics for the bridge to the given node if exists + * + * @param nodeId + * @return + */ + BridgeMetrics getBridgeMetrics(String nodeId); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java index 48f59f49070..c811b6356cb 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java @@ -55,10 +55,10 @@ import org.apache.activemq.artemis.core.server.MessageReference; import org.apache.activemq.artemis.core.server.Queue; import org.apache.activemq.artemis.core.server.cluster.Bridge; -import org.apache.activemq.artemis.core.server.transformer.Transformer; import org.apache.activemq.artemis.core.server.impl.QueueImpl; import org.apache.activemq.artemis.core.server.management.Notification; import org.apache.activemq.artemis.core.server.management.NotificationService; +import org.apache.activemq.artemis.core.server.transformer.Transformer; import org.apache.activemq.artemis.spi.core.protocol.EmbedMessageUtil; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.spi.core.remoting.ReadyListener; @@ -159,6 +159,8 @@ public class BridgeImpl implements Bridge, SessionFailureListener, SendAcknowled private ActiveMQServer server; + private final BridgeMetrics metrics = new BridgeMetrics(); + public BridgeImpl(final ServerLocatorInternal serverLocator, final int initialConnectAttempts, final int reconnectAttempts, @@ -518,6 +520,7 @@ public void sendAcknowledged(final Message message) { } ref.getQueue().acknowledge(ref); pendingAcks.countDown(); + metrics.incrementMessagesAcknowledged(); } else { if (logger.isTraceEnabled()) { logger.trace("BridgeImpl::sendAcknowledged bridge " + this + " could not find reference for message " + message); @@ -611,13 +614,21 @@ public HandleStatus handle(final MessageReference ref) throws Exception { pendingAcks.countUp(); try { + final HandleStatus status; if (message.isLargeMessage()) { deliveringLargeMessage = true; deliverLargeMessage(dest, ref, (LargeServerMessage) message); - return HandleStatus.HANDLED; + status = HandleStatus.HANDLED; } else { - return deliverStandardMessage(dest, ref, message); + status = deliverStandardMessage(dest, ref, message); + } + + //Only increment messages pending acknowledgement if handled by bridge + if (status == HandleStatus.HANDLED) { + metrics.incrementMessagesPendingAcknowledgement(); } + + return status; } catch (Exception e) { // If an exception happened, we must count down immediately pendingAcks.countDown(); @@ -770,6 +781,11 @@ public TopologyMember getTargetNodeFromTopology() { return this.targetNode; } + @Override + public BridgeMetrics getMetrics() { + return this.metrics; + } + @Override public String toString() { return this.getClass().getSimpleName() + "@" + diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeMetrics.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeMetrics.java new file mode 100644 index 00000000000..d8689b6abcd --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeMetrics.java @@ -0,0 +1,69 @@ +/* + * 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.artemis.core.server.cluster.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; + +public class BridgeMetrics { + + public static final String MESSAGES_PENDING_ACKNOWLEDGEMENT_KEY = "messagesPendingAcknowledgement"; + public static final String MESSAGES_ACKNOWLEDGED_KEY = "messagesAcknowledged"; + + private static final AtomicLongFieldUpdater MESSAGES_PENDING_ACKNOWLEDGEMENT_UPDATER = + AtomicLongFieldUpdater.newUpdater(BridgeMetrics.class, MESSAGES_PENDING_ACKNOWLEDGEMENT_KEY); + + private static final AtomicLongFieldUpdater MESSAGES_ACKNOWLEDGED_UPDATER = + AtomicLongFieldUpdater.newUpdater(BridgeMetrics.class, MESSAGES_ACKNOWLEDGED_KEY); + + private volatile long messagesPendingAcknowledgement; + private volatile long messagesAcknowledged; + + public void incrementMessagesPendingAcknowledgement() { + MESSAGES_PENDING_ACKNOWLEDGEMENT_UPDATER.incrementAndGet(this); + } + + public void incrementMessagesAcknowledged() { + MESSAGES_ACKNOWLEDGED_UPDATER.incrementAndGet(this); + } + + /** + * @return the messagesPendingAcknowledgement + */ + public long getMessagesPendingAcknowledgement() { + return messagesPendingAcknowledgement; + } + + /** + * @return the messagesAcknowledged + */ + public long getMessagesAcknowledged() { + return messagesAcknowledged; + } + + /** + * @return New map containing the Bridge metrics + */ + public Map convertToMap() { + final Map metrics = new HashMap<>(); + metrics.put(MESSAGES_PENDING_ACKNOWLEDGEMENT_KEY, messagesPendingAcknowledgement); + metrics.put(MESSAGES_ACKNOWLEDGED_KEY, messagesAcknowledged); + + return metrics; + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/ClusterConnectionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/ClusterConnectionImpl.java index 70923be6698..849575848e9 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/ClusterConnectionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/ClusterConnectionImpl.java @@ -749,6 +749,26 @@ public synchronized void informClusterOfBackup() { topology.updateAsLive(nodeID, localMember); } + + @Override + public ClusterConnectionMetrics getMetrics() { + long messagesPendingAcknowledgement = 0; + long messagesAcknowledged = 0; + for (MessageFlowRecord record : records.values()) { + final BridgeMetrics metrics = record.getBridge() != null ? record.getBridge().getMetrics() : null; + messagesPendingAcknowledgement += metrics != null ? metrics.getMessagesPendingAcknowledgement() : 0; + messagesAcknowledged += metrics != null ? metrics.getMessagesAcknowledged() : 0; + } + + return new ClusterConnectionMetrics(messagesPendingAcknowledgement, messagesAcknowledged); + } + + @Override + public BridgeMetrics getBridgeMetrics(String nodeId) { + final MessageFlowRecord record = records.get(nodeId); + return record != null && record.getBridge() != null ? record.getBridge().getMetrics() : null; + } + private void createNewRecord(final long eventUID, final String targetNodeID, final TransportConfiguration connector, diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/ClusterConnectionMetrics.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/ClusterConnectionMetrics.java new file mode 100644 index 00000000000..5b9e0842247 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/ClusterConnectionMetrics.java @@ -0,0 +1,64 @@ +/* + * 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.artemis.core.server.cluster.impl; + +import java.util.HashMap; +import java.util.Map; + +public class ClusterConnectionMetrics { + + public static final String MESSAGES_PENDING_ACKNOWLEDGEMENT_KEY = "messagesPendingAcknowledgement"; + public static final String MESSAGES_ACKNOWLEDGED_KEY = "messagesAcknowledged"; + + private final long messagesPendingAcknowledgement; + private final long messagesAcknowledged; + + /** + * @param messagesPendingAcknowledgement + * @param messagesAcknowledged + */ + public ClusterConnectionMetrics(long messagesPendingAcknowledgement, long messagesAcknowledged) { + super(); + this.messagesPendingAcknowledgement = messagesPendingAcknowledgement; + this.messagesAcknowledged = messagesAcknowledged; + } + + /** + * @return the messagesPendingAcknowledgement + */ + public long getMessagesPendingAcknowledgement() { + return messagesPendingAcknowledgement; + } + + /** + * @return the messagesAcknowledged + */ + public long getMessagesAcknowledged() { + return messagesAcknowledged; + } + + /** + * @return New map containing the Cluster Connection metrics + */ + public Map convertToMap() { + final Map metrics = new HashMap<>(); + metrics.put(MESSAGES_PENDING_ACKNOWLEDGEMENT_KEY, messagesPendingAcknowledgement); + metrics.put(MESSAGES_ACKNOWLEDGED_KEY, messagesAcknowledged); + + return metrics; + } +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/bridge/BridgeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/bridge/BridgeTest.java index 2d6add7f727..73116f84ef3 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/bridge/BridgeTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/bridge/BridgeTest.java @@ -73,6 +73,7 @@ import org.apache.activemq.artemis.core.server.transformer.AddHeadersTransformer; import org.apache.activemq.artemis.core.server.transformer.Transformer; import org.apache.activemq.artemis.core.server.cluster.impl.BridgeImpl; +import org.apache.activemq.artemis.core.server.cluster.impl.BridgeMetrics; import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl; import org.apache.activemq.artemis.core.server.impl.ServiceRegistryImpl; import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy; @@ -502,6 +503,11 @@ public void internaltestSimpleBridge(final boolean largeMessage, final boolean u sf1.close(); + assertEquals(1, server0.getClusterManager().getBridges().size()); + BridgeMetrics bridgeMetrics = server0.getClusterManager().getBridges().get("bridge1").getMetrics(); + assertEquals(10, bridgeMetrics.getMessagesPendingAcknowledgement()); + assertEquals(10, bridgeMetrics.getMessagesAcknowledged()); + closeFields(); if (server0.getConfiguration().isPersistenceEnabled()) { assertEquals(0, loadQueues(server0).size()); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java index 89d5175dbbb..6e7f9b338ab 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/ClusterTestBase.java @@ -75,7 +75,9 @@ import org.apache.activemq.artemis.core.server.cluster.ClusterConnection; import org.apache.activemq.artemis.core.server.cluster.ClusterManager; import org.apache.activemq.artemis.core.server.cluster.RemoteQueueBinding; +import org.apache.activemq.artemis.core.server.cluster.impl.BridgeMetrics; import org.apache.activemq.artemis.core.server.cluster.impl.ClusterConnectionImpl; +import org.apache.activemq.artemis.core.server.cluster.impl.ClusterConnectionMetrics; import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; import org.apache.activemq.artemis.core.server.cluster.qourum.SharedNothingBackupQuorum; import org.apache.activemq.artemis.core.server.group.GroupingHandler; @@ -1286,6 +1288,22 @@ protected void verifyReceiveRoundRobinInSomeOrderNoAck(final int numMessages, verifyReceiveRoundRobinInSomeOrder(false, numMessages, consumerIDs); } + protected void verifyClusterMetrics(final int node, final String clusterName, final long expectedMessagesPendingAcknowledgement, + final long expectedMessagesAcknowledged) { + final ClusterConnection clusterConnection = servers[node].getClusterManager().getClusterConnection(clusterName); + final ClusterConnectionMetrics clusterMetrics = clusterConnection.getMetrics(); + assertEquals(expectedMessagesPendingAcknowledgement, clusterMetrics.getMessagesPendingAcknowledgement()); + assertEquals(expectedMessagesAcknowledged, clusterMetrics.getMessagesAcknowledged()); + } + + protected void verifyBridgeMetrics(final int node, final String clusterName, final String bridgeNodeId, + final long expectedMessagesPendingAcknowledgement, final long expectedMessagesAcknowledged) { + final ClusterConnection clusterConnection = servers[node].getClusterManager().getClusterConnection(clusterName); + final BridgeMetrics bridgeMetrics = clusterConnection.getBridgeMetrics(bridgeNodeId); + assertEquals(expectedMessagesPendingAcknowledgement, bridgeMetrics.getMessagesPendingAcknowledgement()); + assertEquals(expectedMessagesAcknowledged, bridgeMetrics.getMessagesAcknowledged()); + } + protected int[] getReceivedOrder(final int consumerID) throws Exception { return getReceivedOrder(consumerID, false); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/OneWayChainClusterTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/OneWayChainClusterTest.java index e82f465c606..c360b74388c 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/OneWayChainClusterTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/OneWayChainClusterTest.java @@ -75,6 +75,15 @@ public void testBasicRoundRobin() throws Exception { send(0, "queues.testaddress", 10, false, null); verifyReceiveRoundRobin(10, 0, 1); verifyNotReceive(0, 1); + + //half of the messages should be sent over bridges to the last broker in the chain + //as there is a consumer on that last broker + verifyClusterMetrics(0, "cluster0-1", 5, 5); + verifyClusterMetrics(1, "cluster1-2", 5, 5); + verifyClusterMetrics(2, "cluster2-3", 5, 5); + verifyClusterMetrics(3, "cluster3-4", 5, 5); + verifyClusterMetrics(4, "cluster4-X", 0, 0); + } @Test diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/OnewayTwoNodeClusterTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/OnewayTwoNodeClusterTest.java index f722d6702e8..c2c6982c0d1 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/OnewayTwoNodeClusterTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/OnewayTwoNodeClusterTest.java @@ -198,6 +198,15 @@ public void testBasicLocalReceive() throws Exception { addConsumer(1, 0, "queue0", null); verifyNotReceive(1); + + //Should be 0 as no messages were sent to the second broker + verifyClusterMetrics(0, "cluster1", 0, 0); + + //Should be 0 as no messages were sent to the first broker + verifyClusterMetrics(1, "clusterX", 0, 0); + + //0 messages were sent across the bridge to the second broker + verifyBridgeMetrics(0, "cluster1", servers[1].getClusterManager().getNodeId(), 0, 0); } @Test @@ -224,6 +233,15 @@ public void testBasicRoundRobin() throws Exception { send(0, "queues.testaddress", 10, false, null); verifyReceiveRoundRobin(10, 0, 1); verifyNotReceive(0, 1); + + //half of the messages should be sent over bridge, other half was consumed by local consumer + verifyClusterMetrics(0, "cluster1", 5, 5); + + //Should be 0 as no messages were sent to the first broker + verifyClusterMetrics(1, "clusterX", 0, 0); + + //5 messages were sent across the bridge to the second broker + verifyBridgeMetrics(0, "cluster1", servers[1].getClusterManager().getNodeId(), 5, 5); } @Test diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/BridgeControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/BridgeControlTest.java index df63043501d..c0dd06da928 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/BridgeControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/BridgeControlTest.java @@ -36,6 +36,7 @@ import org.apache.activemq.artemis.core.remoting.impl.invm.TransportConstants; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; +import org.apache.activemq.artemis.core.server.cluster.impl.BridgeMetrics; import org.apache.activemq.artemis.core.server.management.Notification; import org.apache.activemq.artemis.tests.integration.SimpleNotificationService; import org.apache.activemq.artemis.utils.RandomUtil; @@ -64,6 +65,11 @@ public void testAttributes() throws Exception { Assert.assertEquals(bridgeConfig.getRetryIntervalMultiplier(), bridgeControl.getRetryIntervalMultiplier(), 0.000001); Assert.assertEquals(bridgeConfig.getReconnectAttempts(), bridgeControl.getReconnectAttempts()); Assert.assertEquals(bridgeConfig.isUseDuplicateDetection(), bridgeControl.isUseDuplicateDetection()); + Map bridgeMetrics = bridgeControl.getMetrics(); + Assert.assertEquals(0L, bridgeControl.getMessagesPendingAcknowledgement()); + Assert.assertEquals(0L, bridgeControl.getMessagesAcknowledged()); + Assert.assertEquals(0L, bridgeMetrics.get(BridgeMetrics.MESSAGES_PENDING_ACKNOWLEDGEMENT_KEY)); + Assert.assertEquals(0L, bridgeMetrics.get(BridgeMetrics.MESSAGES_ACKNOWLEDGED_KEY)); String[] connectorPairData = bridgeControl.getStaticConnectors(); Assert.assertEquals(bridgeConfig.getStaticConnectors().get(0), connectorPairData[0]); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/BridgeControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/BridgeControlUsingCoreTest.java index e0ff4c84b4a..bd8b51dcfb2 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/BridgeControlUsingCoreTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/BridgeControlUsingCoreTest.java @@ -33,6 +33,7 @@ import org.apache.activemq.artemis.core.remoting.impl.invm.TransportConstants; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; +import org.apache.activemq.artemis.core.server.cluster.impl.BridgeMetrics; import org.apache.activemq.artemis.utils.RandomUtil; import org.junit.Assert; import org.junit.Before; @@ -69,6 +70,13 @@ public void testAttributes() throws Exception { Assert.assertEquals(bridgeConfig.getReconnectAttempts(), proxy.retrieveAttributeValue("reconnectAttempts", Integer.class)); Assert.assertEquals(bridgeConfig.isUseDuplicateDetection(), proxy.retrieveAttributeValue("useDuplicateDetection", Boolean.class)); + @SuppressWarnings("unchecked") + Map bridgeMetrics = (Map) proxy.retrieveAttributeValue("metrics", Map.class); + Assert.assertEquals(0L, proxy.retrieveAttributeValue("messagesPendingAcknowledgement", Long.class)); + Assert.assertEquals(0L, proxy.retrieveAttributeValue("messagesAcknowledged", Long.class)); + Assert.assertEquals(0L, bridgeMetrics.get(BridgeMetrics.MESSAGES_PENDING_ACKNOWLEDGEMENT_KEY)); + Assert.assertEquals(0L, bridgeMetrics.get(BridgeMetrics.MESSAGES_ACKNOWLEDGED_KEY)); + Object[] data = (Object[]) proxy.retrieveAttributeValue("staticConnectors"); Assert.assertEquals(bridgeConfig.getStaticConnectors().get(0), data[0]); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ClusterConnectionControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ClusterConnectionControlTest.java index f414c155831..1fc2c5ecdde 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ClusterConnectionControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ClusterConnectionControlTest.java @@ -16,14 +16,15 @@ */ package org.apache.activemq.artemis.tests.integration.management; -import javax.json.JsonArray; -import javax.management.MBeanServer; -import javax.management.MBeanServerFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.json.JsonArray; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; + import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration; import org.apache.activemq.artemis.api.core.JsonUtil; import org.apache.activemq.artemis.api.core.SimpleString; @@ -40,6 +41,7 @@ import org.apache.activemq.artemis.core.remoting.impl.invm.TransportConstants; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; +import org.apache.activemq.artemis.core.server.cluster.impl.ClusterConnectionMetrics; import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; import org.apache.activemq.artemis.core.server.management.Notification; import org.apache.activemq.artemis.tests.integration.SimpleNotificationService; @@ -82,6 +84,12 @@ public void testAttributes1() throws Exception { Assert.assertEquals(clusterConnectionConfig1.isDuplicateDetection(), clusterConnectionControl.isDuplicateDetection()); Assert.assertEquals(clusterConnectionConfig1.getMessageLoadBalancingType().getType(), clusterConnectionControl.getMessageLoadBalancingType()); Assert.assertEquals(clusterConnectionConfig1.getMaxHops(), clusterConnectionControl.getMaxHops()); + Assert.assertEquals(0L, clusterConnectionControl.getMessagesPendingAcknowledgement()); + Assert.assertEquals(0L, clusterConnectionControl.getMessagesAcknowledged()); + Map clusterMetrics = clusterConnectionControl.getMetrics(); + Assert.assertEquals(0L, clusterMetrics.get(ClusterConnectionMetrics.MESSAGES_PENDING_ACKNOWLEDGEMENT_KEY)); + Assert.assertEquals(0L, clusterMetrics.get(ClusterConnectionMetrics.MESSAGES_ACKNOWLEDGED_KEY)); + Assert.assertNull(clusterConnectionControl.getBridgeMetrics("bad")); Object[] connectors = clusterConnectionControl.getStaticConnectors(); Assert.assertEquals(1, connectors.length); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ClusterConnectionControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ClusterConnectionControlUsingCoreTest.java index 2756b1e704a..0c76a6449ca 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ClusterConnectionControlUsingCoreTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ClusterConnectionControlUsingCoreTest.java @@ -98,6 +98,28 @@ public String getNodeID() { return (String) proxy.retrieveAttributeValue("nodeID"); } + @Override + public long getMessagesPendingAcknowledgement() { + return (Long) proxy.retrieveAttributeValue("messagesPendingAcknowledgement", Long.class); + } + + @Override + public long getMessagesAcknowledged() { + return (Long) proxy.retrieveAttributeValue("messagesAcknowledged", Long.class); + } + + @SuppressWarnings("unchecked") + @Override + public Map getMetrics() { + return (Map) proxy.retrieveAttributeValue("metrics", Map.class); + } + + @SuppressWarnings("unchecked") + @Override + public Map getBridgeMetrics(String nodeId) throws Exception { + return (Map) proxy.invokeOperation("getBridgeMetrics", nodeId); + } + @Override public boolean isStarted() { return (Boolean) proxy.retrieveAttributeValue("started", Boolean.class); From 3c5b050dff611ca1bb315a3424de2707053c12c8 Mon Sep 17 00:00:00 2001 From: Shailendra Kumar Singh Date: Thu, 2 Aug 2018 09:10:46 +0530 Subject: [PATCH 112/207] [ARTEMIS-2008]Add a CLI command to purge queue --- .../apache/activemq/artemis/cli/Artemis.java | 5 +- .../cli/commands/queue/PurgeQueue.java | 72 +++++++++++++++++++ .../integration/cli/QueueCommandTest.java | 29 ++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/PurgeQueue.java diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java index 556bcd89b45..657055e6a67 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java @@ -46,6 +46,7 @@ import org.apache.activemq.artemis.cli.commands.queue.CreateQueue; import org.apache.activemq.artemis.cli.commands.queue.DeleteQueue; import org.apache.activemq.artemis.cli.commands.queue.HelpQueue; +import org.apache.activemq.artemis.cli.commands.queue.PurgeQueue; import org.apache.activemq.artemis.cli.commands.queue.UpdateQueue; import org.apache.activemq.artemis.cli.commands.tools.HelpData; import org.apache.activemq.artemis.cli.commands.tools.PrintData; @@ -153,8 +154,8 @@ private static Cli.CliBuilder builder(File artemisInstance) { String instance = artemisInstance != null ? artemisInstance.getAbsolutePath() : System.getProperty("artemis.instance"); Cli.CliBuilder builder = Cli.builder("artemis").withDescription("ActiveMQ Artemis Command Line").withCommand(HelpAction.class).withCommand(Producer.class).withCommand(Consumer.class).withCommand(Browse.class).withCommand(Mask.class).withDefaultCommand(HelpAction.class); - builder.withGroup("queue").withDescription("Queue tools group (create|delete|update|stat) (example ./artemis queue create)"). - withDefaultCommand(HelpQueue.class).withCommands(CreateQueue.class, DeleteQueue.class, UpdateQueue.class, StatQueue.class); + builder.withGroup("queue").withDescription("Queue tools group (create|delete|update|stat|purge) (example ./artemis queue create)"). + withDefaultCommand(HelpQueue.class).withCommands(CreateQueue.class, DeleteQueue.class, UpdateQueue.class, StatQueue.class, PurgeQueue.class); builder.withGroup("address").withDescription("Address tools group (create|delete|update|show) (example ./artemis address create)"). withDefaultCommand(HelpAddress.class).withCommands(CreateAddress.class, DeleteAddress.class, UpdateAddress.class, ShowAddress.class); diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/PurgeQueue.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/PurgeQueue.java new file mode 100644 index 00000000000..f5d3d7edcf0 --- /dev/null +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/queue/PurgeQueue.java @@ -0,0 +1,72 @@ +/* + * 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.artemis.cli.commands.queue; + +import io.airlift.airline.Command; +import io.airlift.airline.Option; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.management.ManagementHelper; +import org.apache.activemq.artemis.api.core.management.ResourceNames; +import org.apache.activemq.artemis.cli.commands.ActionContext; +import org.apache.activemq.artemis.cli.commands.AbstractAction; + +@Command(name = "purge", description = "purge a queue") +public class PurgeQueue extends AbstractAction { + + @Option(name = "--name", description = "queue name") + String name; + + @Override + public Object execute(ActionContext context) throws Exception { + super.execute(context); + purgeQueue(context); + return null; + } + + private void purgeQueue(final ActionContext context) throws Exception { + performCoreManagement(new ManagementCallback() { + @Override + public void setUpInvocation(ClientMessage message) throws Exception { + ManagementHelper.putOperationInvocation(message, ResourceNames.QUEUE + getName(), "removeAllMessages"); + } + + @Override + public void requestSuccessful(ClientMessage reply) throws Exception { + context.out.println("Queue " + getName() + " purged successfully."); + } + + @Override + public void requestFailed(ClientMessage reply) throws Exception { + String errMsg = (String) ManagementHelper.getResult(reply, String.class); + context.err.println("Failed to purge queue " + getName() + ". Reason: " + errMsg); + } + }); + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + if (name == null) { + name = input("--name", "Please provide the destination name:", ""); + } + + return name; + } +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cli/QueueCommandTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cli/QueueCommandTest.java index b4e8fb61eb1..c581e69a716 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cli/QueueCommandTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cli/QueueCommandTest.java @@ -27,6 +27,7 @@ import org.apache.activemq.artemis.cli.commands.ActionContext; import org.apache.activemq.artemis.cli.commands.queue.CreateQueue; import org.apache.activemq.artemis.cli.commands.queue.DeleteQueue; +import org.apache.activemq.artemis.cli.commands.queue.PurgeQueue; import org.apache.activemq.artemis.cli.commands.queue.UpdateQueue; import org.apache.activemq.artemis.core.server.Queue; import org.apache.activemq.artemis.core.server.QueueQueryResult; @@ -339,6 +340,34 @@ public void testUpdateCoreQueueDoesNotExist() throws Exception { assertFalse(server.queueQuery(queueName).isExists()); } + @Test + public void testPurgeQueue() throws Exception { + SimpleString queueName = new SimpleString("purgeQueue"); + + CreateQueue command = new CreateQueue(); + command.setName(queueName.toString()); + command.setAutoCreateAddress(true); + command.setAnycast(true); + command.execute(new ActionContext()); + + PurgeQueue purge = new PurgeQueue(); + purge.setName(queueName.toString()); + purge.execute(new ActionContext(System.in, new PrintStream(output), new PrintStream(error))); + checkExecutionPassed(purge); + } + + @Test + public void testPurgeQueueDoesNotExist() throws Exception { + SimpleString queueName = new SimpleString("purgeQueue"); + + PurgeQueue purge = new PurgeQueue(); + purge.setName(queueName.toString()); + purge.execute(new ActionContext(System.in, new PrintStream(output), new PrintStream(error))); + checkExecutionFailure(purge, "AMQ119067: Cannot find resource with name queue." + queueName); + + assertFalse(server.queueQuery(queueName).isExists()); + } + private void checkExecutionPassed(AbstractAction command) throws Exception { String fullMessage = output.toString(); System.out.println("output: " + fullMessage); From fd9aad202bbac331a14d6ff58c0546c5d027aa06 Mon Sep 17 00:00:00 2001 From: Jeff Mesnil Date: Mon, 6 Aug 2018 14:11:40 +0200 Subject: [PATCH 113/207] =?UTF-8?q?[ARTEMIS-1947]=C2=A0Session=20metadata?= =?UTF-8?q?=20in=20session=20management=20operation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fill the session's metadata in JSON properties instead of serializing it as an opaque String. JIRA: https://issues.apache.org/jira/browse/ARTEMIS-1947 --- .../management/impl/ActiveMQServerControlImpl.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java index b0b3cc8f4e1..f708c1dfb6b 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java @@ -1923,10 +1923,12 @@ public void buildSessionJSON(JsonArrayBuilder array, ServerSession sess) { obj.add("principal", sess.getValidatedUser()); } - String metadata = sess.getMetaData() == null ? null : sess.getMetaData().toString(); - if (metadata != null) { - // remove leading and trailing curly brackets - obj.add("metadata", metadata.substring(1, metadata.length() - 1)); + if (sess.getMetaData() != null) { + final JsonObjectBuilder metadata = JsonLoader.createObjectBuilder(); + for (Entry entry : sess.getMetaData().entrySet()) { + metadata.add(entry.getKey(), entry.getValue()); + } + obj.add("metadata", metadata); } array.add(obj); From b710df7844c36633566d3f93f2707a378e4c5af8 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Fri, 3 Aug 2018 18:19:05 -0400 Subject: [PATCH 114/207] ARTEMIS-2011 Fixing incompatibility of AddressSettings encode between versions To fix this I added a retry on AddressSettings using code that's closer to the original version --- .../core/settings/impl/AddressSettings.java | 17 ++++-- tests/compatibility-tests/pom.xml | 52 +++++++++++++++++++ .../tests/compatibility/GroovyRun.java | 2 + .../resources/servers/artemisServer.groovy | 41 ++++++++++++--- .../JournalCompatibilityTest.java | 16 +++--- .../compatibility/VersionedBaseTest.java | 5 ++ 6 files changed, 117 insertions(+), 16 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java index f2eb488ae84..75092643aac 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java @@ -20,10 +20,10 @@ import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.api.core.ActiveMQBuffer; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.core.journal.EncodingSupport; -import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.core.settings.Mergeable; import org.apache.activemq.artemis.utils.BufferHelper; import org.apache.activemq.artemis.utils.DataConstants; @@ -729,6 +729,17 @@ public void merge(final AddressSettings merged) { @Override public void decode(ActiveMQBuffer buffer) { + int original = buffer.readerIndex(); + try { + decode(buffer, false); + } catch (Throwable e) { + buffer.readerIndex(original); + // Try a compatible version where the wire was broken + decode(buffer, true); + } + } + + public void decode(ActiveMQBuffer buffer, boolean tryCompatible) { SimpleString policyStr = buffer.readNullableSimpleString(); if (policyStr != null) { @@ -791,7 +802,7 @@ public void decode(ActiveMQBuffer buffer) { autoDeleteQueues = BufferHelper.readNullableBoolean(buffer); - policyStr = buffer.readNullableSimpleString(); + policyStr = tryCompatible ? null : buffer.readNullableSimpleString(); if (policyStr != null) { configDeleteQueues = DeletionPolicy.valueOf(policyStr.toString()); @@ -803,7 +814,7 @@ public void decode(ActiveMQBuffer buffer) { autoDeleteAddresses = BufferHelper.readNullableBoolean(buffer); - policyStr = buffer.readNullableSimpleString(); + policyStr = tryCompatible ? null : buffer.readNullableSimpleString(); if (policyStr != null) { configDeleteAddresses = DeletionPolicy.valueOf(policyStr.toString()); diff --git a/tests/compatibility-tests/pom.xml b/tests/compatibility-tests/pom.xml index f4edcd6d407..a8893037695 100644 --- a/tests/compatibility-tests/pom.xml +++ b/tests/compatibility-tests/pom.xml @@ -350,6 +350,50 @@ ARTEMIS-240 + + compile + + dependency-scan + + 210-check + + + org.apache.activemq:artemis-jms-server:2.1.0 + org.apache.activemq:artemis-jms-client:2.1.0 + org.apache.activemq:artemis-cli:2.1.0 + org.apache.activemq:artemis-hornetq-protocol:2.1.0 + org.apache.activemq:artemis-amqp-protocol:2.1.0 + org.apache.activemq:artemis-hornetq-protocol:2.1.0 + org.codehaus.groovy:groovy-all:${groovy.version} + + + org.apache.activemq.tests:compatibility-tests:${project.version} + + ARTEMIS-210 + + + + compile + + dependency-scan + + 200-check + + + org.apache.activemq:artemis-jms-server:2.0.0 + org.apache.activemq:artemis-jms-client:2.0.0 + org.apache.activemq:artemis-cli:2.0.0 + org.apache.activemq:artemis-hornetq-protocol:2.0.0 + org.apache.activemq:artemis-amqp-protocol:2.0.0 + org.apache.activemq:artemis-hornetq-protocol:2.0.0 + org.codehaus.groovy:groovy-all:${groovy.version} + + + org.apache.activemq.tests:compatibility-tests:${project.version} + + ARTEMIS-200 + + 140-check compile @@ -453,6 +497,14 @@ ARTEMIS-SNAPSHOT ${ARTEMIS-SNAPSHOT}
+ + ARTEMIS-200 + ${ARTEMIS-200} + + + ARTEMIS-210 + ${ARTEMIS-210} + ARTEMIS-240 ${ARTEMIS-240} diff --git a/tests/compatibility-tests/src/main/java/org/apache/activemq/artemis/tests/compatibility/GroovyRun.java b/tests/compatibility-tests/src/main/java/org/apache/activemq/artemis/tests/compatibility/GroovyRun.java index 46b6cec45f7..5efa3d30aef 100644 --- a/tests/compatibility-tests/src/main/java/org/apache/activemq/artemis/tests/compatibility/GroovyRun.java +++ b/tests/compatibility-tests/src/main/java/org/apache/activemq/artemis/tests/compatibility/GroovyRun.java @@ -30,6 +30,8 @@ public class GroovyRun { public static final String SNAPSHOT = "ARTEMIS-SNAPSHOT"; public static final String ONE_FIVE = "ARTEMIS-155"; public static final String ONE_FOUR = "ARTEMIS-140"; + public static final String TWO_ZERO = "ARTEMIS-200"; + public static final String TWO_ONE = "ARTEMIS-210"; public static final String TWO_FOUR = "ARTEMIS-240"; public static final String HORNETQ_235 = "HORNETQ-235"; public static final String HORNETQ_247 = "HORNETQ-247"; diff --git a/tests/compatibility-tests/src/main/resources/servers/artemisServer.groovy b/tests/compatibility-tests/src/main/resources/servers/artemisServer.groovy index c8cc93be87a..4886a5de6a4 100644 --- a/tests/compatibility-tests/src/main/resources/servers/artemisServer.groovy +++ b/tests/compatibility-tests/src/main/resources/servers/artemisServer.groovy @@ -1,4 +1,7 @@ package servers + +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl + /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -17,15 +20,11 @@ package servers */ // starts an artemis server - -import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; import org.apache.activemq.artemis.core.server.JournalType -import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy; -import org.apache.activemq.artemis.core.settings.impl.AddressSettings; -import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl; +import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy +import org.apache.activemq.artemis.core.settings.impl.AddressSettings +import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS -import org.apache.activemq.artemis.tests.compatibility.GroovyRun; - String folder = arg[0]; String id = arg[1]; @@ -63,3 +62,31 @@ server.start(); server.getJMSServerManager().createTopic(true, "topic"); server.getJMSServerManager().createQueue(true, "queue", null, true); + +if (setAddressSettings) { + + // this is to force records that will have pittfals between versions + server.getJMSServerManager().getActiveMQServer().getActiveMQServerControl(). + addAddressSettings("ad1", //@Parameter(desc = "an address match", name = "addressMatch") String addressMatch, + "dla", // @Parameter(desc = "the dead letter address setting", name = "DLA") String DLA, + "exp", //@Parameter(desc = "the expiry address setting", name = "expiryAddress") String expiryAddress, + 0l, //@Parameter(desc = "the expiry delay setting", name = "expiryDelay") long expiryDelay, + false, //@Parameter(desc = "are any queues created for this address a last value queue", name = "lastValueQueue") boolean lastValueQueue, + 1, //@Parameter(desc = "the delivery attempts", name = "deliveryAttempts") int deliveryAttempts, + 10 * 1024 * 1024, //@Parameter(desc = "the max size in bytes", name = "maxSizeBytes") long maxSizeBytes, + 1024 * 1024, //@Parameter(desc = "the page size in bytes", name = "pageSizeBytes") int pageSizeBytes, + 3, //@Parameter(desc = "the max number of pages in the soft memory cache", name = "pageMaxCacheSize") int pageMaxCacheSize, + 0l, //@Parameter(desc = "the redelivery delay", name = "redeliveryDelay") long redeliveryDelay, + 0, //@Parameter(desc = "the redelivery delay multiplier", name = "redeliveryMultiplier") double redeliveryMultiplier, + 0, //@Parameter(desc = "the maximum redelivery delay", name = "maxRedeliveryDelay") long maxRedeliveryDelay, + 0, //@Parameter(desc = "the redistribution delay", name = "redistributionDelay") long redistributionDelay, + false, //@Parameter(desc = "do we send to the DLA when there is no where to route the message", name = "sendToDLAOnNoRoute") boolean sendToDLAOnNoRoute, + "BLOCK", //@Parameter(desc = "the policy to use when the address is full", name = "addressFullMessagePolicy") String addressFullMessagePolicy, + 1000, //@Parameter(desc = "when a consumer falls below this threshold in terms of messages consumed per second it will be considered 'slow'", name = "slowConsumerThreshold") long slowConsumerThreshold, + 1000, //@Parameter(desc = "how often (in seconds) to check for slow consumers", name = "slowConsumerCheckPeriod") long slowConsumerCheckPeriod, + "NOTIFY", //@Parameter(desc = "the policy to use when a slow consumer is detected", name = "slowConsumerPolicy") String slowConsumerPolicy, + true, //@Parameter(desc = "allow queues to be created automatically", name = "autoCreateJmsQueues") boolean autoCreateJmsQueues, + true, // @Parameter(desc = "allow auto-created queues to be deleted automatically", name = "autoDeleteJmsQueues") boolean autoDeleteJmsQueues, + true, //@Parameter(desc = "allow topics to be created automatically", name = "autoCreateJmsTopics") boolean autoCreateJmsTopics, + true) //@Parameter(desc = "allow auto-created topics to be deleted automatically", name = "autoDeleteJmsTopics") boolean autoDeleteJmsTopics) throws Exception; +} diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java index 171e721c386..e63f70b27f4 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java @@ -19,6 +19,8 @@ import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.SNAPSHOT; import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.TWO_FOUR; +import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.TWO_ONE; +import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.TWO_ZERO; import java.util.ArrayList; import java.util.Collection; @@ -62,6 +64,8 @@ public static Collection getParameters() { // combinations.add(new Object[]{SNAPSHOT, ONE_FIVE, ONE_FIVE}); // combinations.add(new Object[]{ONE_FIVE, ONE_FIVE, ONE_FIVE}); + combinations.add(new Object[]{null, TWO_ZERO, SNAPSHOT}); + combinations.add(new Object[]{null, TWO_ONE, SNAPSHOT}); combinations.add(new Object[]{null, TWO_FOUR, SNAPSHOT}); // the purpose on this one is just to validate the test itself. /// if it can't run against itself it won't work at all @@ -94,12 +98,12 @@ public void tearDown() { @Test public void testSendReceive() throws Throwable { setVariable(senderClassloader, "persistent", true); - startServer(serverFolder.getRoot(), senderClassloader, "journalTest"); + startServer(serverFolder.getRoot(), senderClassloader, "journalTest", null, true); evaluate(senderClassloader, "meshTest/sendMessages.groovy", server, sender, "sendAckMessages"); stopServer(senderClassloader); setVariable(receiverClassloader, "persistent", true); - startServer(serverFolder.getRoot(), receiverClassloader, "journalTest"); + startServer(serverFolder.getRoot(), receiverClassloader, "journalTest", null, false); setVariable(receiverClassloader, "latch", null); evaluate(receiverClassloader, "meshTest/sendMessages.groovy", server, receiver, "receiveMessages"); @@ -112,12 +116,12 @@ public void testSendReceive() throws Throwable { @Test public void testSendReceiveQueueMetrics() throws Throwable { setVariable(senderClassloader, "persistent", true); - startServer(serverFolder.getRoot(), senderClassloader, "journalTest"); + startServer(serverFolder.getRoot(), senderClassloader, "journalTest", null, true); evaluate(senderClassloader, "meshTest/sendMessages.groovy", server, sender, "sendAckMessages"); stopServer(senderClassloader); setVariable(receiverClassloader, "persistent", true); - startServer(serverFolder.getRoot(), receiverClassloader, "journalTest"); + startServer(serverFolder.getRoot(), receiverClassloader, "journalTest", null, false); setVariable(receiverClassloader, "latch", null); evaluate(receiverClassloader, "metrics/queueMetrics.groovy", server, receiver, "receiveMessages"); @@ -132,14 +136,14 @@ public void testSendReceiveQueueMetrics() throws Throwable { public void testSendReceiveSizeQueueMetricsPaging() throws Throwable { setVariable(senderClassloader, "persistent", true); //Set max size to 1 to cause messages to immediately go to the paging store - startServer(serverFolder.getRoot(), senderClassloader, "journalTest", Long.toString(1)); + startServer(serverFolder.getRoot(), senderClassloader, "journalTest", Long.toString(1), true); evaluate(senderClassloader, "journalcompatibility/forcepaging.groovy"); evaluate(senderClassloader, "meshTest/sendMessages.groovy", server, sender, "sendAckMessages"); evaluate(senderClassloader, "journalcompatibility/ispaging.groovy"); stopServer(senderClassloader); setVariable(receiverClassloader, "persistent", true); - startServer(serverFolder.getRoot(), receiverClassloader, "journalTest", Long.toString(1)); + startServer(serverFolder.getRoot(), receiverClassloader, "journalTest", Long.toString(1), false); evaluate(receiverClassloader, "journalcompatibility/ispaging.groovy"); diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java index 96d96d73e54..19bafd06de3 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java @@ -190,6 +190,10 @@ public void startServer(File folder, ClassLoader loader, String serverName) thro } public void startServer(File folder, ClassLoader loader, String serverName, String globalMaxSize) throws Throwable { + startServer(folder, loader, serverName, globalMaxSize, false); + + } + public void startServer(File folder, ClassLoader loader, String serverName, String globalMaxSize, boolean setAddressSettings) throws Throwable { folder.mkdirs(); String scriptToUse; @@ -201,6 +205,7 @@ public void startServer(File folder, ClassLoader loader, String serverName, Stri scriptToUse = "servers/hornetqServer.groovy"; } + setVariable(loader, "setAddressSettings", setAddressSettings); evaluate(loader, scriptToUse, folder.getAbsolutePath(), serverName, server, sender, receiver, globalMaxSize); } public void stopServer(ClassLoader loader) throws Throwable { From 7d14c06dd965adff409c32e2a962bad7948b1bca Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Mon, 6 Aug 2018 09:15:15 -0500 Subject: [PATCH 115/207] ARTEMIS-1947 fix tests --- .../integration/management/ActiveMQServerControlTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java index 048d5067999..2b76de61953 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java @@ -1550,8 +1550,8 @@ public void testListAllSessionsAsJSON() throws Exception { Assert.assertEquals("myUser", second.getString("principal")); Assert.assertTrue(second.getJsonNumber("creationTime").longValue() > 0); Assert.assertEquals(1, second.getJsonNumber("consumerCount").longValue()); - Assert.assertTrue(second.getJsonString("metadata").getString().contains("foo=bar")); - Assert.assertTrue(second.getJsonString("metadata").getString().contains("bar=baz")); + Assert.assertEquals(second.getJsonObject("metadata").getJsonString("foo").getString(), "bar"); + Assert.assertEquals(second.getJsonObject("metadata").getJsonString("bar").getString(), "baz"); } @Test @@ -1572,8 +1572,8 @@ public void testListAllSessionsAsJSONWithJMS() throws Exception { JsonArray array = JsonUtil.readJsonArray(jsonString); Assert.assertEquals(1 + (usingCore() ? 1 : 0), array.size()); JsonObject obj = lookupSession(array, ((ActiveMQConnection)con).getInitialSession()); - Assert.assertTrue(obj.getString("metadata").contains(ClientSession.JMS_SESSION_CLIENT_ID_PROPERTY + "=" + clientID)); - Assert.assertTrue(obj.getString("metadata").contains(ClientSession.JMS_SESSION_IDENTIFIER_PROPERTY)); + Assert.assertEquals(obj.getJsonObject("metadata").getJsonString(ClientSession.JMS_SESSION_CLIENT_ID_PROPERTY).getString(), clientID); + Assert.assertNotNull(obj.getJsonObject("metadata").getJsonString(ClientSession.JMS_SESSION_IDENTIFIER_PROPERTY)); } @Test From 0ae7d32532dce9980bab5031d99314c7f66c56f0 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Tue, 7 Aug 2018 10:51:56 -0500 Subject: [PATCH 116/207] ARTEMIS-2014 Treat inability to create directory for paging as critial --- .../io/AbstractSequentialFileFactory.java | 6 +- .../tests/integration/paging/PagingTest.java | 58 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/AbstractSequentialFileFactory.java b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/AbstractSequentialFileFactory.java index 5f191cfa4cb..482aaa2a51b 100644 --- a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/AbstractSequentialFileFactory.java +++ b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/io/AbstractSequentialFileFactory.java @@ -224,8 +224,10 @@ public void releaseBuffer(final ByteBuffer buffer) { @Override public void createDirs() throws Exception { boolean ok = journalDir.mkdirs(); - if (!ok) { - throw new IOException("Failed to create directory " + journalDir); + if (!ok && !journalDir.exists()) { + IOException e = new IOException("Unable to create directory: " + journalDir); + onIOError(e, e.getMessage(), null); + throw e; } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java index 1436bea0312..71be960df2d 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java @@ -35,6 +35,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -96,6 +97,7 @@ import org.jboss.logging.Logger; import org.junit.After; import org.junit.Assert; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -1400,6 +1402,62 @@ public void testReceiveImmediate() throws Exception { } + @Test + public void testInabilityToCreateDirectoryDuringPaging() throws Exception { + // this test only applies to file-based stores + Assume.assumeTrue(storeType == StoreConfiguration.StoreType.FILE); + + clearDataRecreateServerDirs(); + + Configuration config = createDefaultInVMConfig().setJournalSyncNonTransactional(false).setPagingDirectory(UUID.randomUUID().toString()); + + server = createServer(true, config, PagingTest.PAGE_SIZE, PagingTest.PAGE_MAX); + + server.start(); + + final int numberOfMessages = 100; + + locator = createInVMNonHALocator().setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setBlockOnAcknowledge(true); + + sf = createSessionFactory(locator); + + ClientSession session = sf.createSession(false, true, true); + + session.createQueue(PagingTest.ADDRESS, RoutingType.MULTICAST, PagingTest.ADDRESS, null, true); + + ClientProducer producer = session.createProducer(PagingTest.ADDRESS); + + ClientMessage message = null; + + byte[] body = new byte[MESSAGE_SIZE]; + + ByteBuffer bb = ByteBuffer.wrap(body); + + for (int j = 1; j <= MESSAGE_SIZE; j++) { + bb.put(getSamplebyte(j)); + } + + for (int i = 0; i < numberOfMessages; i++) { + message = session.createMessage(true); + + ActiveMQBuffer bodyLocal = message.getBodyBuffer(); + + bodyLocal.writeBytes(body); + + message.putIntProperty(new SimpleString("id"), i); + + try { + producer.send(message); + } catch (Exception e) { + // ignore + } + } + assertTrue(Wait.waitFor(() -> server.getState() == ActiveMQServer.SERVER_STATE.STOPPED, 5000, 200)); + session.close(); + sf.close(); + locator.close(); + } + /** * This test will remove all the page directories during a restart, simulating a crash scenario. The server should still start after this */ From 1b15f955544c8088c744fef04c2d194add1572ae Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Wed, 8 Aug 2018 09:52:38 -0500 Subject: [PATCH 117/207] ARTEMIS-2014 fix directory name in test --- .../activemq/artemis/tests/integration/paging/PagingTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java index 71be960df2d..f97e60a7117 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingTest.java @@ -1409,7 +1409,7 @@ public void testInabilityToCreateDirectoryDuringPaging() throws Exception { clearDataRecreateServerDirs(); - Configuration config = createDefaultInVMConfig().setJournalSyncNonTransactional(false).setPagingDirectory(UUID.randomUUID().toString()); + Configuration config = createDefaultInVMConfig().setJournalSyncNonTransactional(false).setPagingDirectory("/" + UUID.randomUUID().toString()); server = createServer(true, config, PagingTest.PAGE_SIZE, PagingTest.PAGE_MAX); From 5c2f79ed45993c843f6449cb6e649d157c64208b Mon Sep 17 00:00:00 2001 From: Jiri Danek Date: Tue, 7 Aug 2018 16:56:20 +0200 Subject: [PATCH 118/207] NO-JIRA fix flaky test QueueControlTest#testListMessagesWithEmptyFilter The below error is prevented by using Wait.assertEquals where Assert.assertEquals was used previously. java.lang.AssertionError: Expected :2 Actual :1 [...] at org.apache.activemq.artemis.tests.integration.management.QueueControlTest.testListMessagesWithEmptyFilter(QueueControlTest.java:827) --- .../tests/integration/management/QueueControlTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java index da8ea17df05..8eb6e03d60f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java @@ -823,13 +823,11 @@ public void testListMessagesWithEmptyFilter() throws Exception { producer.send(session.createMessage(durable)); producer.send(session.createMessage(durable)); - Map[] messages = queueControl.listMessages(""); - Assert.assertEquals(2, messages.length); + Wait.assertEquals(2, () -> queueControl.listMessages("").length); consumeMessages(2, session, queue); - messages = queueControl.listMessages(""); - Assert.assertEquals(0, messages.length); + Wait.assertEquals(0, () -> queueControl.listMessages("").length); session.deleteQueue(queue); } From fbb622ae6976209a0d2d4ec1852ca8b91c4cf1e9 Mon Sep 17 00:00:00 2001 From: Jiri Danek Date: Tue, 7 Aug 2018 16:59:30 +0200 Subject: [PATCH 119/207] NO-JIRA fix flaky test QueueControlTest#testListMessagesWithNullFilter The below error is prevented by using Wait.assertEquals where Assert.assertEquals was used previously. java.lang.AssertionError: Expected :2 Actual :1 [...] at org.junit.Assert.assertEquals(Assert.java:542) at org.apache.activemq.artemis.tests.integration.management.QueueControlTest.testListMessagesWithNullFilter(QueueControlTest.java:804) --- .../tests/integration/management/QueueControlTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java index 8eb6e03d60f..fb0a3e5999f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java @@ -800,13 +800,11 @@ public void testListMessagesWithNullFilter() throws Exception { producer.send(session.createMessage(durable)); producer.send(session.createMessage(durable)); - Map[] messages = queueControl.listMessages(null); - Assert.assertEquals(2, messages.length); + Wait.assertEquals(2, () -> queueControl.listMessages(null).length); consumeMessages(2, session, queue); - messages = queueControl.listMessages(null); - Assert.assertEquals(0, messages.length); + Wait.assertEquals(0, () -> queueControl.listMessages(null).length); session.deleteQueue(queue); } From 23a30b61587a0b78c8bbc210e42bc0a8b18d03d6 Mon Sep 17 00:00:00 2001 From: Jiri Danek Date: Tue, 7 Aug 2018 17:10:00 +0200 Subject: [PATCH 120/207] NO-JIRA fix flaky test QueueControlTest#testResetMessagesAdded The occasional assertion error is prevented by using Wait.assertEquals where Assert.assertEquals was used previously. I did not observe the timing issue on all asserts (only on the first two), but there is no harm in replacing them all. java.lang.AssertionError: Expected :2 Actual :1 --- .../tests/integration/management/QueueControlTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java index fb0a3e5999f..46d360e7ab8 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java @@ -2311,17 +2311,17 @@ public void testResetMessagesAdded() throws Exception { ClientProducer producer = session.createProducer(address); producer.send(session.createMessage(durable)); - Assert.assertEquals(1, getMessagesAdded(queueControl)); + Wait.assertEquals(1, () -> getMessagesAdded(queueControl)); producer.send(session.createMessage(durable)); - Assert.assertEquals(2, getMessagesAdded(queueControl)); + Wait.assertEquals(2, () -> getMessagesAdded(queueControl)); consumeMessages(2, session, queue); - Assert.assertEquals(2, getMessagesAdded(queueControl)); + Wait.assertEquals(2, () -> getMessagesAdded(queueControl)); queueControl.resetMessagesAdded(); - Assert.assertEquals(0, getMessagesAdded(queueControl)); + Wait.assertEquals(0, () -> getMessagesAdded(queueControl)); session.deleteQueue(queue); } From c6521e0700006de1a00b614768168a0f96c5e28b Mon Sep 17 00:00:00 2001 From: Jiri Danek Date: Tue, 7 Aug 2018 20:02:41 +0200 Subject: [PATCH 121/207] NO-JIRA fix flaky tests QueueControlTest#testChangeMessagePriority{,WithInvalidValue} The occasional assertion error is prevented by using Wait.assertEquals where Assert.assertEquals was used previously. --- .../tests/integration/management/QueueControlTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java index 46d360e7ab8..9f4a93cfa8a 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java @@ -1926,7 +1926,7 @@ public void testChangeMessagePriority() throws Exception { producer.send(message); QueueControl queueControl = createManagementControl(address, queue); - Assert.assertEquals(1, getMessageCount(queueControl)); + Wait.assertEquals(1, () -> getMessageCount(queueControl)); // the message IDs are set on the server Map[] messages = queueControl.listMessages(null); @@ -1959,7 +1959,7 @@ public void testChangeMessagePriorityWithInvalidValue() throws Exception { producer.send(message); QueueControl queueControl = createManagementControl(address, queue); - Assert.assertEquals(1, getMessageCount(queueControl)); + Wait.assertEquals(1, () -> getMessageCount(queueControl)); // the message IDs are set on the server Map[] messages = queueControl.listMessages(null); From fed0426ff3714b66f1289fdaeb3d96088669debb Mon Sep 17 00:00:00 2001 From: Jiri Danek Date: Tue, 7 Aug 2018 20:16:18 +0200 Subject: [PATCH 122/207] NO-JIRA fix flaky tests QueueControlTest#testResetMessagesExpired The occasional assertion error is prevented by using Wait.assertEquals where Assert.assertEquals was used previously. java.lang.AssertionError: Expected :1 Actual :0 [...] at org.junit.Assert.assertEquals(Assert.java:542) at org.apache.activemq.artemis.tests.integration.management.QueueControlTest.testResetMessagesExpired(QueueControlTest.java:2370) --- .../tests/integration/management/QueueControlTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java index 9f4a93cfa8a..0faa055f537 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java @@ -2366,8 +2366,9 @@ public void testResetMessagesExpired() throws Exception { producer.send(message); // the message IDs are set on the server - Map[] messages = queueControl.listMessages(null); - Assert.assertEquals(1, messages.length); + Map[] messages; + Wait.assertEquals(1, () -> queueControl.listMessages(null).length); + messages = queueControl.listMessages(null); long messageID = (Long) messages[0].get("messageID"); queueControl.expireMessage(messageID); From c1a191c547c0fb8e0bce4f17751e356b213f1ddb Mon Sep 17 00:00:00 2001 From: Jiri Danek Date: Tue, 7 Aug 2018 18:22:57 +0200 Subject: [PATCH 123/207] ARTEMIS-2016 fix flaky test QueueControlTest#testRemoveAllWithPagingMode Parameters going into Wait.waitFor were originally wrong, because `durationMillis: 3, sleepMillis: 100` means you would test the condition only once. This commit is changing the durationMillis from 3ms to 3s, swapping the two numbers (duration 100ms, sleep 3ms) would also be reasonable, I think. Next, Wait.assertEquals is here being used, instead of Assert.assertTrue. I saw the test fail only once, and never was able to reproduce it again, but I think this commit does improve the test and so it is worthwhile. java.lang.AssertionError at org.apache.activemq.artemis.tests.integration.management.QueueControlTest.assertMetrics(QueueControlTest.java:2651) at org.apache.activemq.artemis.tests.integration.management.QueueControlTest.assertMessageMetrics(QueueControlTest.java:2615) at org.apache.activemq.artemis.tests.integration.management.QueueControlTest.testRemoveAllWithPagingMode(QueueControlTest.java:1554) --- .../apache/activemq/artemis/junit/Wait.java | 6 +++++- .../management/QueueControlTest.java | 20 +++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/Wait.java b/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/Wait.java index e37539a3a65..c0aa55d40ce 100644 --- a/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/Wait.java +++ b/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/Wait.java @@ -52,7 +52,11 @@ public static void assertEquals(long size, LongCondition condition) throws Excep } public static void assertEquals(long size, LongCondition condition, long timeout) throws Exception { - boolean result = waitFor(() -> condition.getCount() == size, timeout); + assertEquals(size, condition, timeout, SLEEP_MILLIS); + } + + public static void assertEquals(Long size, LongCondition condition, long timeout, long sleepMillis) throws Exception { + boolean result = waitFor(() -> condition.getCount() == size, timeout, sleepMillis); if (!result) { Assert.fail(size + " != " + condition.getCount()); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java index 0faa055f537..123f5812482 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java @@ -2632,25 +2632,25 @@ protected void assertMetrics(final QueueControl queueControl, long messageCount, Supplier durableCount, Supplier durableSize) throws Exception { //make sure count stat equals message count - Assert.assertTrue(Wait.waitFor(() -> count.get().longValue() == messageCount, 3, 100)); + Assert.assertTrue(Wait.waitFor(() -> count.get().longValue() == messageCount, 3 * 1000, 100)); if (messageCount > 0) { //verify size stat greater than 0 - Assert.assertTrue(Wait.waitFor(() -> size.get().longValue() > 0, 3, 100)); + Assert.assertTrue(Wait.waitFor(() -> size.get().longValue() > 0, 3 * 1000, 100)); //If durable then make sure durable count and size are correct if (durable) { - Assert.assertTrue(Wait.waitFor(() -> durableCount.get().longValue() == messageCount, 3, 100)); - Assert.assertTrue(Wait.waitFor(() -> durableSize.get().longValue() > 0, 3, 100)); + Wait.assertEquals(messageCount, () -> durableCount.get().longValue(), 3 * 1000, 100); + Assert.assertTrue(Wait.waitFor(() -> durableSize.get().longValue() > 0, 3 * 1000, 100)); } else { - Assert.assertTrue(Wait.waitFor(() -> durableCount.get().longValue() == 0, 3, 100)); - Assert.assertTrue(Wait.waitFor(() -> durableSize.get().longValue() == 0, 3, 100)); + Wait.assertEquals(0L, () -> durableCount.get().longValue(), 3 * 1000, 100); + Wait.assertEquals(0L, () -> durableSize.get().longValue(), 3 * 1000, 100); } } else { - Assert.assertTrue(Wait.waitFor(() -> count.get().longValue() == 0, 3, 100)); - Assert.assertTrue(Wait.waitFor(() -> durableCount.get().longValue() == 0, 3, 100)); - Assert.assertTrue(Wait.waitFor(() -> size.get().longValue() == 0, 3, 100)); - Assert.assertTrue(Wait.waitFor(() -> durableSize.get().longValue() == 0, 3, 100)); + Wait.assertEquals(0L, () -> count.get().longValue(), 3 * 1000, 100); + Wait.assertEquals(0L, () -> durableCount.get().longValue(), 3 * 1000, 100); + Wait.assertEquals(0L, () -> size.get().longValue(), 3 * 1000, 100); + Wait.assertEquals(0L, () -> durableSize.get().longValue(), 3 * 1000, 100); } } } From 05816fefab47a4f440b2d228624294433e8e86e0 Mon Sep 17 00:00:00 2001 From: Jiri Danek Date: Tue, 7 Aug 2018 16:42:04 +0200 Subject: [PATCH 124/207] NO-JIRA fix unused parameter `durable` in AmqpClientTestSupport#sendMessages --- .../artemis/tests/integration/amqp/AmqpClientTestSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpClientTestSupport.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpClientTestSupport.java index d7cf3f493ca..5d8c68100c4 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpClientTestSupport.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpClientTestSupport.java @@ -326,7 +326,7 @@ protected void sendMessages(String destinationName, int count, RoutingType routi for (int i = 0; i < count; ++i) { AmqpMessage message = new AmqpMessage(); message.setMessageId("MessageID:" + i); - message.setDurable(true); + message.setDurable(durable); if (routingType != null) { message.setMessageAnnotation(AMQPMessageSupport.ROUTING_TYPE.toString(), routingType.getType()); } From 8274703153846c964da71e68d61a5c6e4637ccc6 Mon Sep 17 00:00:00 2001 From: Jiri Danek Date: Tue, 7 Aug 2018 21:01:04 +0200 Subject: [PATCH 125/207] NO-JIRA improve assertions in QueueControlTest This commit replaces assert{True,Equals} with more specific assertion methods. --- .../management/QueueControlTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java index 123f5812482..9a68badad35 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java @@ -112,7 +112,7 @@ public void testAttributes() throws Exception { Assert.assertEquals(address.toString(), queueControl.getAddress()); Assert.assertEquals(filter.toString(), queueControl.getFilter()); Assert.assertEquals(durable, queueControl.isDurable()); - Assert.assertEquals(false, queueControl.isTemporary()); + Assert.assertFalse(queueControl.isTemporary()); session.deleteQueue(queue); } @@ -126,7 +126,7 @@ public void testGetNullFilter() throws Exception { QueueControl queueControl = createManagementControl(address, queue); Assert.assertEquals(queue.toString(), queueControl.getName()); - Assert.assertEquals(null, queueControl.getFilter()); + Assert.assertNull(queueControl.getFilter()); session.deleteQueue(queue); } @@ -1027,7 +1027,7 @@ public void testRetryMultipleMessages() throws Exception { //Verify that original queue has a memory size greater than 0 and DLQ is 0 assertTrue(queueMemorySize1.get() > 0); - assertTrue(queueMemorySize2.get() == 0); + assertEquals(0, queueMemorySize2.get()); // Read and rollback all messages to DLQ ClientConsumer clientConsumer = session.createConsumer(qName); @@ -1042,7 +1042,7 @@ public void testRetryMultipleMessages() throws Exception { Assert.assertNull(clientConsumer.receiveImmediate()); //Verify that original queue has a memory size of 0 and DLQ is greater than 0 after rollback - assertTrue(queueMemorySize1.get() == 0); + assertEquals(0, queueMemorySize1.get()); assertTrue(queueMemorySize2.get() > 0); QueueControl dlqQueueControl = createManagementControl(dla, dlq); @@ -1056,7 +1056,7 @@ public void testRetryMultipleMessages() throws Exception { //Verify that original queue has a memory size of greater than 0 and DLQ is 0 after move assertTrue(queueMemorySize1.get() > 0); - assertTrue(queueMemorySize2.get() == 0); + assertEquals(0, queueMemorySize2.get()); // .. and that the messages is now on the original queue once more. for (int i = 0; i < numMessagesToTest; i++) { @@ -1069,8 +1069,8 @@ public void testRetryMultipleMessages() throws Exception { clientConsumer.close(); //Verify that original queue and DLQ have a memory size of 0 - assertTrue(queueMemorySize1.get() == 0); - assertTrue(queueMemorySize2.get() == 0); + assertEquals(0, queueMemorySize1.get()); + assertEquals(0, queueMemorySize2.get()); } /** @@ -1521,7 +1521,7 @@ public void testRemoveAllWithPagingMode() throws Exception { session.createQueue(address, RoutingType.MULTICAST, queueName, null, durable); Queue queue = server.locateQueue(queueName); - Assert.assertEquals(false, queue.getPageSubscription().isPaging()); + Assert.assertFalse(queue.getPageSubscription().isPaging()); ClientProducer producer = session.createProducer(address); @@ -1545,7 +1545,7 @@ public void testRemoveAllWithPagingMode() throws Exception { producer.send(message); } - Assert.assertEquals(true, queue.getPageSubscription().isPaging()); + Assert.assertTrue(queue.getPageSubscription().isPaging()); QueueControl queueControl = createManagementControl(address, queueName); assertMessageMetrics(queueControl, numberOfMessages, durable); @@ -2570,7 +2570,7 @@ public void testGetScheduledCountOnRemove() throws Exception { final LocalQueueBinding binding = (LocalQueueBinding) server.getPostOffice().getBinding(queue); Queue q = binding.getQueue(); AtomicInteger queueMemorySize1 = (AtomicInteger) queueMemorySizeField.get(q); - assertTrue(queueMemorySize1.get() == 0); + assertEquals(0, queueMemorySize1.get()); ClientProducer producer = session.createProducer(address); ClientMessage message = session.createMessage(durable); @@ -2582,7 +2582,7 @@ public void testGetScheduledCountOnRemove() throws Exception { Assert.assertEquals(0, queueControl.getMessageCount()); //Verify that original queue has a memory size of 0 - assertTrue(queueMemorySize1.get() == 0); + assertEquals(0, queueMemorySize1.get()); session.deleteQueue(queue); } From 8dd0e9472fc9eaf08c3d64c3935aeafbf04a422a Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Tue, 31 Jul 2018 11:16:26 +0200 Subject: [PATCH 126/207] ARTEMIS-1999 Broker uses 100% core's CPU time if msg grouping is used The deliver loop won't give up trying to deliver messages when back-pressure kicks in (credits and/or TCP) if msg grouping is used and there are many consumers registered: this change will allow the loop to exit by instructing the logic that the group consumer is the only consumer to check. --- .../unit/core/server/impl/QueueImplTest.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/QueueImplTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/QueueImplTest.java index 0aa6e5c84ac..b0987aa6228 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/QueueImplTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/QueueImplTest.java @@ -24,6 +24,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.SimpleString; @@ -1289,6 +1290,65 @@ public void testTotalIteratorOrder() throws Exception { } } + @Test + public void testGroupMessageWithManyConsumers() throws Exception { + final CountDownLatch firstMessageHandled = new CountDownLatch(1); + final CountDownLatch finished = new CountDownLatch(2); + final Consumer groupConsumer = new FakeConsumer() { + + int count = 0; + + @Override + public synchronized HandleStatus handle(MessageReference reference) { + if (count == 0) { + //the first message is handled and will be used to determine this consumer + //to be the group consumer + count++; + firstMessageHandled.countDown(); + return HandleStatus.HANDLED; + } else if (count <= 2) { + //the next two attempts to send the second message will be done + //attempting a direct delivery and an async one after that + count++; + finished.countDown(); + return HandleStatus.BUSY; + } else { + //this shouldn't happen, because the last attempt to deliver + //the second message should have stop the delivery loop: + //it will succeed just to let the message being handled and + //reduce the message count to 0 + return HandleStatus.HANDLED; + } + } + }; + final Consumer noConsumer = new FakeConsumer() { + @Override + public synchronized HandleStatus handle(MessageReference reference) { + Assert.fail("this consumer isn't allowed to consume any message"); + throw new AssertionError(); + } + }; + final QueueImpl queue = new QueueImpl(1, new SimpleString("address1"), QueueImplTest.queue1, + null, null, false, true, false, + scheduledExecutor, null, null, null, + ArtemisExecutor.delegate(executor), null, null); + queue.addConsumer(groupConsumer); + queue.addConsumer(noConsumer); + final MessageReference firstMessageReference = generateReference(queue, 1); + final SimpleString groupName = SimpleString.toSimpleString("group"); + firstMessageReference.getMessage().putStringProperty(Message.HDR_GROUP_ID, groupName); + final MessageReference secondMessageReference = generateReference(queue, 2); + secondMessageReference.getMessage().putStringProperty(Message.HDR_GROUP_ID, groupName); + queue.addTail(firstMessageReference, true); + Assert.assertTrue("first message isn't handled", firstMessageHandled.await(3000, TimeUnit.MILLISECONDS)); + Assert.assertEquals("group consumer isn't correctly set", groupConsumer, queue.getGroups().get(groupName)); + queue.addTail(secondMessageReference, true); + final boolean atLeastTwoDeliverAttempts = finished.await(3000, TimeUnit.MILLISECONDS); + Assert.assertTrue(atLeastTwoDeliverAttempts); + Thread.sleep(1000); + Assert.assertEquals("The second message should be in the queue", 1, queue.getMessageCount()); + } + private QueueImpl getNonDurableQueue() { return getQueue(QueueImplTest.queue1, false, false, null); } From 985d1e1fcbce7cf02fd1604f9d9255a2c4736008 Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Wed, 8 Aug 2018 16:33:11 +0100 Subject: [PATCH 127/207] ARTEMIS-1482 Add back check for SimpleString --- .../org/apache/activemq/artemis/api/core/SimpleString.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/SimpleString.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/SimpleString.java index b3af64afb7c..96e48b8a9fd 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/SimpleString.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/SimpleString.java @@ -179,6 +179,9 @@ public static SimpleString readSimpleString(ByteBuf buffer, ByteBufSimpleStringP } public static SimpleString readSimpleString(final ByteBuf buffer, final int length) { + if (length > buffer.readableBytes()) { + throw new IndexOutOfBoundsException(); + } byte[] data = new byte[length]; buffer.readBytes(data); return new SimpleString(data); From 64cf5357e1a88d98b4e2c3569bb10d489f55e404 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Tue, 7 Aug 2018 18:53:13 +0200 Subject: [PATCH 128/207] ARTEMIS-2015 PriorityLinkedListImpl::isEmpty is not thread-safe PriorityLinkedListImpl::size access is changed to be safely observable by a thread different from the one allowed to write the list. --- .../utils/collections/PriorityLinkedList.java | 11 ++++++++- .../collections/PriorityLinkedListImpl.java | 23 ++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/PriorityLinkedList.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/PriorityLinkedList.java index 79a99f310bf..19e58c27b81 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/PriorityLinkedList.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/PriorityLinkedList.java @@ -18,7 +18,8 @@ /** * A type of linked list which maintains items according to a priority - * and allows adding and removing of elements at both ends, and peeking + * and allows adding and removing of elements at both ends, and peeking.
+ * Only {@link #size()} and {@link #isEmpty()} are safe to be called concurrently. */ public interface PriorityLinkedList { @@ -30,9 +31,17 @@ public interface PriorityLinkedList { void clear(); + /** + * Returns the size of this list.
+ * It is safe to be called concurrently. + */ int size(); LinkedListIterator iterator(); + /** + * Returns {@code true} if empty, {@code false} otherwise.
+ * It is safe to be called concurrently. + */ boolean isEmpty(); } diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/PriorityLinkedListImpl.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/PriorityLinkedListImpl.java index 39d6b6dcc63..00cf0464e97 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/PriorityLinkedListImpl.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/PriorityLinkedListImpl.java @@ -18,6 +18,7 @@ import java.lang.reflect.Array; import java.util.NoSuchElementException; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; /** * A priority linked list implementation @@ -26,9 +27,11 @@ */ public class PriorityLinkedListImpl implements PriorityLinkedList { + private static final AtomicIntegerFieldUpdater SIZE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(PriorityLinkedListImpl.class, "size"); + protected LinkedListImpl[] levels; - private int size; + private volatile int size; private int lastReset; @@ -65,7 +68,7 @@ public void addHead(final T t, final int priority) { levels[priority].addHead(t); - size++; + exclusiveIncrementSize(1); } @Override @@ -74,7 +77,7 @@ public void addTail(final T t, final int priority) { levels[priority].addTail(t); - size++; + exclusiveIncrementSize(1); } @Override @@ -94,7 +97,7 @@ public T poll() { t = ll.poll(); if (t != null) { - size--; + exclusiveIncrementSize(-1); if (ll.size() == 0) { if (highestPriority == i) { @@ -116,7 +119,15 @@ public void clear() { list.clear(); } - size = 0; + exclusiveSetSize(0); + } + + private void exclusiveIncrementSize(int amount) { + SIZE_UPDATER.lazySet(this, this.size + amount); + } + + private void exclusiveSetSize(int value) { + SIZE_UPDATER.lazySet(this, value); } @Override @@ -242,7 +253,7 @@ public void remove() { highestPriority = i; } - size--; + exclusiveIncrementSize(-1); } } } From b0c65ba2dd5b7d1bc0dc75f9370c52da49f39bbf Mon Sep 17 00:00:00 2001 From: Robbie Gemmell Date: Tue, 7 Aug 2018 15:35:16 +0100 Subject: [PATCH 129/207] ARTEMIS-1978: update to proton-j 0.27.3 to resolve sequencing issues Adds test exposing broker behaviour from issues stemming from PROTON-1892 and PROTON-1901 --- pom.xml | 2 +- .../transport/amqp/client/AmqpReceiver.java | 18 ++- .../amqp/AmqpLargeMessageTest.java | 138 +++++++++++++++++- 3 files changed, 149 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 221cff985ff..fb95a5efc3e 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,7 @@ 2.4 2.8.47 4.1.24.Final - 0.27.1 + 0.27.3 3.0.19.Final 1.7.21 0.33.0 diff --git a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpReceiver.java b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpReceiver.java index e9fc75bd924..fb4e4daa04d 100644 --- a/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpReceiver.java +++ b/tests/artemis-test-support/src/main/java/org/apache/activemq/transport/amqp/client/AmqpReceiver.java @@ -363,6 +363,20 @@ public void run() { * if an error occurs while sending the flow. */ public void flow(final int credit) throws IOException { + flow(credit, false); + } + + /** + * Controls the amount of credit given to the receiver link. + * + * @param credit + * the amount of credit to grant. + * @param deferWrite + * defer writing to the wire, hold until for the next operation writes. + * @throws IOException + * if an error occurs while sending the flow. + */ + public void flow(final int credit, final boolean deferWrite) throws IOException { checkClosed(); final ClientFuture request = new ClientFuture(); session.getScheduler().execute(new Runnable() { @@ -372,7 +386,9 @@ public void run() { checkClosed(); try { getEndpoint().flow(credit); - session.pumpToProtonTransport(request); + if (!deferWrite) { + session.pumpToProtonTransport(request); + } request.onSuccess(); } catch (Exception e) { request.onFailure(e); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java index 8465c611283..6393bae5c63 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpLargeMessageTest.java @@ -393,13 +393,7 @@ public void testReceiveRedeliveredLargeMessagesWithSessionFlowControl() throws E receiver2.flow(numMsgs); for (int i = 0; i < numMsgs; ++i) { AmqpMessage message = receiver2.receive(5, TimeUnit.SECONDS); - assertNotNull("failed at " + i, message); - - Section body = message.getWrappedMessage().getBody(); - assertNotNull("No message body for msg " + i, body); - - assertTrue("Unexpected message body type for msg " + body.getClass(), body instanceof Data); - assertEquals("Unexpected body content for msg", new Binary(payload, 0, payload.length), ((Data) body).getValue()); + validateMessage(payload, i, message); message.accept(); } @@ -411,6 +405,136 @@ public void testReceiveRedeliveredLargeMessagesWithSessionFlowControl() throws E } } + @Test(timeout = 60000) + public void testReceiveLargeMessagesMultiplexedOnSameSession() throws Exception { + server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST)); + + int numMsgs = 10; + int maxFrameSize = FRAME_SIZE; // Match the brokers outgoing frame size limit to make window sizing easy + int msgSizeA = FRAME_SIZE * 4; // Bigger multi-frame messages + int msgSizeB = maxFrameSize / 2; // Smaller single frame messages + int sessionCapacity = msgSizeA + maxFrameSize; // Restrict session to 1.X of the larger messages in flight at once, make it likely send is partial. + + byte[] payloadA = createLargePayload(msgSizeA); + assertEquals(msgSizeA, payloadA.length); + byte[] payloadB = createLargePayload(msgSizeB); + assertEquals(msgSizeB, payloadB.length); + + String testQueueNameA = getTestName() + "A"; + String testQueueNameB = getTestName() + "B"; + + AmqpClient client = createAmqpClient(); + + AmqpConnection connection = client.createConnection(); + connection.setMaxFrameSize(maxFrameSize); + connection.setSessionIncomingCapacity(sessionCapacity); + + connection.connect(); + addConnection(connection); + try { + AmqpSession session = connection.createSession(); + AmqpSender senderA = session.createSender(testQueueNameA); + AmqpSender senderB = session.createSender(testQueueNameB); + + // Send in the messages + for (int i = 0; i < numMsgs; ++i) { + AmqpMessage messageA = new AmqpMessage(); + messageA.setBytes(payloadA); + + senderA.send(messageA); + + AmqpMessage messageB = new AmqpMessage(); + messageB.setBytes(payloadB); + + senderB.send(messageB); + } + + Wait.assertEquals(numMsgs, () -> getMessageCount(server.getPostOffice(), testQueueNameA), 5000, 10); + Wait.assertEquals(numMsgs, () -> getMessageCount(server.getPostOffice(), testQueueNameB), 5000, 10); + + AmqpReceiver receiverA = session.createReceiver(testQueueNameA); + AmqpReceiver receiverB = session.createReceiver(testQueueNameB); + + // Split credit flow to encourage overlapping + // Flow initial credit for both consumers, in the same TCP frame. + receiverA.flow(numMsgs / 2, true); + receiverB.flow(numMsgs / 2); + + // Flow remaining credit for both consumers, in the same TCP frame. + receiverA.flow(numMsgs / 2, true); + receiverB.flow(numMsgs / 2); + + ArrayList messagesA = new ArrayList<>(); + ArrayList messagesB = new ArrayList<>(); + + long timeout = 6000; + long start = System.nanoTime(); + + // Validate the messages are all received + boolean timeRemaining = true; + while (timeRemaining) { + if (messagesA.size() < numMsgs) { + LOG.debug("Attempting to receive message for receiver A"); + AmqpMessage messageA = receiverA.receive(20, TimeUnit.MILLISECONDS); + if (messageA != null) { + LOG.debug("Got message for receiver A"); + messagesA.add(messageA); + messageA.accept(); + } + } + + if (messagesB.size() < numMsgs) { + LOG.debug("Attempting to receive message for receiver B"); + AmqpMessage messageB = receiverB.receive(20, TimeUnit.MILLISECONDS); + if (messageB != null) { + LOG.debug("Got message for receiver B"); + messagesB.add(messageB); + messageB.accept(); + } + } + + if (messagesA.size() == numMsgs && messagesB.size() == numMsgs) { + LOG.debug("Received expected messages"); + break; + } + + timeRemaining = System.nanoTime() - start < TimeUnit.MILLISECONDS.toNanos(timeout); + } + + assertTrue("Failed to receive all messages in expected time: A=" + messagesA.size() + ", B=" + messagesB.size(), timeRemaining); + + // Validate there aren't any extras + assertNull("Unexpected additional message present for A", receiverA.receiveNoWait()); + assertNull("Unexpected additional message present for B", receiverB.receiveNoWait()); + + // Validate the transfers were reconstituted to give the expected delivery payload. + for (int i = 0; i < numMsgs; ++i) { + AmqpMessage messageA = messagesA.get(i); + validateMessage(payloadA, i, messageA); + + AmqpMessage messageB = messagesB.get(i); + validateMessage(payloadB, i, messageB); + } + + receiverA.close(); + receiverB.close(); + + session.close(); + } finally { + connection.close(); + } + } + + private void validateMessage(byte[] expectedPayload, int msgNum, AmqpMessage message) { + assertNotNull("failed at " + msgNum, message); + + Section body = message.getWrappedMessage().getBody(); + assertNotNull("No message body for msg " + msgNum, body); + + assertTrue("Unexpected message body type for msg " + body.getClass(), body instanceof Data); + assertEquals("Unexpected body content for msg", new Binary(expectedPayload, 0, expectedPayload.length), ((Data) body).getValue()); + } + @Test(timeout = 60000) public void testMessageWithAmqpValueAndEmptyBinaryPreservesBody() throws Exception { server.getAddressSettingsRepository().addMatch("#", new AddressSettings().setDefaultAddressRoutingType(RoutingType.ANYCAST)); From e9155452784f3241f61c4eb87ec0de20a10d0002 Mon Sep 17 00:00:00 2001 From: "Christopher L. Shannon (cshannon)" Date: Wed, 8 Aug 2018 14:10:22 -0400 Subject: [PATCH 130/207] ARTEMIS-2018 - Add bridge events to plugin API Add callbacks to handle bridge events including beforeDeliverBridge, afterDeliverBridge and afterAcknowledgeBridge --- .../core/server/cluster/impl/BridgeImpl.java | 14 +++- .../server/plugin/ActiveMQServerPlugin.java | 37 +++++++++- .../integration/plugin/CorePluginTest.java | 74 ++++++++++++++++--- .../plugin/MethodCalledVerifier.java | 22 ++++++ 4 files changed, 136 insertions(+), 11 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java index c811b6356cb..d2c886b0682 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java @@ -521,6 +521,10 @@ public void sendAcknowledged(final Message message) { ref.getQueue().acknowledge(ref); pendingAcks.countDown(); metrics.incrementMessagesAcknowledged(); + + if (server.hasBrokerPlugins()) { + server.callBrokerPlugins(plugin -> plugin.afterAcknowledgeBridge(this, ref)); + } } else { if (logger.isTraceEnabled()) { logger.trace("BridgeImpl::sendAcknowledged bridge " + this + " could not find reference for message " + message); @@ -614,13 +618,17 @@ public HandleStatus handle(final MessageReference ref) throws Exception { pendingAcks.countUp(); try { + if (server.hasBrokerPlugins()) { + server.callBrokerPlugins(plugin -> plugin.beforeDeliverBridge(this, ref)); + } + final HandleStatus status; if (message.isLargeMessage()) { deliveringLargeMessage = true; deliverLargeMessage(dest, ref, (LargeServerMessage) message); status = HandleStatus.HANDLED; } else { - status = deliverStandardMessage(dest, ref, message); + status = deliverStandardMessage(dest, ref, message); } //Only increment messages pending acknowledgement if handled by bridge @@ -628,6 +636,10 @@ public HandleStatus handle(final MessageReference ref) throws Exception { metrics.incrementMessagesPendingAcknowledgement(); } + if (server.hasBrokerPlugins()) { + server.callBrokerPlugins(plugin -> plugin.afterDeliverBridge(this, ref, status)); + } + return status; } catch (Exception e) { // If an exception happened, we must count down immediately diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java index 97e23b48fe3..704420e88b9 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java @@ -31,6 +31,7 @@ import org.apache.activemq.artemis.core.postoffice.RoutingStatus; import org.apache.activemq.artemis.core.security.SecurityAuth; import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.core.server.HandleStatus; import org.apache.activemq.artemis.core.server.MessageReference; import org.apache.activemq.artemis.core.server.Queue; import org.apache.activemq.artemis.core.server.QueueConfig; @@ -610,7 +611,6 @@ default void messageAcknowledged(MessageReference ref, AckReason reason, ServerC this.messageAcknowledged(ref, reason); } - /** * Before a bridge is deployed * @@ -631,6 +631,41 @@ default void afterDeployBridge(Bridge bridge) throws ActiveMQException { } + /** + * Called immediately before a bridge delivers a message + * + * @param bridge + * @param ref + * @throws ActiveMQException + */ + default void beforeDeliverBridge(Bridge bridge, MessageReference ref) throws ActiveMQException { + + } + + /** + * Called immediately after a bridge delivers a message but before the message + * is acknowledged + * + * @param bridge + * @param ref + * @param status + * @throws ActiveMQException + */ + default void afterDeliverBridge(Bridge bridge, MessageReference ref, HandleStatus status) throws ActiveMQException { + + } + + /** + * Called after delivered message over this bridge has been acknowledged by the remote broker + * + * @param bridge + * @param ref + * @throws ActiveMQException + */ + default void afterAcknowledgeBridge(Bridge bridge, MessageReference ref) throws ActiveMQException { + + } + /** * A Critical failure has been detected. * This will be called before the broker is stopped diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/CorePluginTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/CorePluginTest.java index f83b3eef250..a236b8e3d89 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/CorePluginTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/CorePluginTest.java @@ -16,6 +16,7 @@ */ package org.apache.activemq.artemis.tests.integration.plugin; +import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_ACKNOWLEDGE_BRIDGE; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_ADD_ADDRESS; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_ADD_BINDING; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_CLOSE_CONSUMER; @@ -25,6 +26,7 @@ import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_CREATE_QUEUE; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_CREATE_SESSION; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_DELIVER; +import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_DELIVER_BRIDGE; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_DEPLOY_BRIDGE; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_DESTROY_CONNECTION; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.AFTER_DESTROY_QUEUE; @@ -42,6 +44,7 @@ import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.BEFORE_CREATE_QUEUE; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.BEFORE_CREATE_SESSION; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.BEFORE_DELIVER; +import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.BEFORE_DELIVER_BRIDGE; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.BEFORE_DEPLOY_BRIDGE; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.BEFORE_DESTROY_QUEUE; import static org.apache.activemq.artemis.tests.integration.plugin.MethodCalledVerifier.BEFORE_MESSAGE_ROUTE; @@ -70,6 +73,13 @@ import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.TransportConfiguration; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; +import org.apache.activemq.artemis.api.core.client.ClientConsumer; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientProducer; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; +import org.apache.activemq.artemis.api.core.client.ServerLocator; import org.apache.activemq.artemis.core.config.BridgeConfiguration; import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.CoreQueueConfiguration; @@ -78,10 +88,12 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.MessageReference; import org.apache.activemq.artemis.core.server.ServerConsumer; +import org.apache.activemq.artemis.core.server.cluster.impl.BridgeMetrics; import org.apache.activemq.artemis.core.server.impl.AckReason; import org.apache.activemq.artemis.core.server.impl.AddressInfo; import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerPlugin; import org.apache.activemq.artemis.tests.util.JMSTestBase; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -285,6 +297,7 @@ public void testSimpleBridge() throws Exception { Map server0Params = new HashMap<>(); server0 = createClusteredServerWithParams(false, 0, false, server0Params); + server0.registerBrokerPlugin(verifier); Map server1Params = new HashMap<>(); server1Params.put(TransportConstants.SERVER_ID_PROP_NAME, 1); @@ -293,23 +306,21 @@ public void testSimpleBridge() throws Exception { final String testAddress = "testAddress"; final String queueName0 = "queue0"; final String forwardAddress = "forwardAddress"; + final String queueName1 = "queue1"; + TransportConfiguration server0tc = new TransportConfiguration(INVM_CONNECTOR_FACTORY, server0Params); TransportConfiguration server1tc = new TransportConfiguration(INVM_CONNECTOR_FACTORY, server1Params); HashMap connectors = new HashMap<>(); connectors.put(server1tc.getName(), server1tc); server0.getConfiguration().setConnectorConfigurations(connectors); - server0.registerBrokerPlugin(verifier); + + final int messageSize = 1024; + final int numMessages = 10; ArrayList connectorConfig = new ArrayList<>(); connectorConfig.add(server1tc.getName()); - BridgeConfiguration bridgeConfiguration = new BridgeConfiguration().setName("bridge1") - .setQueueName(queueName0) - .setForwardingAddress(forwardAddress) - .setRetryInterval(1000) - .setReconnectAttemptsOnSameNode(-1) - .setUseDuplicateDetection(false) - .setStaticConnectors(connectorConfig); + BridgeConfiguration bridgeConfiguration = new BridgeConfiguration().setName("bridge1").setQueueName(queueName0).setForwardingAddress(forwardAddress).setRetryInterval(1000).setReconnectAttemptsOnSameNode(-1).setUseDuplicateDetection(false).setConfirmationWindowSize(numMessages * messageSize / 2).setStaticConnectors(connectorConfig); List bridgeConfigs = new ArrayList<>(); bridgeConfigs.add(bridgeConfiguration); @@ -320,14 +331,59 @@ public void testSimpleBridge() throws Exception { queueConfigs0.add(queueConfig0); server0.getConfiguration().setQueueConfigurations(queueConfigs0); + CoreQueueConfiguration queueConfig1 = new CoreQueueConfiguration().setAddress(forwardAddress).setName(queueName1); + List queueConfigs1 = new ArrayList<>(); + queueConfigs1.add(queueConfig1); + server1.getConfiguration().setQueueConfigurations(queueConfigs1); + server1.start(); server0.start(); + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(server0tc, server1tc)); + ClientSessionFactory sf0 = addSessionFactory(locator.createSessionFactory(server0tc)); + ClientSessionFactory sf1 = addSessionFactory(locator.createSessionFactory(server1tc)); + + ClientSession session0 = sf0.createSession(false, true, true); + ClientSession session1 = sf1.createSession(false, true, true); + ClientProducer producer0 = session0.createProducer(new SimpleString(testAddress)); + ClientConsumer consumer1 = session1.createConsumer(queueName1); + session1.start(); + + final byte[] bytes = new byte[messageSize]; + final SimpleString propKey = new SimpleString("testkey"); + + for (int i = 0; i < numMessages; i++) { + ClientMessage message = session0.createMessage(true); + message.putIntProperty(propKey, i); + message.getBodyBuffer().writeBytes(bytes); + producer0.send(message); + } + + for (int i = 0; i < numMessages; i++) { + ClientMessage message = consumer1.receive(5000); + Assert.assertNotNull(message); + Assert.assertEquals(i, message.getObjectProperty(propKey)); + message.acknowledge(); + } + + Assert.assertNull(consumer1.receiveImmediate()); + session0.close(); + session1.close(); + + sf0.close(); + sf1.close(); + + assertEquals(1, server0.getClusterManager().getBridges().size()); + BridgeMetrics bridgeMetrics = server0.getClusterManager().getBridges().get("bridge1").getMetrics(); + assertEquals(10, bridgeMetrics.getMessagesPendingAcknowledgement()); + assertEquals(10, bridgeMetrics.getMessagesAcknowledged()); + + //verify plugin method calls verifier.validatePluginMethodsEquals(1, BEFORE_DEPLOY_BRIDGE, AFTER_DEPLOY_BRIDGE); + verifier.validatePluginMethodsEquals(10, BEFORE_DELIVER_BRIDGE, AFTER_DELIVER_BRIDGE, AFTER_ACKNOWLEDGE_BRIDGE); server0.stop(); server1.stop(); - } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/MethodCalledVerifier.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/MethodCalledVerifier.java index 8977ba5f06b..9c245059461 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/MethodCalledVerifier.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/MethodCalledVerifier.java @@ -24,6 +24,7 @@ import org.apache.activemq.artemis.core.postoffice.QueueBinding; import org.apache.activemq.artemis.core.postoffice.RoutingStatus; import org.apache.activemq.artemis.core.security.SecurityAuth; +import org.apache.activemq.artemis.core.server.HandleStatus; import org.apache.activemq.artemis.core.server.MessageReference; import org.apache.activemq.artemis.core.server.Queue; import org.apache.activemq.artemis.core.server.QueueConfig; @@ -95,6 +96,9 @@ public class MethodCalledVerifier implements ActiveMQServerPlugin { public static final String AFTER_DELIVER = "afterDeliver"; public static final String BEFORE_DEPLOY_BRIDGE = "beforeDeployBridge"; public static final String AFTER_DEPLOY_BRIDGE = "afterDeployBridge"; + public static final String BEFORE_DELIVER_BRIDGE = "beforeDeliverBridge"; + public static final String AFTER_DELIVER_BRIDGE = "afterDeliverBridge"; + public static final String AFTER_ACKNOWLEDGE_BRIDGE = "afterAcknowledgeBridge"; public MethodCalledVerifier(Map methodCalls) { super(); @@ -340,6 +344,24 @@ public void afterDeployBridge(Bridge bridge) { methodCalled(AFTER_DEPLOY_BRIDGE); } + @Override + public void beforeDeliverBridge(Bridge bridge, MessageReference ref) throws ActiveMQException { + Preconditions.checkNotNull(bridge); + methodCalled(BEFORE_DELIVER_BRIDGE); + } + + @Override + public void afterDeliverBridge(Bridge bridge, MessageReference ref, HandleStatus status) throws ActiveMQException { + Preconditions.checkNotNull(bridge); + methodCalled(AFTER_DELIVER_BRIDGE); + } + + @Override + public void afterAcknowledgeBridge(Bridge bridge, MessageReference ref) throws ActiveMQException { + Preconditions.checkNotNull(bridge); + methodCalled(AFTER_ACKNOWLEDGE_BRIDGE); + } + public void validatePluginMethodsEquals(int count, String... names) { Arrays.asList(names).forEach(name -> { try { From 5d8079845ee3fe1092b84533ae63438d79ad353c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Thu, 9 Aug 2018 15:54:32 +0100 Subject: [PATCH 131/207] ARTEMIS-1482 Enhance test to ensure len check is done before byte[] init Set the int to Integer.MAX_VALUE thus if the len check is not done before byte[] initialization the test would blow with an OOM. --- .../org/apache/activemq/artemis/utils/SimpleStringTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/SimpleStringTest.java b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/SimpleStringTest.java index 0498cab8a91..85e579498f4 100644 --- a/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/SimpleStringTest.java +++ b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/SimpleStringTest.java @@ -28,7 +28,7 @@ public class SimpleStringTest { @Test public void testOutOfBoundsThrownOnMalformedString() { ByteBuf byteBuffer = ByteBufAllocator.DEFAULT.buffer(5); - byteBuffer.writeInt(100); + byteBuffer.writeInt(Integer.MAX_VALUE); Exception e = null; try { From 26208b76c768b02c145b00674048fcb91ed5f92c Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Thu, 9 Aug 2018 12:22:43 -0400 Subject: [PATCH 132/207] ARTEMIS-2021 NetworkHealthCheck should only restart servers after net outages --- .../core/server/NetworkHealthCheck.java | 6 ++++- .../artemis/utils/NetworkHealthTest.java | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/core/server/NetworkHealthCheck.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/core/server/NetworkHealthCheck.java index c8f0d4d7c6e..75d823a6afc 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/core/server/NetworkHealthCheck.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/core/server/NetworkHealthCheck.java @@ -60,6 +60,8 @@ public class NetworkHealthCheck extends ActiveMQScheduledComponent { // To be used on tests. As we use the loopback as a valid address on tests. private boolean ignoreLoopback = false; + private boolean ownShutdown = false; + /** * The timeout to be used on isReachable */ @@ -274,7 +276,7 @@ public void run() { if (healthy) { for (ActiveMQComponent component : componentList) { - if (!component.isStarted()) { + if (!component.isStarted() && ownShutdown) { try { ActiveMQUtilLogger.LOGGER.startingService(component.toString()); component.start(); @@ -282,6 +284,7 @@ public void run() { ActiveMQUtilLogger.LOGGER.errorStartingComponent(e, component.toString()); } } + ownShutdown = false; } } else { for (ActiveMQComponent component : componentList) { @@ -289,6 +292,7 @@ public void run() { try { ActiveMQUtilLogger.LOGGER.stoppingService(component.toString()); component.stop(); + ownShutdown = true; } catch (Exception e) { ActiveMQUtilLogger.LOGGER.errorStoppingComponent(e, component.toString()); } diff --git a/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/NetworkHealthTest.java b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/NetworkHealthTest.java index de255253013..a654ff182c5 100644 --- a/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/NetworkHealthTest.java +++ b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/NetworkHealthTest.java @@ -142,6 +142,31 @@ public void testCheck6() throws Exception { Assert.assertTrue(check.check(address)); } + @Test + public void testAlreadyShutdown() throws Exception { + assumeTrue(purePingWorks(IPV6_LOCAL)); + ReusableLatch latch = new ReusableLatch(0); + NetworkHealthCheck check = addCheck(new NetworkHealthCheck(null, 100, 100) { + @Override + public void run() { + super.run(); + latch.countDown(); + System.out.println("Check"); + } + }); + check.addComponent(component); + InetAddress address = InetAddress.getByName("127.0.0.1"); + check.addAddress(address); + + component.stop(); + + latch.setCount(1); + Assert.assertTrue(latch.await(1, TimeUnit.MINUTES)); + + Assert.assertFalse("NetworkHealthCheck should have no business on restarting the component, the network was never down, hence no check needed!", component.isStarted()); + + } + @Test public void testParseSpaces() throws Exception { NetworkHealthCheck check = addCheck(new NetworkHealthCheck(null, 100, 100)); From 6bdfcd04f5f2e6a7e8a7dcfeebc6bc0ede89d0d8 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Thu, 9 Aug 2018 19:16:23 -0400 Subject: [PATCH 133/207] ARTEMIS-2021 setting flag before shutdown --- .../apache/activemq/artemis/core/server/NetworkHealthCheck.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/core/server/NetworkHealthCheck.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/core/server/NetworkHealthCheck.java index 75d823a6afc..ff4fd86abe0 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/core/server/NetworkHealthCheck.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/core/server/NetworkHealthCheck.java @@ -289,10 +289,10 @@ public void run() { } else { for (ActiveMQComponent component : componentList) { if (component.isStarted()) { + ownShutdown = true; try { ActiveMQUtilLogger.LOGGER.stoppingService(component.toString()); component.stop(); - ownShutdown = true; } catch (Exception e) { ActiveMQUtilLogger.LOGGER.errorStoppingComponent(e, component.toString()); } From 1171f01b30468abfa4d6f2de6401802ad59d4c4f Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 9 Aug 2018 11:20:46 -0500 Subject: [PATCH 134/207] ARTEMIS-2020 Use prefixes when useJNDI=false in RA --- .../artemis/ra/inflow/ActiveMQActivation.java | 4 +- .../integration/ra/ResourceAdapterTest.java | 65 +++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java index ede1b01ede4..3a893696bd3 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java @@ -572,10 +572,10 @@ protected void setupDestination() throws Exception { ActiveMQRALogger.LOGGER.instantiatingDestination(spec.getDestinationType(), spec.getDestination()); if (Topic.class.getName().equals(spec.getDestinationType())) { - destination = (ActiveMQDestination) ActiveMQJMSClient.createTopic(spec.getDestination()); + destination = (ActiveMQDestination) ActiveMQJMSClient.createTopic((spec.getTopicPrefix() == null ? "" : spec.getTopicPrefix()) + spec.getDestination()); isTopic = true; } else { - destination = (ActiveMQDestination) ActiveMQJMSClient.createQueue(spec.getDestination()); + destination = (ActiveMQDestination) ActiveMQJMSClient.createQueue((spec.getQueuePrefix() == null ? "" : spec.getQueuePrefix()) + spec.getDestination()); } } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ResourceAdapterTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ResourceAdapterTest.java index 811cc29dba9..6216bb354d6 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ResourceAdapterTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ResourceAdapterTest.java @@ -26,12 +26,17 @@ import java.util.concurrent.CountDownLatch; import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.UDPBroadcastEndpointFactory; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; import org.apache.activemq.artemis.api.core.client.ServerLocator; +import org.apache.activemq.artemis.api.core.management.AddressControl; +import org.apache.activemq.artemis.api.core.management.ResourceNames; import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.core.client.impl.ServerLocatorImpl; +import org.apache.activemq.artemis.core.server.impl.AddressInfo; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQDestination; import org.apache.activemq.artemis.ra.ActiveMQResourceAdapter; @@ -100,6 +105,66 @@ public void testStartStopActivationManyTimes() throws Exception { } + @Test + public void testQueuePrefixWhenUseJndiIsFalse() throws Exception { + final String prefix = "jms.queue."; + final String destinationName = "test"; + final SimpleString prefixedDestinationName = SimpleString.toSimpleString(prefix + destinationName); + server.createQueue(prefixedDestinationName, RoutingType.ANYCAST, prefixedDestinationName, null, false, false); + ActiveMQResourceAdapter ra = new ActiveMQResourceAdapter(); + ra.setConnectorClassName(INVM_CONNECTOR_FACTORY); + ra.start(new BootstrapContext()); + Connection conn = ra.getDefaultActiveMQConnectionFactory().createConnection(); + conn.close(); + + ActiveMQActivationSpec spec = new ActiveMQActivationSpec(); + spec.setResourceAdapter(ra); + spec.setUseJNDI(false); + spec.setDestinationType("javax.jms.Queue"); + spec.setDestination(destinationName); + spec.setQueuePrefix(prefix); + spec.setMaxSession(1); + spec.setSetupAttempts(1); + + ActiveMQActivation activation = new ActiveMQActivation(ra, new MessageEndpointFactory(), spec); + + activation.start(); + + assertEquals(1, server.locateQueue(prefixedDestinationName).getConsumerCount()); + + activation.stop(); + } + + @Test + public void testTopicPrefixWhenUseJndiIsFalse() throws Exception { + final String prefix = "jms.topic."; + final String destinationName = "test"; + final SimpleString prefixedDestinationName = SimpleString.toSimpleString(prefix + destinationName); + server.addAddressInfo(new AddressInfo(prefixedDestinationName).addRoutingType(RoutingType.MULTICAST)); + ActiveMQResourceAdapter ra = new ActiveMQResourceAdapter(); + ra.setConnectorClassName(INVM_CONNECTOR_FACTORY); + ra.start(new BootstrapContext()); + Connection conn = ra.getDefaultActiveMQConnectionFactory().createConnection(); + conn.close(); + + ActiveMQActivationSpec spec = new ActiveMQActivationSpec(); + spec.setResourceAdapter(ra); + spec.setUseJNDI(false); + spec.setDestinationType("javax.jms.Topic"); + spec.setDestination(destinationName); + spec.setTopicPrefix(prefix); + spec.setMaxSession(1); + spec.setSetupAttempts(1); + + ActiveMQActivation activation = new ActiveMQActivation(ra, new MessageEndpointFactory(), spec); + + activation.start(); + + assertEquals(1, ((AddressControl)server.getManagementService().getResource(ResourceNames.ADDRESS + prefixedDestinationName)).getQueueNames().length); + + activation.stop(); + } + @Test public void testStartStop() throws Exception { ActiveMQResourceAdapter qResourceAdapter = new ActiveMQResourceAdapter(); From 2d7c5322a79e0ffa45676e50f3453d5530af78c2 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Wed, 8 Aug 2018 11:10:04 -0500 Subject: [PATCH 135/207] ARTEMIS-2017 Eliminate LRUCache from SelectorParser The LRUCache is not thread-safe and it's usage in SelectorParser could cause growth beyond its configured max size. Instead of modifying the LRUCache to be thread-safe or synchronizing access to it in SelectorParser it should just be removed since it's not on a hot path. --- .../artemis/selector/impl/SelectorParser.java | 102 ++++++++---------- 1 file changed, 43 insertions(+), 59 deletions(-) diff --git a/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/impl/SelectorParser.java b/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/impl/SelectorParser.java index 6812f9ab0df..f169541efbd 100644 --- a/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/impl/SelectorParser.java +++ b/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/impl/SelectorParser.java @@ -24,77 +24,61 @@ import org.apache.activemq.artemis.selector.hyphenated.HyphenatedParser; import org.apache.activemq.artemis.selector.strict.StrictParser; -/** - */ public class SelectorParser { - private static final LRUCache cache = new LRUCache<>(100); private static final String CONVERT_STRING_EXPRESSIONS_PREFIX = "convert_string_expressions:"; private static final String HYPHENATED_PROPS_PREFIX = "hyphenated_props:"; private static final String NO_CONVERT_STRING_EXPRESSIONS_PREFIX = "no_convert_string_expressions:"; private static final String NO_HYPHENATED_PROPS_PREFIX = "no_hyphenated_props:"; public static BooleanExpression parse(String sql) throws FilterException { - Object result = cache.get(sql); - if (result instanceof FilterException) { - throw (FilterException) result; - } else if (result instanceof BooleanExpression) { - return (BooleanExpression) result; - } else { - String actual = sql; - boolean convertStringExpressions = false; - boolean hyphenatedProps = false; - while (true) { - if (actual.startsWith(CONVERT_STRING_EXPRESSIONS_PREFIX)) { - convertStringExpressions = true; - actual = actual.substring(CONVERT_STRING_EXPRESSIONS_PREFIX.length()); - continue; - } - if (actual.startsWith(HYPHENATED_PROPS_PREFIX)) { - hyphenatedProps = true; - actual = actual.substring(HYPHENATED_PROPS_PREFIX.length()); - continue; - } - if (actual.startsWith(NO_CONVERT_STRING_EXPRESSIONS_PREFIX)) { - convertStringExpressions = false; - actual = actual.substring(NO_CONVERT_STRING_EXPRESSIONS_PREFIX.length()); - continue; - } - if (actual.startsWith(NO_HYPHENATED_PROPS_PREFIX)) { - hyphenatedProps = false; - actual = actual.substring(NO_HYPHENATED_PROPS_PREFIX.length()); - continue; - } - break; + String actual = sql; + boolean convertStringExpressions = false; + boolean hyphenatedProps = false; + while (true) { + if (actual.startsWith(CONVERT_STRING_EXPRESSIONS_PREFIX)) { + convertStringExpressions = true; + actual = actual.substring(CONVERT_STRING_EXPRESSIONS_PREFIX.length()); + continue; } - - if (convertStringExpressions) { - ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true); + if (actual.startsWith(HYPHENATED_PROPS_PREFIX)) { + hyphenatedProps = true; + actual = actual.substring(HYPHENATED_PROPS_PREFIX.length()); + continue; } - try { - BooleanExpression e = null; - if (hyphenatedProps) { - HyphenatedParser parser = new HyphenatedParser(new StringReader(actual)); - e = parser.JmsSelector(); - } else { - StrictParser parser = new StrictParser(new StringReader(actual)); - e = parser.JmsSelector(); - } - cache.put(sql, e); - return e; - } catch (Throwable e) { - FilterException fe = new FilterException(actual, e); - cache.put(sql, fe); - throw fe; - } finally { - if (convertStringExpressions) { - ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove(); - } + if (actual.startsWith(NO_CONVERT_STRING_EXPRESSIONS_PREFIX)) { + convertStringExpressions = false; + actual = actual.substring(NO_CONVERT_STRING_EXPRESSIONS_PREFIX.length()); + continue; } + if (actual.startsWith(NO_HYPHENATED_PROPS_PREFIX)) { + hyphenatedProps = false; + actual = actual.substring(NO_HYPHENATED_PROPS_PREFIX.length()); + continue; + } + break; } - } - public static void clearCache() { - cache.clear(); + if (convertStringExpressions) { + ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true); + } + try { + BooleanExpression e = null; + if (hyphenatedProps) { + HyphenatedParser parser = new HyphenatedParser(new StringReader(actual)); + e = parser.JmsSelector(); + } else { + StrictParser parser = new StrictParser(new StringReader(actual)); + e = parser.JmsSelector(); + } + return e; + } catch (Throwable e) { + FilterException fe = new FilterException(actual, e); + throw fe; + } finally { + if (convertStringExpressions) { + ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove(); + } + } } } From e15f3901e4e449ff347ac004ac3c6034ca0e7a0b Mon Sep 17 00:00:00 2001 From: Howard Gao Date: Tue, 7 Aug 2018 16:56:43 +0800 Subject: [PATCH 136/207] ARTEMIS-2013 Can't create durable subscriber to a composite topic An OpenWire client can use a compound destination name of the form "a,b,c..." and consume from, or subscribe to, multiple destinations. Such a compound destination only works for topics when the subscriber is non-durable. Attempting to create a durable subscription on a compound address will end up with an error. The cause is when creating durable subs to multiple topics/addresses the broker uses the same name to create internal queues, which causes duplicate name conflict. --- .../protocol/openwire/amq/AMQConsumer.java | 3 + .../openwire/CompositeDestinationTest.java | 75 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/CompositeDestinationTest.java diff --git a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/amq/AMQConsumer.java b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/amq/AMQConsumer.java index 7e9881b7e3d..fae6ef7ef3d 100644 --- a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/amq/AMQConsumer.java +++ b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/amq/AMQConsumer.java @@ -186,6 +186,9 @@ private SimpleString createTopicSubscription(boolean isDurable, addressInfo.setInternal(internalAddress); if (isDurable) { queueName = org.apache.activemq.artemis.jms.client.ActiveMQDestination.createQueueNameForSubscription(true, clientID, subscriptionName); + if (info.getDestination().isComposite()) { + queueName = queueName.concat(physicalName); + } QueueQueryResult result = session.getCoreSession().executeQueueQuery(queueName); if (result.isExists()) { // Already exists diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/CompositeDestinationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/CompositeDestinationTest.java new file mode 100644 index 00000000000..dd2606825b3 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/CompositeDestinationTest.java @@ -0,0 +1,75 @@ +/* + * 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.artemis.tests.integration.openwire; + +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.server.impl.AddressInfo; +import org.junit.Before; +import org.junit.Test; + +import javax.jms.Connection; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicSubscriber; + +public class CompositeDestinationTest extends BasicOpenWireTest { + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + AddressInfo addressInfo = new AddressInfo(new SimpleString("p.IN"), RoutingType.MULTICAST); + this.server.addAddressInfo(addressInfo); + addressInfo = new AddressInfo(new SimpleString("q.IN"), RoutingType.MULTICAST); + this.server.addAddressInfo(addressInfo); + } + + @Test + public void testDurableSub() throws Exception { + Connection conn = factory.createConnection(); + try { + conn.setClientID("my-client"); + conn.start(); + + Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Topic topic = session.createTopic("p.IN,q.IN"); + + TopicSubscriber sub1 = session.createDurableSubscriber(topic, "durable1", null, false); + + MessageProducer producer = session.createProducer(topic); + + final int num = 10; + + for (int i = 0; i < num; i++) { + producer.send(session.createTextMessage("msg" + i)); + } + + int count = 0; + TextMessage msg = (TextMessage) sub1.receive(2000); + while (msg != null) { + count++; + msg = (TextMessage) sub1.receive(2000); + } + assertEquals("Consumer should receive all messages from every topic", 2 * num, count); + } finally { + conn.close(); + } + } +} From 53f8bc3daff9da95aba406c72e706177c28a9012 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 3 Aug 2018 16:39:09 -0500 Subject: [PATCH 137/207] ARTEMIS-2010 actively detect unauthenticated LDAP Bind requests --- .../core/security/jaas/LDAPLoginModule.java | 15 ++++-- .../security/jaas/LDAPLoginModuleTest.java | 51 +++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java index 7d58a0b97be..14700405845 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java @@ -167,7 +167,7 @@ public boolean login() throws LoginException { throw (LoginException) new LoginException().initCause(e); } - String password; + String password = null; username = ((NameCallback) callbacks[0]).getName(); if (username == null) @@ -175,8 +175,17 @@ public boolean login() throws LoginException { if (((PasswordCallback) callbacks[1]).getPassword() != null) password = new String(((PasswordCallback) callbacks[1]).getPassword()); - else - password = ""; + + /** + * https://tools.ietf.org/html/rfc4513#section-6.3.1 + * + * Clients that use the results from a simple Bind operation to make + * authorization decisions should actively detect unauthenticated Bind + * requests (by verifying that the supplied password is not empty) and + * react appropriately. + */ + if (password == null || (password != null && password.length() == 0)) + throw new FailedLoginException("Password cannot be null or empty"); // authenticate will throw LoginException // in case of failed authentication diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java index 47bedd9b079..b52a717ba96 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java @@ -27,6 +27,7 @@ import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; @@ -268,6 +269,56 @@ public void testPropertyConfigMap() throws Exception { } } + @Test + public void testEmptyPassword() throws Exception { + LoginContext context = new LoginContext("LDAPLogin", new CallbackHandler() { + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + try { + context.login(); + fail("Should have thrown a FailedLoginException"); + } catch (FailedLoginException fle) { + assertEquals("Password cannot be null or empty", fle.getMessage()); + } + context.logout(); + } + + @Test + public void testNullPassword() throws Exception { + LoginContext context = new LoginContext("LDAPLogin", new CallbackHandler() { + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword(null); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + try { + context.login(); + fail("Should have thrown a FailedLoginException"); + } catch (FailedLoginException fle) { + assertEquals("Password cannot be null or empty", fle.getMessage()); + } + context.logout(); + } + private boolean presentInArray(LDAPLoginProperty[] ldapProps, String propertyName) { for (LDAPLoginProperty conf : ldapProps) { if (conf.getPropertyName().equals(propertyName) && (conf.getPropertyValue() != null && !"".equals(conf.getPropertyValue()))) From 19e1bbeb49ced9a8f7b5be09148ba27b53af3172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Thu, 9 Aug 2018 13:43:45 +0100 Subject: [PATCH 138/207] ARTEMIS-2019 - Seperate ServerPlugin Interfaces Seperate plugin interface by area, all extending a base interface. Update code to check and call only plugins implementing specific interfaces. Existing interface extends all the new interfaces for back compatibility or those who want simplicity and don't care about perf. --- .../artemis/core/config/Configuration.java | 64 +- .../core/config/impl/ConfigurationImpl.java | 131 +++- .../core/postoffice/impl/PostOfficeImpl.java | 48 +- .../server/impl/RemotingServiceImpl.java | 8 +- .../artemis/core/server/ActiveMQServer.java | 73 +- .../core/server/cluster/ClusterManager.java | 8 +- .../core/server/cluster/impl/BridgeImpl.java | 12 +- .../core/server/impl/ActiveMQServerImpl.java | 203 +++++- .../artemis/core/server/impl/QueueImpl.java | 12 +- .../core/server/impl/ServerConsumerImpl.java | 16 +- .../core/server/impl/ServerSessionImpl.java | 36 +- .../server/plugin/ActiveMQPluginRunnable.java | 4 +- .../plugin/ActiveMQServerAddressPlugin.java | 95 +++ .../plugin/ActiveMQServerBasePlugin.java | 52 ++ .../plugin/ActiveMQServerBindingPlugin.java | 73 ++ .../plugin/ActiveMQServerBridgePlugin.java | 85 +++ .../ActiveMQServerConnectionPlugin.java | 49 ++ .../plugin/ActiveMQServerConsumerPlugin.java | 97 +++ .../plugin/ActiveMQServerCriticalPlugin.java | 36 + .../plugin/ActiveMQServerMessagePlugin.java | 236 ++++++ .../server/plugin/ActiveMQServerPlugin.java | 673 +----------------- .../plugin/ActiveMQServerQueuePlugin.java | 81 +++ .../plugin/ActiveMQServerSessionPlugin.java | 126 ++++ .../config/impl/FileConfigurationTest.java | 3 +- 24 files changed, 1450 insertions(+), 771 deletions(-) create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerAddressPlugin.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBasePlugin.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBindingPlugin.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBridgePlugin.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerConnectionPlugin.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerConsumerPlugin.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerCriticalPlugin.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerMessagePlugin.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerQueuePlugin.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerSessionPlugin.java diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java index 5d9c2eba22d..b1c49c3a4d0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java @@ -23,6 +23,16 @@ import java.util.Properties; import java.util.Set; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerAddressPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBasePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBindingPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBridgePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerConnectionPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerConsumerPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerCriticalPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerMessagePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerQueuePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerSessionPlugin; import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy; import org.apache.activemq.artemis.api.core.BroadcastGroupConfiguration; import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration; @@ -33,7 +43,6 @@ import org.apache.activemq.artemis.core.server.JournalType; import org.apache.activemq.artemis.core.server.SecuritySettingPlugin; import org.apache.activemq.artemis.core.server.group.impl.GroupingHandlerConfiguration; -import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerPlugin; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings; @@ -1129,20 +1138,65 @@ default boolean isJDBC() { /** * @param plugins */ - void registerBrokerPlugins(List plugins); + void registerBrokerPlugins(List plugins); /** * @param plugin */ - void registerBrokerPlugin(ActiveMQServerPlugin plugin); + void registerBrokerPlugin(ActiveMQServerBasePlugin plugin); /** * @param plugin */ - void unRegisterBrokerPlugin(ActiveMQServerPlugin plugin); + void unRegisterBrokerPlugin(ActiveMQServerBasePlugin plugin); /** * @return */ - List getBrokerPlugins(); + List getBrokerPlugins(); + + /** + * @return + */ + List getBrokerConnectionPlugins(); + + /** + * @return + */ + List getBrokerSessionPlugins(); + + /** + * @return + */ + List getBrokerConsumerPlugins(); + + /** + * @return + */ + List getBrokerAddressPlugins(); + + /** + * @return + */ + List getBrokerQueuePlugins(); + + /** + * @return + */ + List getBrokerBindingPlugins(); + + /** + * @return + */ + List getBrokerMessagePlugins(); + + /** + * @return + */ + List getBrokerBridgePlugins(); + + /** + * @return + */ + List getBrokerCriticalPlugins(); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java index ae4a25f5d2e..3e51d63dd28 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java @@ -42,6 +42,16 @@ import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.core.config.storage.DatabaseStorageConfiguration; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerAddressPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBasePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBindingPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBridgePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerConnectionPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerConsumerPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerCriticalPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerMessagePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerQueuePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerSessionPlugin; import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy; import org.apache.activemq.artemis.api.core.BroadcastGroupConfiguration; import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration; @@ -66,7 +76,6 @@ import org.apache.activemq.artemis.core.server.NetworkHealthCheck; import org.apache.activemq.artemis.core.server.SecuritySettingPlugin; import org.apache.activemq.artemis.core.server.group.impl.GroupingHandlerConfiguration; -import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerPlugin; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings; import org.apache.activemq.artemis.utils.Env; @@ -244,7 +253,16 @@ public class ConfigurationImpl implements Configuration, Serializable { private List securitySettingPlugins = new ArrayList<>(); - private final List brokerPlugins = new CopyOnWriteArrayList<>(); + private final List brokerPlugins = new CopyOnWriteArrayList<>(); + private final List brokerConnectionPlugins = new CopyOnWriteArrayList<>(); + private final List brokerSessionPlugins = new CopyOnWriteArrayList<>(); + private final List brokerConsumerPlugins = new CopyOnWriteArrayList<>(); + private final List brokerAddressPlugins = new CopyOnWriteArrayList<>(); + private final List brokerQueuePlugins = new CopyOnWriteArrayList<>(); + private final List brokerBindingPlugins = new CopyOnWriteArrayList<>(); + private final List brokerMessagePlugins = new CopyOnWriteArrayList<>(); + private final List brokerBridgePlugins = new CopyOnWriteArrayList<>(); + private final List brokerCriticalPlugins = new CopyOnWriteArrayList<>(); private Map> securityRoleNameMappings = new HashMap<>(); @@ -1371,25 +1389,124 @@ public List getSecuritySettingPlugins() { } @Override - public void registerBrokerPlugins(final List plugins) { - brokerPlugins.addAll(plugins); + public void registerBrokerPlugins(final List plugins) { + plugins.forEach(plugin -> registerBrokerPlugin(plugin)); } @Override - public void registerBrokerPlugin(final ActiveMQServerPlugin plugin) { + public void registerBrokerPlugin(final ActiveMQServerBasePlugin plugin) { brokerPlugins.add(plugin); + if (plugin instanceof ActiveMQServerConnectionPlugin) { + brokerConnectionPlugins.add((ActiveMQServerConnectionPlugin) plugin); + } + if (plugin instanceof ActiveMQServerSessionPlugin) { + brokerSessionPlugins.add((ActiveMQServerSessionPlugin) plugin); + } + if (plugin instanceof ActiveMQServerConsumerPlugin) { + brokerConsumerPlugins.add((ActiveMQServerConsumerPlugin) plugin); + } + if (plugin instanceof ActiveMQServerAddressPlugin) { + brokerAddressPlugins.add((ActiveMQServerAddressPlugin) plugin); + } + if (plugin instanceof ActiveMQServerQueuePlugin) { + brokerQueuePlugins.add((ActiveMQServerQueuePlugin) plugin); + } + if (plugin instanceof ActiveMQServerBindingPlugin) { + brokerBindingPlugins.add((ActiveMQServerBindingPlugin) plugin); + } + if (plugin instanceof ActiveMQServerMessagePlugin) { + brokerMessagePlugins.add((ActiveMQServerMessagePlugin) plugin); + } + if (plugin instanceof ActiveMQServerBridgePlugin) { + brokerBridgePlugins.add((ActiveMQServerBridgePlugin) plugin); + } + if (plugin instanceof ActiveMQServerCriticalPlugin) { + brokerCriticalPlugins.add((ActiveMQServerCriticalPlugin) plugin); + } } @Override - public void unRegisterBrokerPlugin(final ActiveMQServerPlugin plugin) { + public void unRegisterBrokerPlugin(final ActiveMQServerBasePlugin plugin) { brokerPlugins.remove(plugin); + if (plugin instanceof ActiveMQServerConnectionPlugin) { + brokerConnectionPlugins.remove(plugin); + } + if (plugin instanceof ActiveMQServerSessionPlugin) { + brokerSessionPlugins.remove(plugin); + } + if (plugin instanceof ActiveMQServerConsumerPlugin) { + brokerConsumerPlugins.remove(plugin); + } + if (plugin instanceof ActiveMQServerAddressPlugin) { + brokerAddressPlugins.remove(plugin); + } + if (plugin instanceof ActiveMQServerQueuePlugin) { + brokerQueuePlugins.remove(plugin); + } + if (plugin instanceof ActiveMQServerBindingPlugin) { + brokerBindingPlugins.remove(plugin); + } + if (plugin instanceof ActiveMQServerMessagePlugin) { + brokerMessagePlugins.remove(plugin); + } + if (plugin instanceof ActiveMQServerBridgePlugin) { + brokerBridgePlugins.remove(plugin); + } + if (plugin instanceof ActiveMQServerCriticalPlugin) { + brokerCriticalPlugins.remove(plugin); + } } @Override - public List getBrokerPlugins() { + public List getBrokerPlugins() { return brokerPlugins; } + @Override + public List getBrokerConnectionPlugins() { + return brokerConnectionPlugins; + } + + @Override + public List getBrokerSessionPlugins() { + return brokerSessionPlugins; + } + + @Override + public List getBrokerConsumerPlugins() { + return brokerConsumerPlugins; + } + + @Override + public List getBrokerAddressPlugins() { + return brokerAddressPlugins; + } + + @Override + public List getBrokerQueuePlugins() { + return brokerQueuePlugins; + } + + @Override + public List getBrokerBindingPlugins() { + return brokerBindingPlugins; + } + + @Override + public List getBrokerMessagePlugins() { + return brokerMessagePlugins; + } + + @Override + public List getBrokerBridgePlugins() { + return brokerBridgePlugins; + } + + @Override + public List getBrokerCriticalPlugins() { + return brokerCriticalPlugins; + } + @Override public File getBrokerInstance() { if (artemisInstance != null) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index 247b9ed9bfb..9a3e84478f0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -435,8 +435,8 @@ public boolean addAddressInfo(AddressInfo addressInfo) throws Exception { private boolean internalAddressInfo(AddressInfo addressInfo, boolean reload) throws Exception { synchronized (addressLock) { - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeAddAddress(addressInfo, reload)); + if (server.hasBrokerAddressPlugins()) { + server.callBrokerAddressPlugins(plugin -> plugin.beforeAddAddress(addressInfo, reload)); } boolean result; @@ -451,8 +451,8 @@ private boolean internalAddressInfo(AddressInfo addressInfo, boolean reload) thr if (!addressInfo.isInternal()) { managementService.registerAddress(addressInfo); } - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterAddAddress(addressInfo, reload)); + if (server.hasBrokerAddressPlugins()) { + server.callBrokerAddressPlugins(plugin -> plugin.afterAddAddress(addressInfo, reload)); } } catch (Exception e) { e.printStackTrace(); @@ -552,13 +552,13 @@ public QueueBinding updateQueue(SimpleString name, public AddressInfo updateAddressInfo(SimpleString addressName, EnumSet routingTypes) throws Exception { synchronized (addressLock) { - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeUpdateAddress(addressName, routingTypes)); + if (server.hasBrokerAddressPlugins()) { + server.callBrokerAddressPlugins(plugin -> plugin.beforeUpdateAddress(addressName, routingTypes)); } final AddressInfo address = addressManager.updateAddressInfo(addressName, routingTypes); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterUpdateAddress(address)); + if (server.hasBrokerAddressPlugins()) { + server.callBrokerAddressPlugins(plugin -> plugin.afterUpdateAddress(address)); } return address; @@ -574,8 +574,8 @@ public AddressInfo removeAddressInfo(SimpleString address) throws Exception { @Override public AddressInfo removeAddressInfo(SimpleString address, boolean force) throws Exception { synchronized (addressLock) { - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeRemoveAddress(address)); + if (server.hasBrokerAddressPlugins()) { + server.callBrokerAddressPlugins(plugin -> plugin.beforeRemoveAddress(address)); } final Bindings bindingsForAddress = getDirectBindings(address); @@ -593,8 +593,8 @@ public AddressInfo removeAddressInfo(SimpleString address, boolean force) throws } managementService.unregisterAddress(address); final AddressInfo addressInfo = addressManager.removeAddressInfo(address); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterRemoveAddress(address, addressInfo)); + if (server.hasBrokerAddressPlugins()) { + server.callBrokerAddressPlugins(plugin -> plugin.afterRemoveAddress(address, addressInfo)); } return addressInfo; @@ -628,8 +628,8 @@ public List listQueuesForAddress(SimpleString address) throws Exception { // even though failover is complete @Override public synchronized void addBinding(final Binding binding) throws Exception { - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeAddBinding(binding)); + if (server.hasBrokerBindingPlugins()) { + server.callBrokerBindingPlugins(plugin -> plugin.beforeAddBinding(binding)); } addressManager.addBinding(binding); @@ -662,8 +662,8 @@ public synchronized void addBinding(final Binding binding) throws Exception { managementService.sendNotification(new Notification(uid, CoreNotificationType.BINDING_ADDED, props)); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterAddBinding(binding)); + if (server.hasBrokerBindingPlugins()) { + server.callBrokerBindingPlugins(plugin -> plugin.afterAddBinding(binding)); } } @@ -673,8 +673,8 @@ public synchronized Binding removeBinding(final SimpleString uniqueName, Transaction tx, boolean deleteData) throws Exception { - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeRemoveBinding(uniqueName, tx, deleteData)); + if (server.hasBrokerBindingPlugins()) { + server.callBrokerBindingPlugins(plugin -> plugin.beforeRemoveBinding(uniqueName, tx, deleteData)); } addressSettingsRepository.clearCache(); @@ -722,8 +722,8 @@ public synchronized Binding removeBinding(final SimpleString uniqueName, binding.close(); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterRemoveBinding(binding, tx, deleteData) ); + if (server.hasBrokerBindingPlugins()) { + server.callBrokerBindingPlugins(plugin -> plugin.afterRemoveBinding(binding, tx, deleteData) ); } return binding; @@ -869,8 +869,8 @@ public RoutingStatus route(final Message message, } } - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeMessageRoute(message, context, direct, rejectDuplicates)); + if (server.hasBrokerMessagePlugins()) { + server.callBrokerMessagePlugins(plugin -> plugin.beforeMessageRoute(message, context, direct, rejectDuplicates)); } if (logger.isTraceEnabled()) { @@ -935,8 +935,8 @@ public RoutingStatus route(final Message message, context.getTransaction().commit(); } - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterMessageRoute(message, context, direct, rejectDuplicates, result)); + if (server.hasBrokerMessagePlugins()) { + server.callBrokerMessagePlugins(plugin -> plugin.afterMessageRoute(message, context, direct, rejectDuplicates, result)); } return result; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/server/impl/RemotingServiceImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/server/impl/RemotingServiceImpl.java index 5a1ddefae38..09d67ad0754 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/server/impl/RemotingServiceImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/server/impl/RemotingServiceImpl.java @@ -515,8 +515,8 @@ public void connectionCreated(final ActiveMQComponent component, ConnectionEntry entry = protocol.createConnectionEntry((Acceptor) component, connection); try { - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterCreateConnection(entry.connection)); + if (server.hasBrokerConnectionPlugins()) { + server.callBrokerConnectionPlugins(plugin -> plugin.afterCreateConnection(entry.connection)); } } catch (ActiveMQException t) { logger.warn("Error executing afterCreateConnection plugin method: {}", t.getMessage(), t); @@ -549,8 +549,8 @@ private void issueFailure(Object connectionID, ActiveMQException e) { RemotingConnection removedConnection = removeConnection(connectionID); if (removedConnection != null) { try { - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterDestroyConnection(removedConnection)); + if (server.hasBrokerConnectionPlugins()) { + server.callBrokerConnectionPlugins(plugin -> plugin.afterDestroyConnection(removedConnection)); } } catch (ActiveMQException t) { logger.warn("Error executing afterDestroyConnection plugin method: {}", t.getMessage(), t); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java index e15feb4a56f..1883362eaf6 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java @@ -52,7 +52,16 @@ import org.apache.activemq.artemis.core.server.impl.ConnectorsService; import org.apache.activemq.artemis.core.server.management.ManagementService; import org.apache.activemq.artemis.core.server.plugin.ActiveMQPluginRunnable; -import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerAddressPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBasePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBindingPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBridgePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerConnectionPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerConsumerPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerCriticalPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerMessagePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerQueuePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerSessionPlugin; import org.apache.activemq.artemis.core.server.reload.ReloadManager; import org.apache.activemq.artemis.core.settings.HierarchicalRepository; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; @@ -225,18 +234,72 @@ void destroyQueue(SimpleString queueName, */ void callPostQueueDeletionCallbacks(SimpleString address, SimpleString queueName) throws Exception; - void registerBrokerPlugin(ActiveMQServerPlugin plugin); + void registerBrokerPlugin(ActiveMQServerBasePlugin plugin); - void unRegisterBrokerPlugin(ActiveMQServerPlugin plugin); + void unRegisterBrokerPlugin(ActiveMQServerBasePlugin plugin); - void registerBrokerPlugins(List plugins); + void registerBrokerPlugins(List plugins); - List getBrokerPlugins(); + List getBrokerPlugins(); + + List getBrokerConnectionPlugins(); + + List getBrokerSessionPlugins(); + + List getBrokerConsumerPlugins(); + + List getBrokerAddressPlugins(); + + List getBrokerQueuePlugins(); + + List getBrokerBindingPlugins(); + + List getBrokerMessagePlugins(); + + List getBrokerBridgePlugins(); + + List getBrokerCriticalPlugins(); void callBrokerPlugins(ActiveMQPluginRunnable pluginRun) throws ActiveMQException; + void callBrokerConnectionPlugins(ActiveMQPluginRunnable pluginRun) throws ActiveMQException; + + void callBrokerSessionPlugins(ActiveMQPluginRunnable pluginRun) throws ActiveMQException; + + void callBrokerConsumerPlugins(ActiveMQPluginRunnable pluginRun) throws ActiveMQException; + + void callBrokerAddressPlugins(ActiveMQPluginRunnable pluginRun) throws ActiveMQException; + + void callBrokerQueuePlugins(ActiveMQPluginRunnable pluginRun) throws ActiveMQException; + + void callBrokerBindingPlugins(ActiveMQPluginRunnable pluginRun) throws ActiveMQException; + + void callBrokerMessagePlugins(ActiveMQPluginRunnable pluginRun) throws ActiveMQException; + + void callBrokerBridgePlugins(ActiveMQPluginRunnable pluginRun) throws ActiveMQException; + + void callBrokerCriticalPlugins(ActiveMQPluginRunnable pluginRun) throws ActiveMQException; + boolean hasBrokerPlugins(); + boolean hasBrokerConnectionPlugins(); + + boolean hasBrokerSessionPlugins(); + + boolean hasBrokerConsumerPlugins(); + + boolean hasBrokerAddressPlugins(); + + boolean hasBrokerQueuePlugins(); + + boolean hasBrokerBindingPlugins(); + + boolean hasBrokerMessagePlugins(); + + boolean hasBrokerBridgePlugins(); + + boolean hasBrokerCriticalPlugins(); + void checkQueueCreationLimit(String username) throws Exception; ServerSession createSession(String name, diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ClusterManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ClusterManager.java index 82e7b8d59ac..a3540332275 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ClusterManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ClusterManager.java @@ -406,8 +406,8 @@ public synchronized void deployBridge(final BridgeConfiguration config) throws E return; } - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeDeployBridge(config)); + if (server.hasBrokerBridgePlugins()) { + server.callBrokerBridgePlugins(plugin -> plugin.beforeDeployBridge(config)); } Queue queue = (Queue) binding.getBindable(); @@ -483,8 +483,8 @@ public synchronized void deployBridge(final BridgeConfiguration config) throws E bridge.start(); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterDeployBridge(bridge)); + if (server.hasBrokerBridgePlugins()) { + server.callBrokerBridgePlugins(plugin -> plugin.afterDeployBridge(bridge)); } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java index d2c886b0682..20b5ac96be9 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/impl/BridgeImpl.java @@ -522,8 +522,8 @@ public void sendAcknowledged(final Message message) { pendingAcks.countDown(); metrics.incrementMessagesAcknowledged(); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterAcknowledgeBridge(this, ref)); + if (server.hasBrokerBridgePlugins()) { + server.callBrokerBridgePlugins(plugin -> plugin.afterAcknowledgeBridge(this, ref)); } } else { if (logger.isTraceEnabled()) { @@ -618,8 +618,8 @@ public HandleStatus handle(final MessageReference ref) throws Exception { pendingAcks.countUp(); try { - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeDeliverBridge(this, ref)); + if (server.hasBrokerBridgePlugins()) { + server.callBrokerBridgePlugins(plugin -> plugin.beforeDeliverBridge(this, ref)); } final HandleStatus status; @@ -636,8 +636,8 @@ public HandleStatus handle(final MessageReference ref) throws Exception { metrics.incrementMessagesPendingAcknowledgement(); } - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterDeliverBridge(this, ref, status)); + if (server.hasBrokerBridgePlugins()) { + server.callBrokerBridgePlugins(plugin -> plugin.afterDeliverBridge(this, ref, status)); } return status; 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 a71a862b5ab..a8e64477a74 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 @@ -149,7 +149,16 @@ import org.apache.activemq.artemis.core.server.management.ManagementService; import org.apache.activemq.artemis.core.server.management.impl.ManagementServiceImpl; import org.apache.activemq.artemis.core.server.plugin.ActiveMQPluginRunnable; -import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerAddressPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBasePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBindingPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBridgePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerConnectionPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerConsumerPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerCriticalPlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerMessagePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerQueuePlugin; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerSessionPlugin; import org.apache.activemq.artemis.core.server.reload.ReloadCallback; import org.apache.activemq.artemis.core.server.reload.ReloadManager; import org.apache.activemq.artemis.core.server.reload.ReloadManagerImpl; @@ -655,7 +664,9 @@ private void sendCriticalNotification(final CriticalComponent criticalComponent) @Override public void run() { try { - callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.criticalFailure(criticalComponent) : null); + if (hasBrokerCriticalPlugins()) { + callBrokerCriticalPlugins(plugin -> plugin.criticalFailure(criticalComponent)); + } } catch (Throwable e) { logger.warn(e.getMessage(), e); } @@ -1412,14 +1423,17 @@ public ServerSession createSession(final String name, checkSessionLimit(validatedUser); - callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.beforeCreateSession(name, username, minLargeMessageSize, connection, - autoCommitSends, autoCommitAcks, preAcknowledge, xa, defaultAddress, callback, autoCreateQueues, context, prefixes) : null); - + if (hasBrokerSessionPlugins()) { + callBrokerSessionPlugins(plugin -> plugin.beforeCreateSession(name, username, minLargeMessageSize, connection, + autoCommitSends, autoCommitAcks, preAcknowledge, xa, defaultAddress, callback, autoCreateQueues, context, prefixes)); + } final ServerSessionImpl session = internalCreateSession(name, username, password, validatedUser, minLargeMessageSize, connection, autoCommitSends, autoCommitAcks, preAcknowledge, xa, defaultAddress, callback, context, autoCreateQueues, prefixes); sessions.put(name, session); - callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.afterCreateSession(session) : null); + if (hasBrokerSessionPlugins()) { + callBrokerSessionPlugins(plugin -> plugin.afterCreateSession(session)); + } return session; } @@ -1898,8 +1912,10 @@ public void destroyQueue(final SimpleString queueName, return; } - callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.beforeDestroyQueue(queueName, session, checkConsumerCount, - removeConsumers, autoDeleteAddress) : null); + if (hasBrokerQueuePlugins()) { + callBrokerQueuePlugins(plugin -> plugin.beforeDestroyQueue(queueName, session, checkConsumerCount, + removeConsumers, autoDeleteAddress)); + } addressSettingsRepository.clearCache(); @@ -1930,8 +1946,10 @@ public void destroyQueue(final SimpleString queueName, queue.deleteQueue(removeConsumers); - callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.afterDestroyQueue(queue, address, session, checkConsumerCount, - removeConsumers, autoDeleteAddress) : null); + if (hasBrokerQueuePlugins()) { + callBrokerQueuePlugins(plugin -> plugin.afterDestroyQueue(queue, address, session, checkConsumerCount, + removeConsumers, autoDeleteAddress)); + } AddressInfo addressInfo = getAddressInfo(address); if (autoDeleteAddress && postOffice != null && addressInfo != null && addressInfo.isAutoCreated()) { @@ -2008,32 +2026,126 @@ public void callPostQueueDeletionCallbacks(final SimpleString address, } @Override - public void registerBrokerPlugins(final List plugins) { + public void registerBrokerPlugins(final List plugins) { configuration.registerBrokerPlugins(plugins); plugins.forEach(plugin -> plugin.registered(this)); } @Override - public void registerBrokerPlugin(final ActiveMQServerPlugin plugin) { + public void registerBrokerPlugin(final ActiveMQServerBasePlugin plugin) { configuration.registerBrokerPlugin(plugin); plugin.registered(this); } @Override - public void unRegisterBrokerPlugin(final ActiveMQServerPlugin plugin) { + public void unRegisterBrokerPlugin(final ActiveMQServerBasePlugin plugin) { configuration.unRegisterBrokerPlugin(plugin); plugin.unregistered(this); } @Override - public List getBrokerPlugins() { + public List getBrokerPlugins() { return configuration.getBrokerPlugins(); } + @Override + public List getBrokerConnectionPlugins() { + return configuration.getBrokerConnectionPlugins(); + } + + @Override + public List getBrokerSessionPlugins() { + return configuration.getBrokerSessionPlugins(); + } + + @Override + public List getBrokerConsumerPlugins() { + return configuration.getBrokerConsumerPlugins(); + } + + @Override + public List getBrokerAddressPlugins() { + return configuration.getBrokerAddressPlugins(); + } + + @Override + public List getBrokerQueuePlugins() { + return configuration.getBrokerQueuePlugins(); + } + + @Override + public List getBrokerBindingPlugins() { + return configuration.getBrokerBindingPlugins(); + } + + @Override + public List getBrokerMessagePlugins() { + return configuration.getBrokerMessagePlugins(); + } + + @Override + public List getBrokerBridgePlugins() { + return configuration.getBrokerBridgePlugins(); + } + + @Override + public List getBrokerCriticalPlugins() { + return configuration.getBrokerCriticalPlugins(); + } + @Override public void callBrokerPlugins(final ActiveMQPluginRunnable pluginRun) throws ActiveMQException { + callBrokerPlugins(getBrokerPlugins(), pluginRun); + } + + @Override + public void callBrokerConnectionPlugins(final ActiveMQPluginRunnable pluginRun) throws ActiveMQException { + callBrokerPlugins(getBrokerConnectionPlugins(), pluginRun); + } + + @Override + public void callBrokerSessionPlugins(final ActiveMQPluginRunnable pluginRun) throws ActiveMQException { + callBrokerPlugins(getBrokerSessionPlugins(), pluginRun); + } + + @Override + public void callBrokerConsumerPlugins(final ActiveMQPluginRunnable pluginRun) throws ActiveMQException { + callBrokerPlugins(getBrokerConsumerPlugins(), pluginRun); + } + + @Override + public void callBrokerAddressPlugins(final ActiveMQPluginRunnable pluginRun) throws ActiveMQException { + callBrokerPlugins(getBrokerAddressPlugins(), pluginRun); + } + + @Override + public void callBrokerQueuePlugins(final ActiveMQPluginRunnable pluginRun) throws ActiveMQException { + callBrokerPlugins(getBrokerQueuePlugins(), pluginRun); + } + + @Override + public void callBrokerBindingPlugins(final ActiveMQPluginRunnable pluginRun) throws ActiveMQException { + callBrokerPlugins(getBrokerBindingPlugins(), pluginRun); + } + + @Override + public void callBrokerMessagePlugins(final ActiveMQPluginRunnable pluginRun) throws ActiveMQException { + callBrokerPlugins(getBrokerMessagePlugins(), pluginRun); + } + + @Override + public void callBrokerBridgePlugins(final ActiveMQPluginRunnable pluginRun) throws ActiveMQException { + callBrokerPlugins(getBrokerBridgePlugins(), pluginRun); + } + + @Override + public void callBrokerCriticalPlugins(final ActiveMQPluginRunnable pluginRun) throws ActiveMQException { + callBrokerPlugins(getBrokerCriticalPlugins(), pluginRun); + } + + private

void callBrokerPlugins(final List

plugins, final ActiveMQPluginRunnable

pluginRun) throws ActiveMQException { if (pluginRun != null) { - for (ActiveMQServerPlugin plugin : getBrokerPlugins()) { + for (P plugin : plugins) { try { pluginRun.run(plugin); } catch (Throwable e) { @@ -2053,6 +2165,51 @@ public boolean hasBrokerPlugins() { return !getBrokerPlugins().isEmpty(); } + @Override + public boolean hasBrokerConnectionPlugins() { + return !getBrokerConnectionPlugins().isEmpty(); + } + + @Override + public boolean hasBrokerSessionPlugins() { + return !getBrokerSessionPlugins().isEmpty(); + } + + @Override + public boolean hasBrokerConsumerPlugins() { + return !getBrokerConsumerPlugins().isEmpty(); + } + + @Override + public boolean hasBrokerAddressPlugins() { + return !getBrokerAddressPlugins().isEmpty(); + } + + @Override + public boolean hasBrokerQueuePlugins() { + return !getBrokerQueuePlugins().isEmpty(); + } + + @Override + public boolean hasBrokerBindingPlugins() { + return !getBrokerBindingPlugins().isEmpty(); + } + + @Override + public boolean hasBrokerMessagePlugins() { + return !getBrokerMessagePlugins().isEmpty(); + } + + @Override + public boolean hasBrokerBridgePlugins() { + return !getBrokerBridgePlugins().isEmpty(); + } + + @Override + public boolean hasBrokerCriticalPlugins() { + return !getBrokerCriticalPlugins().isEmpty(); + } + @Override public ExecutorFactory getExecutorFactory() { return executorFactory; @@ -2854,7 +3011,9 @@ public Queue createQueue(final AddressInfo addrInfo, .delayBeforeDispatch(delayBeforeDispatch) .build(); - callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.beforeCreateQueue(queueConfig) : null); + if (hasBrokerQueuePlugins()) { + callBrokerQueuePlugins(plugin -> plugin.beforeCreateQueue(queueConfig)); + } final Queue queue = queueFactory.createQueueWith(queueConfig); @@ -2898,7 +3057,9 @@ public Queue createQueue(final AddressInfo addrInfo, managementService.registerQueue(queue, queue.getAddress(), storageManager); } - callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.afterCreateQueue(queue) : null); + if (hasBrokerQueuePlugins()) { + callBrokerQueuePlugins(plugin -> plugin.afterCreateQueue(queue)); + } callPostQueueCreationCallbacks(queue.getName()); @@ -2978,7 +3139,9 @@ public Queue createQueue(final SimpleString address, .delayBeforeDispatch(delayBeforeDispatch) .build(); - callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.beforeCreateQueue(queueConfig) : null); + if (hasBrokerQueuePlugins()) { + callBrokerQueuePlugins(plugin -> plugin.beforeCreateQueue(queueConfig)); + } final Queue queue = queueFactory.createQueueWith(queueConfig); @@ -3020,7 +3183,9 @@ public Queue createQueue(final SimpleString address, managementService.registerQueue(queue, queue.getAddress(), storageManager); - callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.afterCreateQueue(queue) : null); + if (hasBrokerQueuePlugins()) { + callBrokerQueuePlugins(plugin -> plugin.afterCreateQueue(queue)); + } callPostQueueCreationCallbacks(queue.getName()); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java index 0d4c6865b5b..8af8aaa7634 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java @@ -1384,8 +1384,8 @@ public void acknowledge(final MessageReference ref, final AckReason reason, fina messagesAcknowledged.incrementAndGet(); } - if (server != null && server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.messageAcknowledged(ref, reason, consumer)); + if (server != null && server.hasBrokerMessagePlugins()) { + server.callBrokerMessagePlugins(plugin -> plugin.messageAcknowledged(ref, reason, consumer)); } } @@ -1422,8 +1422,8 @@ public void acknowledge(final Transaction tx, final MessageReference ref, final messagesAcknowledged.incrementAndGet(); } - if (server != null && server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.messageAcknowledged(ref, reason, consumer)); + if (server != null && server.hasBrokerMessagePlugins()) { + server.callBrokerMessagePlugins(plugin -> plugin.messageAcknowledged(ref, reason, consumer)); } } @@ -1514,9 +1514,9 @@ public void expire(final MessageReference ref, final ServerConsumer consumer) th acknowledge(ref, AckReason.EXPIRED, consumer); } - if (server != null && server.hasBrokerPlugins()) { + if (server != null && server.hasBrokerMessagePlugins()) { final SimpleString expiryAddress = messageExpiryAddress; - server.callBrokerPlugins(plugin -> plugin.messageExpired(ref, expiryAddress, consumer)); + server.callBrokerMessagePlugins(plugin -> plugin.messageExpired(ref, expiryAddress, consumer)); } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java index 22bfdaf17e3..ddd0b717200 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java @@ -446,8 +446,8 @@ public void proceedDeliver(MessageReference reference) throws Exception { try { Message message = reference.getMessage(); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeDeliver(this, reference)); + if (server.hasBrokerMessagePlugins()) { + server.callBrokerMessagePlugins(plugin -> plugin.beforeDeliver(this, reference)); } if (message.isLargeMessage() && supportLargeMessage) { @@ -466,8 +466,8 @@ public void proceedDeliver(MessageReference reference) throws Exception { } finally { lockDelivery.readLock().unlock(); callback.afterDelivery(); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterDeliver(this, reference)); + if (server.hasBrokerMessagePlugins()) { + server.callBrokerMessagePlugins(plugin -> plugin.afterDeliver(this, reference)); } } @@ -489,8 +489,8 @@ public synchronized void close(final boolean failed) throws Exception { logger.trace("ServerConsumerImpl::" + this + " being closed with failed=" + failed, new Exception("trace")); } - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeCloseConsumer(this, failed)); + if (server.hasBrokerConsumerPlugins()) { + server.callBrokerConsumerPlugins(plugin -> plugin.beforeCloseConsumer(this, failed)); } setStarted(false); @@ -550,8 +550,8 @@ public synchronized void close(final boolean failed) throws Exception { managementService.sendNotification(notification); } - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterCloseConsumer(this, failed)); + if (server.hasBrokerConsumerPlugins()) { + server.callBrokerConsumerPlugins(plugin -> plugin.afterCloseConsumer(this, failed)); } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java index c0bca6b666f..4910e6621b1 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java @@ -356,8 +356,8 @@ protected void doClose(final boolean failed) throws Exception { } synchronized (this) { if (!closed) { - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeCloseSession(this, failed)); + if (server.hasBrokerSessionPlugins()) { + server.callBrokerSessionPlugins(plugin -> plugin.beforeCloseSession(this, failed)); } } this.setStarted(false); @@ -412,8 +412,8 @@ protected void doClose(final boolean failed) throws Exception { closed = true; - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterCloseSession(this, failed)); + if (server.hasBrokerSessionPlugins()) { + server.callBrokerSessionPlugins(plugin -> plugin.afterCloseSession(this, failed)); } } } @@ -470,16 +470,16 @@ public ServerConsumer createConsumer(final long consumerID, Filter filter = FilterImpl.createFilter(filterString); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeCreateConsumer(consumerID, (QueueBinding) binding, + if (server.hasBrokerConsumerPlugins()) { + server.callBrokerConsumerPlugins(plugin -> plugin.beforeCreateConsumer(consumerID, (QueueBinding) binding, filterString, browseOnly, supportLargeMessage)); } ServerConsumer consumer = new ServerConsumerImpl(consumerID, this, (QueueBinding) binding, filter, started, browseOnly, storageManager, callback, preAcknowledge, strictUpdateDeliveryCount, managementService, supportLargeMessage, credits, server); consumers.put(consumer.getID(), consumer); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterCreateConsumer(consumer)); + if (server.hasBrokerConsumerPlugins()) { + server.callBrokerConsumerPlugins(plugin -> plugin.afterCreateConsumer(consumer)); } if (!browseOnly) { @@ -1422,8 +1422,8 @@ public synchronized RoutingStatus send(Transaction tx, message = msg; } - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeSend(this, tx, message, direct, noAutoCreateQueue)); + if (server.hasBrokerMessagePlugins()) { + server.callBrokerMessagePlugins(plugin -> plugin.beforeSend(this, tx, message, direct, noAutoCreateQueue)); } // If the protocol doesn't support flow control, we have no choice other than fail the communication @@ -1470,8 +1470,8 @@ public synchronized RoutingStatus send(Transaction tx, result = doSend(tx, message, address, direct, noAutoCreateQueue); } - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterSend(this, tx, message, direct, noAutoCreateQueue, result)); + if (server.hasBrokerMessagePlugins()) { + server.callBrokerMessagePlugins(plugin -> plugin.afterSend(this, tx, message, direct, noAutoCreateQueue, result)); } return result; @@ -1504,8 +1504,8 @@ public void setTransferring(final boolean transferring) { @Override public void addMetaData(String key, String data) throws Exception { - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.beforeSessionMetadataAdded(this, key, data)); + if (server.hasBrokerSessionPlugins()) { + server.callBrokerSessionPlugins(plugin -> plugin.beforeSessionMetadataAdded(this, key, data)); } if (metaData == null) { @@ -1513,8 +1513,8 @@ public void addMetaData(String key, String data) throws Exception { } metaData.put(key, data); - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.afterSessionMetadataAdded(this, key, data)); + if (server.hasBrokerSessionPlugins()) { + server.callBrokerSessionPlugins(plugin -> plugin.afterSessionMetadataAdded(this, key, data)); } } @@ -1523,8 +1523,8 @@ public boolean addUniqueMetaData(String key, String data) throws Exception { ServerSession sessionWithMetaData = server.lookupSession(key, data); if (sessionWithMetaData != null && sessionWithMetaData != this) { // There is a duplication of this property - if (server.hasBrokerPlugins()) { - server.callBrokerPlugins(plugin -> plugin.duplicateSessionMetadataFailure(this, key, data)); + if (server.hasBrokerSessionPlugins()) { + server.callBrokerSessionPlugins(plugin -> plugin.duplicateSessionMetadataFailure(this, key, data)); } return false; } else { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQPluginRunnable.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQPluginRunnable.java index 4abe95df010..c51a7f31f09 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQPluginRunnable.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQPluginRunnable.java @@ -19,8 +19,8 @@ import org.apache.activemq.artemis.api.core.ActiveMQException; -public interface ActiveMQPluginRunnable { +public interface ActiveMQPluginRunnable

{ - void run(ActiveMQServerPlugin plugin) throws ActiveMQException; + void run(P plugin) throws ActiveMQException; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerAddressPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerAddressPlugin.java new file mode 100644 index 00000000000..2a431e444ff --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerAddressPlugin.java @@ -0,0 +1,95 @@ +/** + * 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.artemis.core.server.plugin; + +import java.util.EnumSet; +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.server.impl.AddressInfo; + +/** + * + */ +public interface ActiveMQServerAddressPlugin extends ActiveMQServerBasePlugin { + + /** + * Before an address is added tot he broker + * + * @param addressInfo The addressInfo that will be added + * @param reload If the address is being reloaded + * @throws ActiveMQException + */ + default void beforeAddAddress(AddressInfo addressInfo, boolean reload) throws ActiveMQException { + + } + + /** + * After an address has been added tot he broker + * + * @param addressInfo The newly added address + * @param reload If the address is being reloaded + * @throws ActiveMQException + */ + default void afterAddAddress(AddressInfo addressInfo, boolean reload) throws ActiveMQException { + + } + + + /** + * Before an address is updated + * + * @param address The existing address info that is about to be updated + * @param routingTypes The new routing types that the address will be updated with + * @throws ActiveMQException + */ + default void beforeUpdateAddress(SimpleString address, EnumSet routingTypes) throws ActiveMQException { + + } + + /** + * After an address has been updated + * + * @param addressInfo The newly updated address info + * @throws ActiveMQException + */ + default void afterUpdateAddress(AddressInfo addressInfo) throws ActiveMQException { + + } + + /** + * Before an address is removed + * + * @param address The address that will be removed + * @throws ActiveMQException + */ + default void beforeRemoveAddress(SimpleString address) throws ActiveMQException { + + } + + /** + * After an address has been removed + * + * @param address The address that has been removed + * @param addressInfo The address info that has been removed or null if not removed + * @throws ActiveMQException + */ + default void afterRemoveAddress(SimpleString address, AddressInfo addressInfo) throws ActiveMQException { + + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBasePlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBasePlugin.java new file mode 100644 index 00000000000..3f29afca7bc --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBasePlugin.java @@ -0,0 +1,52 @@ +/** + * 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.artemis.core.server.plugin; + +import java.util.Map; +import org.apache.activemq.artemis.core.server.ActiveMQServer; + + +/** + * + */ +public interface ActiveMQServerBasePlugin { + + /** + * used to pass configured properties to Plugin + * + * @param properties + */ + default void init(Map properties) { + } + + /** + * The plugin has been registered with the server + * + * @param server The ActiveMQServer the plugin has been registered to + */ + default void registered(ActiveMQServer server) { + } + + /** + * The plugin has been unregistered with the server + * + * @param server The ActiveMQServer the plugin has been unregistered to + */ + default void unregistered(ActiveMQServer server) { + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBindingPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBindingPlugin.java new file mode 100644 index 00000000000..44efd7973b7 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBindingPlugin.java @@ -0,0 +1,73 @@ +/** + * 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.artemis.core.server.plugin; + +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.postoffice.Binding; +import org.apache.activemq.artemis.core.transaction.Transaction; + +/** + * + */ +public interface ActiveMQServerBindingPlugin extends ActiveMQServerBasePlugin { + + /** + * Before a binding is added + * + * @param binding + * @throws ActiveMQException + */ + default void beforeAddBinding(Binding binding) throws ActiveMQException { + + } + + /** + * After a binding has been added + * + * @param binding The newly added binding + * @throws ActiveMQException + */ + default void afterAddBinding(Binding binding) throws ActiveMQException { + + } + + /** + * Before a binding is removed + * + * @param uniqueName + * @param tx + * @param deleteData + * @throws ActiveMQException + */ + default void beforeRemoveBinding(SimpleString uniqueName, Transaction tx, boolean deleteData) throws ActiveMQException { + + } + + /** + * After a binding is removed + * + * @param binding + * @param tx + * @param deleteData + * @throws ActiveMQException + */ + default void afterRemoveBinding(Binding binding, Transaction tx, boolean deleteData) throws ActiveMQException { + + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBridgePlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBridgePlugin.java new file mode 100644 index 00000000000..ca2493aa7db --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerBridgePlugin.java @@ -0,0 +1,85 @@ +/** + * 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.artemis.core.server.plugin; + +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.core.config.BridgeConfiguration; +import org.apache.activemq.artemis.core.server.HandleStatus; +import org.apache.activemq.artemis.core.server.MessageReference; +import org.apache.activemq.artemis.core.server.cluster.Bridge; + +/** + * + */ +public interface ActiveMQServerBridgePlugin extends ActiveMQServerBasePlugin { + + /** + * Before a bridge is deployed + * + * @param config The bridge configuration + * @throws ActiveMQException + */ + default void beforeDeployBridge(BridgeConfiguration config) throws ActiveMQException { + + } + + /** + * After a bridge has been deployed + * + * @param bridge The newly deployed bridge + * @throws ActiveMQException + */ + default void afterDeployBridge(Bridge bridge) throws ActiveMQException { + + } + + /** + * Called immediately before a bridge delivers a message + * + * @param bridge + * @param ref + * @throws ActiveMQException + */ + default void beforeDeliverBridge(Bridge bridge, MessageReference ref) throws ActiveMQException { + + } + + /** + * Called immediately after a bridge delivers a message but before the message + * is acknowledged + * + * @param bridge + * @param ref + * @param status + * @throws ActiveMQException + */ + default void afterDeliverBridge(Bridge bridge, MessageReference ref, HandleStatus status) throws ActiveMQException { + + } + + /** + * Called after delivered message over this bridge has been acknowledged by the remote broker + * + * @param bridge + * @param ref + * @throws ActiveMQException + */ + default void afterAcknowledgeBridge(Bridge bridge, MessageReference ref) throws ActiveMQException { + + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerConnectionPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerConnectionPlugin.java new file mode 100644 index 00000000000..3489f2d74d9 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerConnectionPlugin.java @@ -0,0 +1,49 @@ +/** + * 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.artemis.core.server.plugin; + +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; + +/** + * + */ +public interface ActiveMQServerConnectionPlugin extends ActiveMQServerBasePlugin { + + + + /** + * A connection has been created. + * + * @param connection The newly created connection + * @throws ActiveMQException + */ + default void afterCreateConnection(RemotingConnection connection) throws ActiveMQException { + + } + + /** + * A connection has been destroyed. + * + * @param connection + * @throws ActiveMQException + */ + default void afterDestroyConnection(RemotingConnection connection) throws ActiveMQException { + + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerConsumerPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerConsumerPlugin.java new file mode 100644 index 00000000000..93b9c4f6591 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerConsumerPlugin.java @@ -0,0 +1,97 @@ +/** + * 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.artemis.core.server.plugin; + +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.postoffice.QueueBinding; +import org.apache.activemq.artemis.core.server.ServerConsumer; + +/** + * + */ +public interface ActiveMQServerConsumerPlugin extends ActiveMQServerBasePlugin { + + /** + * Before a consumer is created + * + * @param consumerID + * @param queueName + * @param filterString + * @param browseOnly + * @param supportLargeMessage + * @throws ActiveMQException + * + * @deprecated use {@link #beforeCreateConsumer(long, QueueBinding, SimpleString, boolean, boolean) + */ + @Deprecated + default void beforeCreateConsumer(long consumerID, SimpleString queueName, SimpleString filterString, + boolean browseOnly, boolean supportLargeMessage) throws ActiveMQException { + + } + + + /** + * + * Before a consumer is created + * + * @param consumerID + * @param QueueBinding + * @param filterString + * @param browseOnly + * @param supportLargeMessage + * @throws ActiveMQException + */ + default void beforeCreateConsumer(long consumerID, QueueBinding queueBinding, SimpleString filterString, + boolean browseOnly, boolean supportLargeMessage) throws ActiveMQException { + //by default call the old method for backwards compatibility + this.beforeCreateConsumer(consumerID, queueBinding.getQueue().getName(), filterString, browseOnly, supportLargeMessage); + } + + /** + * After a consumer has been created + * + * @param consumer the created consumer + * @throws ActiveMQException + */ + default void afterCreateConsumer(ServerConsumer consumer) throws ActiveMQException { + + } + + /** + * Before a consumer is closed + * + * @param consumer + * @param failed + * @throws ActiveMQException + */ + default void beforeCloseConsumer(ServerConsumer consumer, boolean failed) throws ActiveMQException { + + } + + /** + * After a consumer is closed + * + * @param consumer + * @param failed + * @throws ActiveMQException + */ + default void afterCloseConsumer(ServerConsumer consumer, boolean failed) throws ActiveMQException { + + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerCriticalPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerCriticalPlugin.java new file mode 100644 index 00000000000..0a32c1a9492 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerCriticalPlugin.java @@ -0,0 +1,36 @@ +/** + * 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.artemis.core.server.plugin; + +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.utils.critical.CriticalComponent; + +/** + * + */ +public interface ActiveMQServerCriticalPlugin extends ActiveMQServerBasePlugin { + + /** + * A Critical failure has been detected. + * This will be called before the broker is stopped + * @param components + * @throws ActiveMQException + */ + default void criticalFailure(CriticalComponent components) throws ActiveMQException { + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerMessagePlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerMessagePlugin.java new file mode 100644 index 00000000000..aef0970a4f6 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerMessagePlugin.java @@ -0,0 +1,236 @@ +/** + * 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.artemis.core.server.plugin; + +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.Message; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.postoffice.RoutingStatus; +import org.apache.activemq.artemis.core.server.MessageReference; +import org.apache.activemq.artemis.core.server.RoutingContext; +import org.apache.activemq.artemis.core.server.ServerConsumer; +import org.apache.activemq.artemis.core.server.ServerSession; +import org.apache.activemq.artemis.core.server.impl.AckReason; +import org.apache.activemq.artemis.core.transaction.Transaction; + +/** + * + */ +public interface ActiveMQServerMessagePlugin extends ActiveMQServerBasePlugin { + + /** + * Before a message is sent + * + * @param session the session that sends the message + * @param tx + * @param message + * @param direct + * @param noAutoCreateQueue + * @throws ActiveMQException + */ + default void beforeSend(ServerSession session, Transaction tx, Message message, boolean direct, boolean noAutoCreateQueue) throws ActiveMQException { + //by default call the old method for backwards compatibility + this.beforeSend(tx, message, direct, noAutoCreateQueue); + } + + /** + * After a message is sent + * + * @param session the session that sends the message + * @param tx + * @param message + * @param direct + * @param noAutoCreateQueue + * @param result + * @throws ActiveMQException + */ + default void afterSend(ServerSession session, Transaction tx, Message message, boolean direct, boolean noAutoCreateQueue, + RoutingStatus result) throws ActiveMQException { + //by default call the old method for backwards compatibility + this.afterSend(tx, message, direct, noAutoCreateQueue, result); + } + + + /** + * Before a message is sent + * + * @param tx + * @param message + * @param direct + * @param noAutoCreateQueue + * @throws ActiveMQException + * + * @deprecated use {@link #beforeSend(ServerSession, Transaction, Message, boolean, boolean)} + */ + @Deprecated + default void beforeSend(Transaction tx, Message message, boolean direct, boolean noAutoCreateQueue) throws ActiveMQException { + + } + + /** + * After a message is sent + * + * @param tx + * @param message + * @param direct + * @param noAutoCreateQueue + * @param result + * @throws ActiveMQException + * + * @deprecated use {@link #afterSend(ServerSession, Transaction, Message, boolean, boolean, RoutingStatus)} + */ + @Deprecated + default void afterSend(Transaction tx, Message message, boolean direct, boolean noAutoCreateQueue, + RoutingStatus result) throws ActiveMQException { + + } + + /** + * Before a message is routed + * + * @param message + * @param context + * @param direct + * @param rejectDuplicates + * @throws ActiveMQException + */ + default void beforeMessageRoute(Message message, RoutingContext context, boolean direct, boolean rejectDuplicates) throws ActiveMQException { + + } + + /** + * After a message is routed + * + * @param message + * @param context + * @param direct + * @param rejectDuplicates + * @param result + * @throws ActiveMQException + */ + default void afterMessageRoute(Message message, RoutingContext context, boolean direct, boolean rejectDuplicates, + RoutingStatus result) throws ActiveMQException { + + } + + /** + * Before a message is delivered to a client consumer + * + * @param consumer the consumer the message will be delivered to + * @param reference message reference + * @throws ActiveMQException + */ + default void beforeDeliver(ServerConsumer consumer, MessageReference reference) throws ActiveMQException { + //by default call the old method for backwards compatibility + this.beforeDeliver(reference); + } + + /** + * After a message is delivered to a client consumer + * + * @param consumer the consumer the message was delivered to + * @param reference message reference + * @throws ActiveMQException + */ + default void afterDeliver(ServerConsumer consumer, MessageReference reference) throws ActiveMQException { + //by default call the old method for backwards compatibility + this.afterDeliver(reference); + } + + /** + * Before a message is delivered to a client consumer + * + * @param reference + * @throws ActiveMQException + * + * @deprecated use throws ActiveMQException {@link #beforeDeliver(ServerConsumer, MessageReference)} + */ + @Deprecated + default void beforeDeliver(MessageReference reference) throws ActiveMQException { + + } + + /** + * After a message is delivered to a client consumer + * + * @param reference + * @throws ActiveMQException + * + * @deprecated use {@link #afterDeliver(ServerConsumer, MessageReference)} + */ + @Deprecated + default void afterDeliver(MessageReference reference) throws ActiveMQException { + + } + + /** + * A message has been expired + * + * @param message The expired message + * @param messageExpiryAddress The message expiry address if exists + * @throws ActiveMQException + * + * @deprecated use {@link #messageExpired(MessageReference, SimpleString, ServerConsumer)} + */ + @Deprecated + default void messageExpired(MessageReference message, SimpleString messageExpiryAddress) throws ActiveMQException { + + } + + /** + * A message has been expired + * + * @param message The expired message + * @param messageExpiryAddress The message expiry address if exists + * @param consumer the Consumer that acknowledged the message - this field is optional + * and can be null + * @throws ActiveMQException + */ + default void messageExpired(MessageReference message, SimpleString messageExpiryAddress, ServerConsumer consumer) throws ActiveMQException { + messageExpired(message, messageExpiryAddress); + } + + /** + * A message has been acknowledged + * + * @param ref The acked message + * @param reason The ack reason + * @throws ActiveMQException + * + * @deprecated use {@link #messageAcknowledged(MessageReference, AckReason, ServerConsumer)} + */ + @Deprecated + default void messageAcknowledged(MessageReference ref, AckReason reason) throws ActiveMQException { + + } + + /** + * A message has been acknowledged + * + * @param ref The acked message + * @param reason The ack reason + * @param consumer the Consumer that acknowledged the message - this field is optional + * and can be null + * @throws ActiveMQException + * + */ + default void messageAcknowledged(MessageReference ref, AckReason reason, ServerConsumer consumer) throws ActiveMQException { + //by default call the old method for backwards compatibility + this.messageAcknowledged(ref, reason); + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java index 704420e88b9..426001129a7 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java @@ -17,669 +17,18 @@ package org.apache.activemq.artemis.core.server.plugin; -import java.util.EnumSet; -import java.util.Map; - -import org.apache.activemq.artemis.api.core.ActiveMQException; -import org.apache.activemq.artemis.api.core.Message; -import org.apache.activemq.artemis.api.core.RoutingType; -import org.apache.activemq.artemis.api.core.SimpleString; -import org.apache.activemq.artemis.core.config.BridgeConfiguration; -import org.apache.activemq.artemis.core.persistence.OperationContext; -import org.apache.activemq.artemis.core.postoffice.Binding; -import org.apache.activemq.artemis.core.postoffice.QueueBinding; -import org.apache.activemq.artemis.core.postoffice.RoutingStatus; -import org.apache.activemq.artemis.core.security.SecurityAuth; -import org.apache.activemq.artemis.core.server.ActiveMQServer; -import org.apache.activemq.artemis.core.server.HandleStatus; -import org.apache.activemq.artemis.core.server.MessageReference; -import org.apache.activemq.artemis.core.server.Queue; -import org.apache.activemq.artemis.core.server.QueueConfig; -import org.apache.activemq.artemis.core.server.RoutingContext; -import org.apache.activemq.artemis.core.server.ServerConsumer; -import org.apache.activemq.artemis.core.server.ServerSession; -import org.apache.activemq.artemis.core.server.cluster.Bridge; -import org.apache.activemq.artemis.core.server.impl.AckReason; -import org.apache.activemq.artemis.core.server.impl.AddressInfo; -import org.apache.activemq.artemis.core.transaction.Transaction; -import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; -import org.apache.activemq.artemis.spi.core.protocol.SessionCallback; -import org.apache.activemq.artemis.utils.critical.CriticalComponent; - /** * */ -public interface ActiveMQServerPlugin { - - /** - * The plugin has been registered with the server - * - * @param server The ActiveMQServer the plugin has been registered to - */ - default void registered(ActiveMQServer server) { - - } - - /** - * The plugin has been unregistered with the server - * - * @param server The ActiveMQServer the plugin has been unregistered to - */ - default void unregistered(ActiveMQServer server) { - - } - - /** - * A connection has been created. - * - * @param connection The newly created connection - * @throws ActiveMQException - */ - default void afterCreateConnection(RemotingConnection connection) throws ActiveMQException { - - } - - /** - * A connection has been destroyed. - * - * @param connection - * @throws ActiveMQException - */ - default void afterDestroyConnection(RemotingConnection connection) throws ActiveMQException { - - } - - /** - * Before a session is created. - * - * @param name - * @param username - * @param minLargeMessageSize - * @param connection - * @param autoCommitSends - * @param autoCommitAcks - * @param preAcknowledge - * @param xa - * @param defaultAddress - * @param callback - * @param autoCreateQueues - * @param context - * @param prefixes - * @throws ActiveMQException - */ - default void beforeCreateSession(String name, String username, int minLargeMessageSize, - RemotingConnection connection, boolean autoCommitSends, boolean autoCommitAcks, boolean preAcknowledge, - boolean xa, String defaultAddress, SessionCallback callback, boolean autoCreateQueues, OperationContext context, - Map prefixes) throws ActiveMQException { - - } - - /** - * After a session has been created. - * - * @param session The newly created session - * @throws ActiveMQException - */ - default void afterCreateSession(ServerSession session) throws ActiveMQException { - - } - - /** - * Before a session is closed - * - * @param session - * @param failed - * @throws ActiveMQException - */ - default void beforeCloseSession(ServerSession session, boolean failed) throws ActiveMQException { - - } - - /** - * After a session is closed - * - * @param session - * @param failed - * @throws ActiveMQException - */ - default void afterCloseSession(ServerSession session, boolean failed) throws ActiveMQException { - - } - - /** - * Before session metadata is added to the session - * - * @param session - * @param key - * @param data - * @throws ActiveMQException - */ - default void beforeSessionMetadataAdded(ServerSession session, String key, String data) throws ActiveMQException { - - } - - /** - * Called when adding session metadata fails because the metadata is a duplicate - * - * @param session - * @param key - * @param data - * @throws ActiveMQException - */ - default void duplicateSessionMetadataFailure(ServerSession session, String key, String data) throws ActiveMQException { - - } - - /** - * After session metadata is added to the session - * - * @param session - * @param key - * @param data - * @throws ActiveMQException - */ - default void afterSessionMetadataAdded(ServerSession session, String key, String data) throws ActiveMQException { - - } - - /** - * Before a consumer is created - * - * @param consumerID - * @param queueName - * @param filterString - * @param browseOnly - * @param supportLargeMessage - * @throws ActiveMQException - * - * @deprecated use {@link #beforeCreateConsumer(long, QueueBinding, SimpleString, boolean, boolean) - */ - @Deprecated - default void beforeCreateConsumer(long consumerID, SimpleString queueName, SimpleString filterString, - boolean browseOnly, boolean supportLargeMessage) throws ActiveMQException { - - } - - - /** - * - * Before a consumer is created - * - * @param consumerID - * @param QueueBinding - * @param filterString - * @param browseOnly - * @param supportLargeMessage - * @throws ActiveMQException - */ - default void beforeCreateConsumer(long consumerID, QueueBinding queueBinding, SimpleString filterString, - boolean browseOnly, boolean supportLargeMessage) throws ActiveMQException { - //by default call the old method for backwards compatibility - this.beforeCreateConsumer(consumerID, queueBinding.getQueue().getName(), filterString, browseOnly, supportLargeMessage); - } - - /** - * After a consumer has been created - * - * @param consumer the created consumer - * @throws ActiveMQException - */ - default void afterCreateConsumer(ServerConsumer consumer) throws ActiveMQException { - - } - - /** - * Before a consumer is closed - * - * @param consumer - * @param failed - * @throws ActiveMQException - */ - default void beforeCloseConsumer(ServerConsumer consumer, boolean failed) throws ActiveMQException { - - } - - /** - * After a consumer is closed - * - * @param consumer - * @param failed - * @throws ActiveMQException - */ - default void afterCloseConsumer(ServerConsumer consumer, boolean failed) throws ActiveMQException { - - } - - /** - * Before an address is added tot he broker - * - * @param addressInfo The addressInfo that will be added - * @param reload If the address is being reloaded - * @throws ActiveMQException - */ - default void beforeAddAddress(AddressInfo addressInfo, boolean reload) throws ActiveMQException { - - } - - /** - * After an address has been added tot he broker - * - * @param addressInfo The newly added address - * @param reload If the address is being reloaded - * @throws ActiveMQException - */ - default void afterAddAddress(AddressInfo addressInfo, boolean reload) throws ActiveMQException { - - } - - - /** - * Before an address is updated - * - * @param address The existing address info that is about to be updated - * @param routingTypes The new routing types that the address will be updated with - * @throws ActiveMQException - */ - default void beforeUpdateAddress(SimpleString address, EnumSet routingTypes) throws ActiveMQException { - - } - - /** - * After an address has been updated - * - * @param addressInfo The newly updated address info - * @throws ActiveMQException - */ - default void afterUpdateAddress(AddressInfo addressInfo) throws ActiveMQException { - - } - - /** - * Before an address is removed - * - * @param address The address that will be removed - * @throws ActiveMQException - */ - default void beforeRemoveAddress(SimpleString address) throws ActiveMQException { - - } - - /** - * After an address has been removed - * - * @param address The address that has been removed - * @param addressInfo The address info that has been removed or null if not removed - * @throws ActiveMQException - */ - default void afterRemoveAddress(SimpleString address, AddressInfo addressInfo) throws ActiveMQException { - - } - - /** - * Before a queue is created - * - * @param queueConfig - * @throws ActiveMQException - */ - default void beforeCreateQueue(QueueConfig queueConfig) throws ActiveMQException { - - } - - /** - * After a queue has been created - * - * @param queue The newly created queue - * @throws ActiveMQException - */ - default void afterCreateQueue(Queue queue) throws ActiveMQException { - - } - - /** - * Before a queue is destroyed - * - * @param queueName - * @param session - * @param checkConsumerCount - * @param removeConsumers - * @param autoDeleteAddress - * @throws ActiveMQException - */ - default void beforeDestroyQueue(SimpleString queueName, final SecurityAuth session, boolean checkConsumerCount, - boolean removeConsumers, boolean autoDeleteAddress) throws ActiveMQException { - - } - - /** - * After a queue has been destroyed - * - * @param queue - * @param address - * @param session - * @param checkConsumerCount - * @param removeConsumers - * @param autoDeleteAddress - * @throws ActiveMQException - */ - default void afterDestroyQueue(Queue queue, SimpleString address, final SecurityAuth session, boolean checkConsumerCount, - boolean removeConsumers, boolean autoDeleteAddress) throws ActiveMQException { - - } - - /** - * Before a binding is added - * - * @param binding - * @throws ActiveMQException - */ - default void beforeAddBinding(Binding binding) throws ActiveMQException { - - } - - /** - * After a binding has been added - * - * @param binding The newly added binding - * @throws ActiveMQException - */ - default void afterAddBinding(Binding binding) throws ActiveMQException { - - } - - /** - * Before a binding is removed - * - * @param uniqueName - * @param tx - * @param deleteData - * @throws ActiveMQException - */ - default void beforeRemoveBinding(SimpleString uniqueName, Transaction tx, boolean deleteData) throws ActiveMQException { - - } - - /** - * After a binding is removed - * - * @param binding - * @param tx - * @param deleteData - * @throws ActiveMQException - */ - default void afterRemoveBinding(Binding binding, Transaction tx, boolean deleteData) throws ActiveMQException { - - } - - /** - * Before a message is sent - * - * @param session the session that sends the message - * @param tx - * @param message - * @param direct - * @param noAutoCreateQueue - * @throws ActiveMQException - */ - default void beforeSend(ServerSession session, Transaction tx, Message message, boolean direct, boolean noAutoCreateQueue) throws ActiveMQException { - //by default call the old method for backwards compatibility - this.beforeSend(tx, message, direct, noAutoCreateQueue); - } - - /** - * After a message is sent - * - * @param session the session that sends the message - * @param tx - * @param message - * @param direct - * @param noAutoCreateQueue - * @param result - * @throws ActiveMQException - */ - default void afterSend(ServerSession session, Transaction tx, Message message, boolean direct, boolean noAutoCreateQueue, - RoutingStatus result) throws ActiveMQException { - //by default call the old method for backwards compatibility - this.afterSend(tx, message, direct, noAutoCreateQueue, result); - } - - - /** - * Before a message is sent - * - * @param tx - * @param message - * @param direct - * @param noAutoCreateQueue - * @throws ActiveMQException - * - * @deprecated use {@link #beforeSend(ServerSession, Transaction, Message, boolean, boolean)} - */ - @Deprecated - default void beforeSend(Transaction tx, Message message, boolean direct, boolean noAutoCreateQueue) throws ActiveMQException { - - } - - /** - * After a message is sent - * - * @param tx - * @param message - * @param direct - * @param noAutoCreateQueue - * @param result - * @throws ActiveMQException - * - * @deprecated use {@link #afterSend(ServerSession, Transaction, Message, boolean, boolean, RoutingStatus)} - */ - @Deprecated - default void afterSend(Transaction tx, Message message, boolean direct, boolean noAutoCreateQueue, - RoutingStatus result) throws ActiveMQException { - - } - - /** - * Before a message is routed - * - * @param message - * @param context - * @param direct - * @param rejectDuplicates - * @throws ActiveMQException - */ - default void beforeMessageRoute(Message message, RoutingContext context, boolean direct, boolean rejectDuplicates) throws ActiveMQException { - - } - - /** - * After a message is routed - * - * @param message - * @param context - * @param direct - * @param rejectDuplicates - * @param result - * @throws ActiveMQException - */ - default void afterMessageRoute(Message message, RoutingContext context, boolean direct, boolean rejectDuplicates, - RoutingStatus result) throws ActiveMQException { - - } - - /** - * Before a message is delivered to a client consumer - * - * @param consumer the consumer the message will be delivered to - * @param reference message reference - * @throws ActiveMQException - */ - default void beforeDeliver(ServerConsumer consumer, MessageReference reference) throws ActiveMQException { - //by default call the old method for backwards compatibility - this.beforeDeliver(reference); - } - - /** - * After a message is delivered to a client consumer - * - * @param consumer the consumer the message was delivered to - * @param reference message reference - * @throws ActiveMQException - */ - default void afterDeliver(ServerConsumer consumer, MessageReference reference) throws ActiveMQException { - //by default call the old method for backwards compatibility - this.afterDeliver(reference); - } - - /** - * Before a message is delivered to a client consumer - * - * @param reference - * @throws ActiveMQException - * - * @deprecated use throws ActiveMQException {@link #beforeDeliver(ServerConsumer, MessageReference)} - */ - @Deprecated - default void beforeDeliver(MessageReference reference) throws ActiveMQException { - - } - - /** - * After a message is delivered to a client consumer - * - * @param reference - * @throws ActiveMQException - * - * @deprecated use {@link #afterDeliver(ServerConsumer, MessageReference)} - */ - @Deprecated - default void afterDeliver(MessageReference reference) throws ActiveMQException { - - } - - /** - * A message has been expired - * - * @param message The expired message - * @param messageExpiryAddress The message expiry address if exists - * @throws ActiveMQException - * - * @deprecated use {@link #messageExpired(MessageReference, SimpleString, ServerConsumer)} - */ - @Deprecated - default void messageExpired(MessageReference message, SimpleString messageExpiryAddress) throws ActiveMQException { - - } - - /** - * A message has been expired - * - * @param message The expired message - * @param messageExpiryAddress The message expiry address if exists - * @param consumer the Consumer that acknowledged the message - this field is optional - * and can be null - * @throws ActiveMQException - */ - default void messageExpired(MessageReference message, SimpleString messageExpiryAddress, ServerConsumer consumer) throws ActiveMQException { - messageExpired(message, messageExpiryAddress); - } - - /** - * A message has been acknowledged - * - * @param ref The acked message - * @param reason The ack reason - * @throws ActiveMQException - * - * @deprecated use {@link #messageAcknowledged(MessageReference, AckReason, ServerConsumer)} - */ - @Deprecated - default void messageAcknowledged(MessageReference ref, AckReason reason) throws ActiveMQException { - - } - - /** - * A message has been acknowledged - * - * @param ref The acked message - * @param reason The ack reason - * @param consumer the Consumer that acknowledged the message - this field is optional - * and can be null - * @throws ActiveMQException - * - */ - default void messageAcknowledged(MessageReference ref, AckReason reason, ServerConsumer consumer) throws ActiveMQException { - //by default call the old method for backwards compatibility - this.messageAcknowledged(ref, reason); - } - - /** - * Before a bridge is deployed - * - * @param config The bridge configuration - * @throws ActiveMQException - */ - default void beforeDeployBridge(BridgeConfiguration config) throws ActiveMQException { - - } - - /** - * After a bridge has been deployed - * - * @param bridge The newly deployed bridge - * @throws ActiveMQException - */ - default void afterDeployBridge(Bridge bridge) throws ActiveMQException { - - } - - /** - * Called immediately before a bridge delivers a message - * - * @param bridge - * @param ref - * @throws ActiveMQException - */ - default void beforeDeliverBridge(Bridge bridge, MessageReference ref) throws ActiveMQException { - - } - - /** - * Called immediately after a bridge delivers a message but before the message - * is acknowledged - * - * @param bridge - * @param ref - * @param status - * @throws ActiveMQException - */ - default void afterDeliverBridge(Bridge bridge, MessageReference ref, HandleStatus status) throws ActiveMQException { - - } - - /** - * Called after delivered message over this bridge has been acknowledged by the remote broker - * - * @param bridge - * @param ref - * @throws ActiveMQException - */ - default void afterAcknowledgeBridge(Bridge bridge, MessageReference ref) throws ActiveMQException { - - } - - /** - * A Critical failure has been detected. - * This will be called before the broker is stopped - * @param components - * @throws ActiveMQException - */ - default void criticalFailure(CriticalComponent components) throws ActiveMQException { - } - - /** - * used to pass configured properties to Plugin - * - * @param properties - */ - default void init(Map properties) { - } +public interface ActiveMQServerPlugin extends + ActiveMQServerBasePlugin, + ActiveMQServerConnectionPlugin, + ActiveMQServerSessionPlugin, + ActiveMQServerConsumerPlugin, + ActiveMQServerAddressPlugin, + ActiveMQServerQueuePlugin, + ActiveMQServerBindingPlugin, + ActiveMQServerMessagePlugin, + ActiveMQServerBridgePlugin, + ActiveMQServerCriticalPlugin { } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerQueuePlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerQueuePlugin.java new file mode 100644 index 00000000000..50cfea9c125 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerQueuePlugin.java @@ -0,0 +1,81 @@ +/** + * 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.artemis.core.server.plugin; + +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.security.SecurityAuth; +import org.apache.activemq.artemis.core.server.Queue; +import org.apache.activemq.artemis.core.server.QueueConfig; + +/** + * + */ +public interface ActiveMQServerQueuePlugin extends ActiveMQServerBasePlugin { + + /** + * Before a queue is created + * + * @param queueConfig + * @throws ActiveMQException + */ + default void beforeCreateQueue(QueueConfig queueConfig) throws ActiveMQException { + + } + + /** + * After a queue has been created + * + * @param queue The newly created queue + * @throws ActiveMQException + */ + default void afterCreateQueue(Queue queue) throws ActiveMQException { + + } + + /** + * Before a queue is destroyed + * + * @param queueName + * @param session + * @param checkConsumerCount + * @param removeConsumers + * @param autoDeleteAddress + * @throws ActiveMQException + */ + default void beforeDestroyQueue(SimpleString queueName, final SecurityAuth session, boolean checkConsumerCount, + boolean removeConsumers, boolean autoDeleteAddress) throws ActiveMQException { + + } + + /** + * After a queue has been destroyed + * + * @param queue + * @param address + * @param session + * @param checkConsumerCount + * @param removeConsumers + * @param autoDeleteAddress + * @throws ActiveMQException + */ + default void afterDestroyQueue(Queue queue, SimpleString address, final SecurityAuth session, boolean checkConsumerCount, + boolean removeConsumers, boolean autoDeleteAddress) throws ActiveMQException { + + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerSessionPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerSessionPlugin.java new file mode 100644 index 00000000000..965bb6cddfd --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerSessionPlugin.java @@ -0,0 +1,126 @@ +/** + * 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.artemis.core.server.plugin; + +import java.util.Map; +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.persistence.OperationContext; +import org.apache.activemq.artemis.core.server.ServerSession; +import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; +import org.apache.activemq.artemis.spi.core.protocol.SessionCallback; + +/** + * + */ +public interface ActiveMQServerSessionPlugin extends ActiveMQServerBasePlugin { + + /** + * Before a session is created. + * + * @param name + * @param username + * @param minLargeMessageSize + * @param connection + * @param autoCommitSends + * @param autoCommitAcks + * @param preAcknowledge + * @param xa + * @param defaultAddress + * @param callback + * @param autoCreateQueues + * @param context + * @param prefixes + * @throws ActiveMQException + */ + default void beforeCreateSession(String name, String username, int minLargeMessageSize, + RemotingConnection connection, boolean autoCommitSends, boolean autoCommitAcks, boolean preAcknowledge, + boolean xa, String defaultAddress, SessionCallback callback, boolean autoCreateQueues, OperationContext context, + Map prefixes) throws ActiveMQException { + + } + + /** + * After a session has been created. + * + * @param session The newly created session + * @throws ActiveMQException + */ + default void afterCreateSession(ServerSession session) throws ActiveMQException { + + } + + /** + * Before a session is closed + * + * @param session + * @param failed + * @throws ActiveMQException + */ + default void beforeCloseSession(ServerSession session, boolean failed) throws ActiveMQException { + + } + + /** + * After a session is closed + * + * @param session + * @param failed + * @throws ActiveMQException + */ + default void afterCloseSession(ServerSession session, boolean failed) throws ActiveMQException { + + } + + /** + * Before session metadata is added to the session + * + * @param session + * @param key + * @param data + * @throws ActiveMQException + */ + default void beforeSessionMetadataAdded(ServerSession session, String key, String data) throws ActiveMQException { + + } + + /** + * Called when adding session metadata fails because the metadata is a duplicate + * + * @param session + * @param key + * @param data + * @throws ActiveMQException + */ + default void duplicateSessionMetadataFailure(ServerSession session, String key, String data) throws ActiveMQException { + + } + + /** + * After session metadata is added to the session + * + * @param session + * @param key + * @param data + * @throws ActiveMQException + */ + default void afterSessionMetadataAdded(ServerSession session, String key, String data) throws ActiveMQException { + + } +} diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java index 114779d318f..996b9fe8b67 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java @@ -29,6 +29,7 @@ import java.util.Set; import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBasePlugin; import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy; import org.apache.activemq.artemis.api.core.BroadcastGroupConfiguration; import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration; @@ -649,7 +650,7 @@ public void testBrokerPlugin() throws Exception { deploymentManager.addDeployable(fc); deploymentManager.readConfiguration(); - List brokerPlugins = fc.getBrokerPlugins(); + List brokerPlugins = fc.getBrokerPlugins(); assertEquals(2, brokerPlugins.size()); assertTrue(brokerPlugins.get(0) instanceof EmptyPlugin1); assertTrue(brokerPlugins.get(1) instanceof EmptyPlugin2); From af90bc5f435c8e8acaa524602067dbbc5606a662 Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Wed, 1 Aug 2018 17:54:35 +0100 Subject: [PATCH 139/207] Make OpenFile Thread Safe --- .../file/JDBCSequentialFileFactoryDriver.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactoryDriver.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactoryDriver.java index 14ad25e8250..e736dcadd08 100644 --- a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactoryDriver.java +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFileFactoryDriver.java @@ -108,12 +108,14 @@ public List listFiles(String extension) throws Exception { * @throws SQLException */ public void openFile(JDBCSequentialFile file) throws SQLException { - final long fileId = fileExists(file); - if (fileId < 0) { - createFile(file); - } else { - file.setId(fileId); - loadFile(file); + synchronized (connection) { + final long fileId = fileExists(file); + if (fileId < 0) { + createFile(file); + } else { + file.setId(fileId); + loadFile(file); + } } } From d6d73c7f2372e211022c5aca7f0052e84170a6f0 Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Sun, 12 Aug 2018 15:12:11 +0100 Subject: [PATCH 140/207] ARTEMIS-2025 Ensure correct calculation of message body size --- .../core/client/impl/ClientMessageImpl.java | 3 ++- .../core/message/impl/CoreMessage.java | 2 +- .../tests/integration/ra/JMSContextTest.java | 26 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientMessageImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientMessageImpl.java index 8068aa94380..d14c64ee31b 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientMessageImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientMessageImpl.java @@ -189,7 +189,8 @@ public boolean isCompressed() { @Override public int getBodySize() { - return getBodyBuffer().writerIndex() - getBodyBuffer().readerIndex(); + checkEncode(); + return endOfBodyPosition - BUFFER_HEADER_SPACE; } @Override diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java index 09188281d40..323d9f4dd00 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java @@ -308,7 +308,7 @@ public void sendBuffer_1X(ByteBuf sendBuffer) { sendBuffer.readerIndex(0); } - private synchronized void checkEncode() { + protected synchronized void checkEncode() { if (!validBuffer) { encode(); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/JMSContextTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/JMSContextTest.java index 6956499e5b2..658b3239fae 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/JMSContextTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/JMSContextTest.java @@ -16,8 +16,13 @@ */ package org.apache.activemq.artemis.tests.integration.ra; +import javax.jms.JMSConsumer; import javax.jms.JMSContext; +import javax.jms.JMSProducer; import javax.jms.JMSRuntimeException; +import javax.jms.MessageFormatRuntimeException; +import javax.jms.Queue; +import javax.jms.TextMessage; import javax.transaction.TransactionManager; import java.util.HashSet; import java.util.Set; @@ -143,4 +148,25 @@ public void clientAckTestNoActiveJTATx() throws Exception { assertEquals(context.getSessionMode(), JMSContext.AUTO_ACKNOWLEDGE); } + @Test + public void testJMSContextConsumerThrowsMessageFormatExceptionOnMalformedBody() throws Exception { + Queue queue = createQueue(true, "ContextMalformedBodyTestQueue"); + + JMSContext context = qraConnectionFactory.createContext(); + JMSProducer producer = context.createProducer(); + + TextMessage message = context.createTextMessage("TestMessage"); + producer.send(queue, message); + + JMSConsumer consumer = context.createConsumer(queue); + + try { + consumer.receiveBody(Boolean.class); + fail("Should thrown MessageFormatException"); + } catch (MessageFormatRuntimeException mfre) { + // Do nothing test passed + } catch (Exception e) { + fail("Threw wrong exception, should be MessageFormatRuntimeException, instead got: " + e.getClass().getCanonicalName()); + } + } } From e15917129f92ecd1ee3e503d5896668b4743345d Mon Sep 17 00:00:00 2001 From: Arthur Fritz Santiago Date: Fri, 10 Aug 2018 11:49:16 -0300 Subject: [PATCH 141/207] [ARTEMIS-2022] Create count messages 'group by' this property filter --- .../api/core/management/QueueControl.java | 9 +++ .../management/impl/QueueControlImpl.java | 27 +++++++++ .../management/QueueControlTest.java | 60 +++++++++++++++++++ .../management/QueueControlUsingCoreTest.java | 5 ++ 4 files changed, 101 insertions(+) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java index 2aafcb144e7..c2d7ba6a2a6 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java @@ -296,6 +296,15 @@ public interface QueueControl { @Operation(desc = "Returns the number of the messages in the queue", impact = MBeanOperationInfo.INFO) long countMessages() throws Exception; + /** + * Counts the number of messages in this queue group by property received in the parameter using JSON serialization. + * In case no has property return amount of messages in the *_UNDEFINED_* + *
+ * Using {@code null} or an empty filter will count all messages from this queue. + */ + @Operation(desc = "Returns the number of the messages in the separate property value the queue", impact = MBeanOperationInfo.INFO) + String countMessagesProperty(@Parameter(name = "filter", desc = "This filter separate account messages") String filter) throws Exception; + /** * Removes the message corresponding to the specified message ID. * diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java index 16e948339e4..24afd9fa0cf 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java @@ -63,6 +63,7 @@ public class QueueControlImpl extends AbstractControl implements QueueControl { public static final int FLUSH_LIMIT = 500; + public static final String UNDEFINED = "*_UNDEFINED_*"; // Constants ----------------------------------------------------- // Attributes ---------------------------------------------------- @@ -711,6 +712,32 @@ public long countMessages(final String filterStr) throws Exception { } } + @Override + public String countMessagesProperty(final String filter) throws Exception { + checkStarted(); + clearIO(); + try { + try (LinkedListIterator iterator = queue.browserIterator()) { + Map result = new HashMap<>(); + String propertySearch = filter == null ? UNDEFINED : filter; + try { + while (iterator.hasNext()) { + MessageReference ref = iterator.next(); + String messageProperty = ref.getMessage().getStringProperty(propertySearch); + messageProperty = messageProperty == null ? UNDEFINED : messageProperty ; + Integer value = result.getOrDefault(messageProperty, 0); + result.put(messageProperty, ++value); + } + } catch (NoSuchElementException ignored) { + // this could happen through paging browsing + } + return JsonUtil.toJsonObject(result).toString(); + } + } finally { + blockOnIO(); + } + } + @Override public boolean removeMessage(final long messageID) throws Exception { checkStarted(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java index 9a68badad35..c3406672db1 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java @@ -1781,6 +1781,66 @@ public void testCountMessagesWithInvalidFilter() throws Exception { session.deleteQueue(queue); } + @Test + public void testCountMessagesPropertyExist() throws Exception { + String key = new String("key_group"); + String valueGroup1 = "group_1"; + String valueGroup2 = "group_2"; + + SimpleString address = RandomUtil.randomSimpleString(); + SimpleString queue = RandomUtil.randomSimpleString(); + + session.createQueue(address, queue, null, false); + ClientProducer producer = session.createProducer(address); + + for (int i = 0; i < 100; i++) { + ClientMessage msg = session.createMessage(false); + if(i % 3 == 0){ + msg.putStringProperty(key, valueGroup1); + } else { + msg.putStringProperty(key, valueGroup2); + } + producer.send(msg); + } + + for (int i = 0; i < 20; i++) { + ClientMessage msg = session.createMessage(false); + producer.send(msg); + } + + QueueControl queueControl = createManagementControl(address, queue); + Assert.assertEquals(120, getMessageCount(queueControl)); + String result = queueControl.countMessagesProperty(key); + JsonObject jsonObject = JsonUtil.readJsonObject(result); + Assert.assertEquals(34, jsonObject.getInt(valueGroup1)); + Assert.assertEquals(66, jsonObject.getInt(valueGroup2)); + Assert.assertEquals(20, jsonObject.getInt("*_UNDEFINED_*")); + session.deleteQueue(queue); + } + + @Test + public void testCountMessagesPropertyWithNullFilter() throws Exception { + SimpleString address = RandomUtil.randomSimpleString(); + SimpleString queue = RandomUtil.randomSimpleString(); + + session.createQueue(address, queue, null, false); + ClientProducer producer = session.createProducer(address); + + for (int i = 0; i < 100; i++) { + ClientMessage msg = session.createMessage(false); + msg.putStringProperty(RandomUtil.randomString(), RandomUtil.randomString()); + producer.send(msg); + } + + QueueControl queueControl = createManagementControl(address, queue); + Assert.assertEquals(100, getMessageCount(queueControl)); + String result = queueControl.countMessagesProperty(null); + JsonObject jsonObject = JsonUtil.readJsonObject(result); + Assert.assertEquals(100, jsonObject.getInt("*_UNDEFINED_*")); + session.deleteQueue(queue); + } + + @Test public void testExpireMessagesWithFilter() throws Exception { SimpleString key = new SimpleString("key"); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java index dcdb2f108fb..b0efb93f388 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java @@ -101,6 +101,11 @@ public long countMessages() throws Exception { return (Long) proxy.invokeOperation(Long.class, "countMessages"); } + @Override + public String countMessagesProperty(final String filter) throws Exception { + return (String) proxy.invokeOperation(String.class, "countMessagesProperty", filter); + } + @Override public boolean expireMessage(final long messageID) throws Exception { return (Boolean) proxy.invokeOperation("expireMessage", messageID); From 349477ed31b8d4993dca757981419feec341d014 Mon Sep 17 00:00:00 2001 From: Arthur Fritz Santiago Date: Fri, 10 Aug 2018 12:09:32 -0300 Subject: [PATCH 142/207] [ARTEMIS-2022] Ajust checkstyle --- .../activemq/artemis/core/management/impl/QueueControlImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java index 24afd9fa0cf..ed467793dc1 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java @@ -724,7 +724,7 @@ public String countMessagesProperty(final String filter) throws Exception { while (iterator.hasNext()) { MessageReference ref = iterator.next(); String messageProperty = ref.getMessage().getStringProperty(propertySearch); - messageProperty = messageProperty == null ? UNDEFINED : messageProperty ; + messageProperty = messageProperty == null ? UNDEFINED : messageProperty; Integer value = result.getOrDefault(messageProperty, 0); result.put(messageProperty, ++value); } From 24a28da09f1e7f6aa82f7952f2d4d351d22d11a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Fri, 10 Aug 2018 22:37:26 +0100 Subject: [PATCH 143/207] ARTEMIS-2022 - Enhancements Fix checkstyle Avoid duplicated logic Ability to filter and group Instantiate SimpleString property key once Get property value via getObjectProprty to ensure all special mapped properties such as in AMQPMessage would return Avoid a custom string to represent null, instead rely on Java's representation "null" by using Objects.toString to get the string value of the property value used to group by. --- .../activemq/artemis/api/core/JsonUtil.java | 2 +- .../api/core/management/QueueControl.java | 8 +-- .../management/impl/QueueControlImpl.java | 62 ++++++++----------- .../management/QueueControlTest.java | 10 +-- .../management/QueueControlUsingCoreTest.java | 4 +- 5 files changed, 38 insertions(+), 48 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/JsonUtil.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/JsonUtil.java index fdb5b52797e..eed944f29b6 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/JsonUtil.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/JsonUtil.java @@ -258,7 +258,7 @@ public static JsonObject toJsonObject(Map map) { JsonObjectBuilder jsonObjectBuilder = JsonLoader.createObjectBuilder(); if (map != null) { for (Map.Entry entry : map.entrySet()) { - addToObject(entry.getKey(), entry.getValue(), jsonObjectBuilder); + addToObject(String.valueOf(entry.getKey()), entry.getValue(), jsonObjectBuilder); } } return jsonObjectBuilder.build(); diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java index c2d7ba6a2a6..d213446c01f 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java @@ -297,13 +297,13 @@ public interface QueueControl { long countMessages() throws Exception; /** - * Counts the number of messages in this queue group by property received in the parameter using JSON serialization. - * In case no has property return amount of messages in the *_UNDEFINED_* + * Counts the number of messages in this queue matching the specified filter, grouped by the given property field. + * In case of null property will be grouped in "null" *
* Using {@code null} or an empty filter will count all messages from this queue. */ - @Operation(desc = "Returns the number of the messages in the separate property value the queue", impact = MBeanOperationInfo.INFO) - String countMessagesProperty(@Parameter(name = "filter", desc = "This filter separate account messages") String filter) throws Exception; + @Operation(desc = "Returns the number of the messages in the queue matching the given filter, grouped by the given property field", impact = MBeanOperationInfo.INFO) + String countMessages(@Parameter(name = "filter", desc = "This filter separate account messages") String filter, @Parameter(name = "groupByProperty", desc = "This property to group by") String groupByProperty) throws Exception; /** * Removes the message corresponding to the specified message ID. diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java index ed467793dc1..7377846208b 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java @@ -63,7 +63,7 @@ public class QueueControlImpl extends AbstractControl implements QueueControl { public static final int FLUSH_LIMIT = 500; - public static final String UNDEFINED = "*_UNDEFINED_*"; + // Constants ----------------------------------------------------- // Attributes ---------------------------------------------------- @@ -683,56 +683,46 @@ public long countMessages() throws Exception { @Override public long countMessages(final String filterStr) throws Exception { - checkStarted(); + Long value = intenalCountMessages(filterStr, null).get(null); + return value == null ? 0 : value; + } + @Override + public String countMessages(final String filterStr, final String groupByProperty) throws Exception { + return JsonUtil.toJsonObject(intenalCountMessages(filterStr, groupByProperty)).toString(); + } + + private Map intenalCountMessages(final String filterStr, final String groupByPropertyStr) throws Exception { + checkStarted(); clearIO(); + Map result = new HashMap<>(); + try { Filter filter = FilterImpl.createFilter(filterStr); - if (filter == null) { - return getMessageCount(); + SimpleString groupByProperty = SimpleString.toSimpleString(groupByPropertyStr); + if (filter == null && groupByProperty == null) { + result.put(null, getMessageCount()); } else { try (LinkedListIterator iterator = queue.browserIterator()) { - int count = 0; - try { while (iterator.hasNext()) { - MessageReference ref = iterator.next(); - if (filter.match(ref.getMessage())) { - count++; + Message message = iterator.next().getMessage(); + if (filter == null || filter.match(message)) { + if (groupByProperty == null) { + result.compute(null, (k,v) -> v == null ? 1 : ++v); + } else { + Object value = message.getObjectProperty(groupByProperty); + String valueStr = value == null ? null : value.toString(); + result.compute(valueStr, (k,v) -> v == null ? 1 : ++v); + } } } } catch (NoSuchElementException ignored) { // this could happen through paging browsing } - return count; - } - } - } finally { - blockOnIO(); - } - } - - @Override - public String countMessagesProperty(final String filter) throws Exception { - checkStarted(); - clearIO(); - try { - try (LinkedListIterator iterator = queue.browserIterator()) { - Map result = new HashMap<>(); - String propertySearch = filter == null ? UNDEFINED : filter; - try { - while (iterator.hasNext()) { - MessageReference ref = iterator.next(); - String messageProperty = ref.getMessage().getStringProperty(propertySearch); - messageProperty = messageProperty == null ? UNDEFINED : messageProperty; - Integer value = result.getOrDefault(messageProperty, 0); - result.put(messageProperty, ++value); - } - } catch (NoSuchElementException ignored) { - // this could happen through paging browsing } - return JsonUtil.toJsonObject(result).toString(); } + return result; } finally { blockOnIO(); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java index c3406672db1..5ba0b39ba90 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlTest.java @@ -1795,7 +1795,7 @@ public void testCountMessagesPropertyExist() throws Exception { for (int i = 0; i < 100; i++) { ClientMessage msg = session.createMessage(false); - if(i % 3 == 0){ + if (i % 3 == 0) { msg.putStringProperty(key, valueGroup1); } else { msg.putStringProperty(key, valueGroup2); @@ -1810,11 +1810,11 @@ public void testCountMessagesPropertyExist() throws Exception { QueueControl queueControl = createManagementControl(address, queue); Assert.assertEquals(120, getMessageCount(queueControl)); - String result = queueControl.countMessagesProperty(key); + String result = queueControl.countMessages(null, key); JsonObject jsonObject = JsonUtil.readJsonObject(result); Assert.assertEquals(34, jsonObject.getInt(valueGroup1)); Assert.assertEquals(66, jsonObject.getInt(valueGroup2)); - Assert.assertEquals(20, jsonObject.getInt("*_UNDEFINED_*")); + Assert.assertEquals(20, jsonObject.getInt("null")); session.deleteQueue(queue); } @@ -1834,9 +1834,9 @@ public void testCountMessagesPropertyWithNullFilter() throws Exception { QueueControl queueControl = createManagementControl(address, queue); Assert.assertEquals(100, getMessageCount(queueControl)); - String result = queueControl.countMessagesProperty(null); + String result = queueControl.countMessages(null,null); JsonObject jsonObject = JsonUtil.readJsonObject(result); - Assert.assertEquals(100, jsonObject.getInt("*_UNDEFINED_*")); + Assert.assertEquals(100, jsonObject.getInt("null")); session.deleteQueue(queue); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java index b0efb93f388..0a62334fafe 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java @@ -102,8 +102,8 @@ public long countMessages() throws Exception { } @Override - public String countMessagesProperty(final String filter) throws Exception { - return (String) proxy.invokeOperation(String.class, "countMessagesProperty", filter); + public String countMessages(final String filter, final String groupByFilter) throws Exception { + return (String) proxy.invokeOperation(String.class, "countMessages", filter, groupByFilter); } @Override From e1ba608d64883f071442340ed091441187f48a01 Mon Sep 17 00:00:00 2001 From: Robbie Gemmell Date: Mon, 13 Aug 2018 14:01:01 +0100 Subject: [PATCH 144/207] ARTEMIS-2027: handle aborted AMQP deliveries --- .../artemis-amqp-protocol/pom.xml | 5 ++ .../proton/ProtonServerReceiverContext.java | 17 +++++ .../ProtonServerReceiverContextTest.java | 73 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContextTest.java diff --git a/artemis-protocols/artemis-amqp-protocol/pom.xml b/artemis-protocols/artemis-amqp-protocol/pom.xml index 5f91a51fddb..e383b3740ac 100644 --- a/artemis-protocols/artemis-amqp-protocol/pom.xml +++ b/artemis-protocols/artemis-amqp-protocol/pom.xml @@ -123,5 +123,10 @@ org.osgi osgi.cmpn + + org.mockito + mockito-core + test + diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java index aad89a8d0cb..c3df1a73d02 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java @@ -218,6 +218,23 @@ public void onMessage(Delivery delivery) throws ActiveMQAMQPException { if (!delivery.isReadable()) { return; } + + if (delivery.isAborted()) { + receiver = ((Receiver) delivery.getLink()); + + // Aborting implicitly remotely settles, so advance + // receiver to the next delivery and settle locally. + receiver.advance(); + delivery.settle(); + + // Replenish the credit if not doing a drain + if (!receiver.getDrain()) { + receiver.flow(1); + } + + return; + } + if (delivery.isPartial()) { return; } diff --git a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContextTest.java b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContextTest.java new file mode 100644 index 00000000000..88dfe3a9d37 --- /dev/null +++ b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContextTest.java @@ -0,0 +1,73 @@ +/* + * 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.artemis.protocol.amqp.proton; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPException; +import org.apache.qpid.proton.engine.Delivery; +import org.apache.qpid.proton.engine.Receiver; +import org.junit.Test; + +public class ProtonServerReceiverContextTest { + + @Test + public void testOnMessageWithAbortedDelivery() throws Exception { + doOnMessageWithAbortedDeliveryTestImpl(false); + } + + @Test + public void testOnMessageWithAbortedDeliveryDrain() throws Exception { + doOnMessageWithAbortedDeliveryTestImpl(true); + } + + private void doOnMessageWithAbortedDeliveryTestImpl(boolean drain) throws ActiveMQAMQPException { + Receiver mockReceiver = mock(Receiver.class); + AMQPConnectionContext mockConnContext = mock(AMQPConnectionContext.class); + + when(mockConnContext.getAmqpCredits()).thenReturn(100); + when(mockConnContext.getAmqpLowCredits()).thenReturn(30); + + ProtonServerReceiverContext rc = new ProtonServerReceiverContext(null, mockConnContext, null, mockReceiver); + + Delivery mockDelivery = mock(Delivery.class); + when(mockDelivery.isReadable()).thenReturn(true); + when(mockDelivery.isAborted()).thenReturn(true); + when(mockDelivery.isPartial()).thenReturn(true); + when(mockDelivery.getLink()).thenReturn(mockReceiver); + + if (drain) { + when(mockReceiver.getDrain()).thenReturn(true); + } + + rc.onMessage(mockDelivery); + + verify(mockReceiver, times(1)).advance(); + verify(mockDelivery, times(1)).settle(); + + verify(mockReceiver, times(1)).getDrain(); + if (!drain) { + verify(mockReceiver, times(1)).flow(1); + } + verifyNoMoreInteractions(mockReceiver); + } + +} From 754671bddb6d7c400641e4f26e250c8264bef0ec Mon Sep 17 00:00:00 2001 From: Matthias Berndt Date: Mon, 13 Aug 2018 12:50:37 +0200 Subject: [PATCH 145/207] NO-JIRA fix typo --- docs/user-manual/en/address-model.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user-manual/en/address-model.md b/docs/user-manual/en/address-model.md index ac5e49bf2ab..77ca68c6d89 100644 --- a/docs/user-manual/en/address-model.md +++ b/docs/user-manual/en/address-model.md @@ -301,7 +301,7 @@ Parameter|Description `auto-create-addresses` element to the `address-setting` you want the broker to automatically create. -- (Optional) Add the `address-setting` if it does not exits. Use the match +- (Optional) Add the `address-setting` if it does not exist. Use the match parameter and the [wildcard syntax](wildcard-syntax.md) to match more than one specific address. @@ -328,7 +328,7 @@ with `/news/politics/` will be automatically created by the broker. `auto-delete-addresses` element to the `address-setting` you want the broker to automatically create. -- (Optional) Add the `address-setting` if it does not exits. Use the match +- (Optional) Add the `address-setting` if it does not exist. Use the match parameter and the [wildcard syntax](wildcard-syntax.md) to match more than one specific address. From df583922f5d1975818a55b05f1b019f5c5b4e748 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 9 Aug 2018 21:32:23 -0500 Subject: [PATCH 146/207] ARTEMIS-2023 Support 1x prefixes for JMS dests created using session In some cases users who migrate from 1.x to 2.x may still want to keep the legacy prefixes for their JMS destinations (i.e. "jms.queue.", "jms.topic.", etc.). This commit adds a boolean on our ConnectionFactory implementation so that it will use the old prefixes when invoking the queue/topic creation methods on the Session implementation. --- .../api/core/client/ActiveMQClient.java | 2 + .../core/protocol/core/impl/PacketImpl.java | 2 + .../jms/client/ActiveMQConnection.java | 9 ++- .../jms/client/ActiveMQConnectionFactory.java | 23 ++++++-- .../jms/client/ActiveMQDestination.java | 12 ++++ .../artemis/jms/client/ActiveMQSession.java | 37 ++++++++++-- .../jms/client/ActiveMQXAConnection.java | 3 +- .../artemis/jms/client/ActiveMQXASession.java | 3 +- .../ConnectionFactoryConfiguration.java | 4 ++ .../ConnectionFactoryConfigurationImpl.java | 22 +++++++- .../jms/server/impl/JMSServerManagerImpl.java | 1 + .../ActiveMQRAManagedConnectionFactory.java | 8 +++ .../artemis/ra/ActiveMQResourceAdapter.java | 26 +++++++++ .../ra/ConnectionFactoryProperties.java | 25 +++++++++ .../integration/jms/SimpleJNDIClientTest.java | 56 +++++++++++++++++++ .../ra/ActiveMQResourceAdapterConfigTest.java | 6 ++ 16 files changed, 222 insertions(+), 17 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java index cb107680320..d24efff84f2 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java @@ -135,6 +135,8 @@ public final class ActiveMQClient { public static final boolean DEFAULT_USE_TOPOLOGY_FOR_LOADBALANCING = true; + public static final boolean DEFAULT_ENABLE_1X_PREFIXES = false; + public static final String THREAD_POOL_MAX_SIZE_PROPERTY_KEY = "activemq.artemis.client.global.thread.pool.max.size"; public static final String SCHEDULED_THREAD_POOL_SIZE_PROPERTY_KEY = "activemq.artemis.client.global.scheduled.thread.pool.core.size"; diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/PacketImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/PacketImpl.java index c275e213b18..87ba0c3a754 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/PacketImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/PacketImpl.java @@ -35,7 +35,9 @@ public class PacketImpl implements Packet { public static final SimpleString OLD_QUEUE_PREFIX = new SimpleString("jms.queue."); + public static final SimpleString OLD_TEMP_QUEUE_PREFIX = new SimpleString("jms.tempqueue."); public static final SimpleString OLD_TOPIC_PREFIX = new SimpleString("jms.topic."); + public static final SimpleString OLD_TEMP_TOPIC_PREFIX = new SimpleString("jms.temptopic."); // The minimal size for all the packets, Common data for all the packets (look at // PacketImpl.encode) diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java index 357b7f6cfde..ec67cc33d9c 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java @@ -129,6 +129,8 @@ public class ActiveMQConnection extends ActiveMQConnectionForContextImpl impleme private final boolean cacheDestinations; + private final boolean enable1xPrefixes; + private ClientSession initialSession; private final Exception creationStack; @@ -147,6 +149,7 @@ public ActiveMQConnection(final ConnectionFactoryOptions options, final int dupsOKBatchSize, final int transactionBatchSize, final boolean cacheDestinations, + final boolean enable1xPrefixes, final ClientSessionFactory sessionFactory) { this.options = options; @@ -170,6 +173,8 @@ public ActiveMQConnection(final ConnectionFactoryOptions options, this.cacheDestinations = cacheDestinations; + this.enable1xPrefixes = enable1xPrefixes; + creationStack = new Exception(); } @@ -661,9 +666,9 @@ protected ActiveMQSession createAMQSession(boolean isXA, ClientSession session, int type) { if (isXA) { - return new ActiveMQXASession(options, this, transacted, true, acknowledgeMode, cacheDestinations, session, type); + return new ActiveMQXASession(options, this, transacted, true, acknowledgeMode, cacheDestinations, enable1xPrefixes, session, type); } else { - return new ActiveMQSession(options, this, transacted, false, acknowledgeMode, cacheDestinations, session, type); + return new ActiveMQSession(options, this, transacted, false, acknowledgeMode, cacheDestinations, enable1xPrefixes, session, type); } } diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java index 2ab3bf7e57c..db1fc782690 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java @@ -92,6 +92,8 @@ public class ActiveMQConnectionFactory extends JNDIStorable implements Connectio private boolean ignoreJTA; + private boolean enable1xPrefixes = ActiveMQClient.DEFAULT_ENABLE_1X_PREFIXES; + @Override public void writeExternal(ObjectOutput out) throws IOException { URI uri = toURI(); @@ -477,6 +479,15 @@ public synchronized void setCacheDestinations(final boolean cacheDestinations) { this.cacheDestinations = cacheDestinations; } + public synchronized boolean isEnable1xPrefixes() { + return this.enable1xPrefixes; + } + + public synchronized void setEnable1xPrefixes(final boolean enable1xPrefixes) { + checkWrite(); + this.enable1xPrefixes = enable1xPrefixes; + } + public synchronized long getClientFailureCheckPeriod() { return serverLocator.getClientFailureCheckPeriod(); } @@ -824,19 +835,19 @@ protected synchronized ActiveMQConnection createConnectionInternal(final String if (isXA) { if (type == ActiveMQConnection.TYPE_GENERIC_CONNECTION) { - connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, factory); + connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory); } else if (type == ActiveMQConnection.TYPE_QUEUE_CONNECTION) { - connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, factory); + connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory); } else if (type == ActiveMQConnection.TYPE_TOPIC_CONNECTION) { - connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, factory); + connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory); } } else { if (type == ActiveMQConnection.TYPE_GENERIC_CONNECTION) { - connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, factory); + connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory); } else if (type == ActiveMQConnection.TYPE_QUEUE_CONNECTION) { - connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, factory); + connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory); } else if (type == ActiveMQConnection.TYPE_TOPIC_CONNECTION) { - connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, factory); + connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory); } } diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java index c5e4884ce67..81aada16ffe 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java @@ -236,6 +236,18 @@ public static ActiveMQTemporaryTopic createTemporaryTopic(final ActiveMQSession return createTemporaryTopic(address, session); } + public static ActiveMQTemporaryQueue createTemporaryQueue(final ActiveMQSession session, final String prefix) { + String address = prefix + UUID.randomUUID().toString(); + + return createTemporaryQueue(address, session); + } + + public static ActiveMQTemporaryTopic createTemporaryTopic(final ActiveMQSession session, final String prefix) { + String address = prefix + UUID.randomUUID().toString(); + + return createTemporaryTopic(address, session); + } + public static ActiveMQTemporaryTopic createTemporaryTopic(String address, final ActiveMQSession session) { return new ActiveMQTemporaryTopic(address, session); } diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java index 065c7a3fba0..8d34ca1adb0 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java @@ -61,6 +61,7 @@ import org.apache.activemq.artemis.api.core.client.ClientSession.AddressQuery; import org.apache.activemq.artemis.api.core.client.ClientSession.QueueQuery; import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl; import org.apache.activemq.artemis.selector.filter.FilterException; import org.apache.activemq.artemis.selector.impl.SelectorParser; import org.apache.activemq.artemis.utils.SelectorTranslator; @@ -101,6 +102,8 @@ public class ActiveMQSession implements QueueSession, TopicSession { private final boolean cacheDestination; + private final boolean enable1xPrefixes; + private final Map topicCache = new ConcurrentHashMap<>(); private final Map queueCache = new ConcurrentHashMap<>(); @@ -113,6 +116,7 @@ protected ActiveMQSession(final ConnectionFactoryOptions options, final boolean xa, final int ackMode, final boolean cacheDestination, + final boolean enable1xPrefixes, final ClientSession session, final int sessionType) { this.options = options; @@ -130,6 +134,8 @@ protected ActiveMQSession(final ConnectionFactoryOptions options, this.xa = xa; this.cacheDestination = cacheDestination; + + this.enable1xPrefixes = enable1xPrefixes; } // Session implementation ---------------------------------------- @@ -885,7 +891,12 @@ public TemporaryQueue createTemporaryQueue() throws JMSException { } try { - ActiveMQTemporaryQueue queue = ActiveMQDestination.createTemporaryQueue(this); + final ActiveMQTemporaryQueue queue; + if (enable1xPrefixes) { + queue = ActiveMQDestination.createTemporaryQueue(this, PacketImpl.OLD_TEMP_QUEUE_PREFIX.toString()); + } else { + queue = ActiveMQDestination.createTemporaryQueue(this); + } SimpleString simpleAddress = queue.getSimpleAddress(); @@ -907,7 +918,12 @@ public TemporaryTopic createTemporaryTopic() throws JMSException { } try { - ActiveMQTemporaryTopic topic = ActiveMQDestination.createTemporaryTopic(this); + final ActiveMQTemporaryTopic topic; + if (enable1xPrefixes) { + topic = ActiveMQDestination.createTemporaryTopic(this, PacketImpl.OLD_TEMP_TOPIC_PREFIX.toString()); + } else { + topic = ActiveMQDestination.createTemporaryTopic(this); + } SimpleString simpleAddress = topic.getSimpleAddress(); @@ -1133,12 +1149,17 @@ private void checkClosed() throws JMSException { } private ActiveMQQueue lookupQueue(final String queueName, boolean isTemporary) throws ActiveMQException { + String queueNameToUse = queueName; + if (enable1xPrefixes) { + queueNameToUse = (isTemporary ? PacketImpl.OLD_TEMP_QUEUE_PREFIX.toString() : PacketImpl.OLD_QUEUE_PREFIX.toString()) + queueName; + } + ActiveMQQueue queue; if (isTemporary) { - queue = ActiveMQDestination.createTemporaryQueue(queueName); + queue = ActiveMQDestination.createTemporaryQueue(queueNameToUse); } else { - queue = ActiveMQDestination.createQueue(queueName); + queue = ActiveMQDestination.createQueue(queueNameToUse); } QueueQuery response = session.queueQuery(queue.getSimpleAddress()); @@ -1151,13 +1172,17 @@ private ActiveMQQueue lookupQueue(final String queueName, boolean isTemporary) t } private ActiveMQTopic lookupTopic(final String topicName, final boolean isTemporary) throws ActiveMQException { + String topicNameToUse = topicName; + if (enable1xPrefixes) { + topicNameToUse = (isTemporary ? PacketImpl.OLD_TEMP_TOPIC_PREFIX.toString() : PacketImpl.OLD_TOPIC_PREFIX.toString()) + topicName; + } ActiveMQTopic topic; if (isTemporary) { - topic = ActiveMQDestination.createTemporaryTopic(topicName); + topic = ActiveMQDestination.createTemporaryTopic(topicNameToUse); } else { - topic = ActiveMQDestination.createTopic(topicName); + topic = ActiveMQDestination.createTopic(topicNameToUse); } AddressQuery query = session.addressQuery(topic.getSimpleAddress()); diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQXAConnection.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQXAConnection.java index fcc9bb25168..0d6158ed52a 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQXAConnection.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQXAConnection.java @@ -42,8 +42,9 @@ public ActiveMQXAConnection(final ConnectionFactoryOptions options, final int dupsOKBatchSize, final int transactionBatchSize, final boolean cacheDestinations, + final boolean enable1xNaming, final ClientSessionFactory sessionFactory) { - super(options, username, password, connectionType, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, sessionFactory); + super(options, username, password, connectionType, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xNaming, sessionFactory); } @Override diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQXASession.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQXASession.java index 6ec936e84be..699ecde9c32 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQXASession.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQXASession.java @@ -37,8 +37,9 @@ protected ActiveMQXASession(final ConnectionFactoryOptions options, boolean xa, int ackMode, boolean cacheDestinations, + boolean enable1xNaming, ClientSession session, int sessionType) { - super(options, connection, transacted, xa, ackMode, cacheDestinations, session, sessionType); + super(options, connection, transacted, xa, ackMode, cacheDestinations, enable1xNaming, session, sessionType); } } diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/ConnectionFactoryConfiguration.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/ConnectionFactoryConfiguration.java index 51fa0f387bc..43b9904b21a 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/ConnectionFactoryConfiguration.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/ConnectionFactoryConfiguration.java @@ -189,4 +189,8 @@ public interface ConnectionFactoryConfiguration extends EncodingSupport { int getInitialMessagePacketSize(); ConnectionFactoryConfiguration setInitialMessagePacketSize(int size); + + boolean isEnable1xPrefixes(); + + ConnectionFactoryConfiguration setEnable1xPrefixes(boolean enable1xPrefixes); } diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java index 4faf13d320a..2f546ec163f 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java @@ -124,6 +124,8 @@ public class ConnectionFactoryConfigurationImpl implements ConnectionFactoryConf private int initialMessagePacketSize = ActiveMQClient.DEFAULT_INITIAL_MESSAGE_PACKET_SIZE; + private boolean enable1xPrefixes = ActiveMQClient.DEFAULT_ENABLE_1X_PREFIXES; + // Static -------------------------------------------------------- // Constructors -------------------------------------------------- @@ -532,6 +534,17 @@ public ConnectionFactoryConfiguration setGroupID(final String groupID) { return this; } + @Override + public boolean isEnable1xPrefixes() { + return enable1xPrefixes; + } + + @Override + public ConnectionFactoryConfiguration setEnable1xPrefixes(final boolean enable1xPrefixes) { + this.enable1xPrefixes = enable1xPrefixes; + return this; + } + // Encoding Support Implementation -------------------------------------------------------------- @Override @@ -623,6 +636,8 @@ public void decode(final ActiveMQBuffer buffer) { deserializationBlackList = BufferHelper.readNullableSimpleStringAsString(buffer); deserializationWhiteList = BufferHelper.readNullableSimpleStringAsString(buffer); + + enable1xPrefixes = buffer.readableBytes() > 0 ? buffer.readBoolean() : null; } @Override @@ -712,6 +727,8 @@ public void encode(final ActiveMQBuffer buffer) { BufferHelper.writeAsNullableSimpleString(buffer, deserializationBlackList); BufferHelper.writeAsNullableSimpleString(buffer, deserializationWhiteList); + + buffer.writeBoolean(enable1xPrefixes); } @Override @@ -825,7 +842,10 @@ public int getEncodeSize() { BufferHelper.sizeOfNullableSimpleString(deserializationBlackList) + - BufferHelper.sizeOfNullableSimpleString(deserializationWhiteList); + BufferHelper.sizeOfNullableSimpleString(deserializationWhiteList) + + + DataConstants.SIZE_BOOLEAN; + // enable1xPrefixes; return size; } diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/impl/JMSServerManagerImpl.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/impl/JMSServerManagerImpl.java index 0e608807c2f..58acbbceda3 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/impl/JMSServerManagerImpl.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/impl/JMSServerManagerImpl.java @@ -1220,6 +1220,7 @@ protected ActiveMQConnectionFactory internalCreateCFPOJO(final ConnectionFactory cf.setDeserializationBlackList(cfConfig.getDeserializationBlackList()); cf.setDeserializationWhiteList(cfConfig.getDeserializationWhiteList()); cf.setInitialMessagePacketSize(cfConfig.getInitialMessagePacketSize()); + cf.setEnable1xPrefixes(cfConfig.isEnable1xPrefixes()); return cf; } diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAManagedConnectionFactory.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAManagedConnectionFactory.java index ef5698b1c25..af3dcfbd435 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAManagedConnectionFactory.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAManagedConnectionFactory.java @@ -592,6 +592,14 @@ public void setCacheDestinations(final Boolean cacheDestinations) { mcfProperties.setCacheDestinations(cacheDestinations); } + public Boolean isEnable1xPrefixes() { + return mcfProperties.isEnable1xPrefixes(); + } + + public void setEnable1xPrefixes(final Boolean enable1xPrefixes) { + mcfProperties.setEnable1xPrefixes(enable1xPrefixes); + } + public Integer getScheduledThreadPoolMaxSize() { return mcfProperties.getScheduledThreadPoolMaxSize(); } diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java index 5326a6b1eaa..8c31c2abb52 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java @@ -661,6 +661,32 @@ public Boolean isCacheDestinations() { return raProperties.isCacheDestinations(); } + /** + * Set enable1xPrefixes + * + * @param enable1xPrefixes The value + */ + public void setEnable1xPrefixes(final Boolean enable1xPrefixes) { + if (logger.isTraceEnabled()) { + ActiveMQRALogger.LOGGER.trace("setEnable1xPrefixes(" + enable1xPrefixes + ")"); + } + + raProperties.setEnable1xPrefixes(enable1xPrefixes); + } + + /** + * Get isCacheDestinations + * + * @return The value + */ + public Boolean isEnable1xPrefixes() { + if (logger.isTraceEnabled()) { + ActiveMQRALogger.LOGGER.trace("isEnable1xPrefixes()"); + } + + return raProperties.isEnable1xPrefixes(); + } + /** * Set compressLargeMessage * diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ConnectionFactoryProperties.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ConnectionFactoryProperties.java index ba0484aa78e..cda5afcec9e 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ConnectionFactoryProperties.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ConnectionFactoryProperties.java @@ -114,6 +114,8 @@ public class ConnectionFactoryProperties implements ConnectionFactoryOptions { private Boolean cacheDestinations; + private Boolean enable1xPrefixes; + private Integer initialMessagePacketSize; private Integer scheduledThreadPoolMaxSize; @@ -629,6 +631,21 @@ public void setCacheDestinations(final Boolean cacheDestinations) { this.cacheDestinations = cacheDestinations; } + public Boolean isEnable1xPrefixes() { + if (ConnectionFactoryProperties.trace) { + ActiveMQRALogger.LOGGER.trace("isEnable1xPrefixes()"); + } + return enable1xPrefixes; + } + + public void setEnable1xPrefixes(final Boolean enable1xPrefixes) { + if (ConnectionFactoryProperties.trace) { + ActiveMQRALogger.LOGGER.trace("setEnable1xPrefixes(" + enable1xPrefixes + ")"); + } + hasBeenUpdated = true; + this.enable1xPrefixes = enable1xPrefixes; + } + public Integer getScheduledThreadPoolMaxSize() { if (ConnectionFactoryProperties.trace) { ActiveMQRALogger.LOGGER.trace("getScheduledThreadPoolMaxSize()"); @@ -975,6 +992,13 @@ else if (!protocolManagerFactoryStr.equals(other.protocolManagerFactoryStr)) return false; } else if (!deserializationWhiteList.equals(other.deserializationWhiteList)) return false; + + if (this.enable1xPrefixes == null) { + if (other.enable1xPrefixes != null) + return false; + } else if (!this.enable1xPrefixes.equals(other.enable1xPrefixes)) + return false; + return true; } @@ -1027,6 +1051,7 @@ public int hashCode() { result = prime * result + ((connectionParameters == null) ? 0 : connectionParameters.hashCode()); result = prime * result + ((deserializationBlackList == null) ? 0 : deserializationBlackList.hashCode()); result = prime * result + ((deserializationWhiteList == null) ? 0 : deserializationWhiteList.hashCode()); + result = prime * result + ((enable1xPrefixes == null) ? 0 : enable1xPrefixes.hashCode()); return result; } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java index 54109b1e9f6..dc48d9a13d3 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java @@ -49,6 +49,7 @@ import org.apache.activemq.artemis.api.jms.JMSFactoryType; import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.ha.SharedStoreMasterPolicyConfiguration; +import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl; import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; import org.apache.activemq.artemis.core.security.Role; import org.apache.activemq.artemis.core.server.ActiveMQServer; @@ -664,4 +665,59 @@ public void testProviderUrlDefaultAndCustom() throws NamingException, JMSExcepti testContext(ctx, "myConnectionFactory", JMSFactoryType.CF); } + + @Test + public void test1xNaming() throws NamingException, JMSException { + liveService.getSecurityStore().setSecurityEnabled(false); + Hashtable props = new Hashtable<>(); + props.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory"); + props.put("connectionFactory.ConnectionFactory", "vm://0?enable1xPrefixes=true"); + props.put("connectionFactory.ConnectionFactory2", "vm://0"); + Context ctx = new InitialContext(props); + + ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("ConnectionFactory"); + ((ActiveMQConnectionFactory)connectionFactory).setEnable1xPrefixes(true); + Connection connection = connectionFactory.createConnection(); + Session session = connection.createSession(); + + assertTrue(session.createQueue("testQueue").getQueueName().startsWith(PacketImpl.OLD_QUEUE_PREFIX.toString())); + assertTrue(session.createTemporaryQueue().getQueueName().startsWith(PacketImpl.OLD_TEMP_QUEUE_PREFIX.toString())); + assertTrue(session.createTopic("testTopic").getTopicName().startsWith(PacketImpl.OLD_TOPIC_PREFIX.toString())); + assertTrue(session.createTemporaryTopic().getTopicName().startsWith(PacketImpl.OLD_TEMP_TOPIC_PREFIX.toString())); + + connection.close(); + + // test setting programmatically + connectionFactory = (ConnectionFactory) ctx.lookup("ConnectionFactory2"); + ((ActiveMQConnectionFactory)connectionFactory).setEnable1xPrefixes(true); + connection = connectionFactory.createConnection(); + session = connection.createSession(); + + assertTrue(session.createQueue("testQueue").getQueueName().startsWith(PacketImpl.OLD_QUEUE_PREFIX.toString())); + assertTrue(session.createTemporaryQueue().getQueueName().startsWith(PacketImpl.OLD_TEMP_QUEUE_PREFIX.toString())); + assertTrue(session.createTopic("testTopic").getTopicName().startsWith(PacketImpl.OLD_TOPIC_PREFIX.toString())); + assertTrue(session.createTemporaryTopic().getTopicName().startsWith(PacketImpl.OLD_TEMP_TOPIC_PREFIX.toString())); + + connection.close(); + } + + @Test + public void test1xNamingNegative() throws NamingException, JMSException { + liveService.getSecurityStore().setSecurityEnabled(false); + Hashtable props = new Hashtable<>(); + props.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory"); + props.put("connectionFactory.ConnectionFactory", "vm://0"); + Context ctx = new InitialContext(props); + + ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("ConnectionFactory"); + Connection connection = connectionFactory.createConnection(); + Session session = connection.createSession(); + + assertFalse(session.createQueue("testQueue").getQueueName().startsWith(PacketImpl.OLD_QUEUE_PREFIX.toString())); + assertFalse(session.createTemporaryQueue().getQueueName().startsWith(PacketImpl.OLD_TEMP_QUEUE_PREFIX.toString())); + assertFalse(session.createTopic("testTopic").getTopicName().startsWith(PacketImpl.OLD_TOPIC_PREFIX.toString())); + assertFalse(session.createTemporaryTopic().getTopicName().startsWith(PacketImpl.OLD_TEMP_TOPIC_PREFIX.toString())); + + connection.close(); + } } \ No newline at end of file diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ActiveMQResourceAdapterConfigTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ActiveMQResourceAdapterConfigTest.java index 96992aa2559..0473db406ff 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ActiveMQResourceAdapterConfigTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ActiveMQResourceAdapterConfigTest.java @@ -414,6 +414,12 @@ public class ActiveMQResourceAdapterConfigTest extends ActiveMQTestBase { " IgnoreJTA" + " boolean" + " " + + " " + + " " + + " ***add***" + + " Enable1xPrefixes" + + " boolean" + + " " + " "; private static String rootConfig = "" + config + commentedOutConfigs + ""; From ad6db747010774ec55592b4304ad45c169a7209f Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Fri, 10 Aug 2018 12:25:14 +0100 Subject: [PATCH 147/207] ARTEMIS-2024 Enable SharedClientID on ConnectionFactory --- .../api/core/client/ActiveMQClient.java | 2 ++ .../jms/client/ActiveMQConnection.java | 11 +++++++++- .../jms/client/ActiveMQConnectionFactory.java | 14 +++++++++++- .../ConnectionFactoryConfiguration.java | 4 ++++ .../ConnectionFactoryConfigurationImpl.java | 22 ++++++++++++++++++- .../jms/server/impl/JMSServerManagerImpl.java | 1 + .../artemis/ra/ActiveMQResourceAdapter.java | 2 ++ .../ra/ConnectionFactoryProperties.java | 17 ++++++++++++++ .../artemis/ra/inflow/ActiveMQActivation.java | 2 ++ .../activemq/ActiveMQConnectionFactory.java | 1 + .../jms/ActiveMQConnectionFactoryTest.java | 3 ++- .../jms/client/ConnectionTest.java | 21 ++++++++++++++++++ .../ra/ConnectionFactoryPropertiesTest.java | 1 + 13 files changed, 97 insertions(+), 4 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java index d24efff84f2..ab647a31705 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java @@ -95,6 +95,8 @@ public final class ActiveMQClient { public static final boolean DEFAULT_PRE_ACKNOWLEDGE = false; + public static final boolean DEFAULT_ENABLED_SHARED_CLIENT_ID = false; + public static final long DEFAULT_DISCOVERY_INITIAL_WAIT_TIMEOUT = 10000; public static final long DEFAULT_DISCOVERY_REFRESH_TIMEOUT = 10000; diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java index ec67cc33d9c..0f04889697c 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnection.java @@ -679,11 +679,19 @@ protected final void checkClosed() throws JMSException { } public void authorize() throws JMSException { + authorize(true); + } + + public void authorize(boolean validateClientId) throws JMSException { try { initialSession = sessionFactory.createSession(username, password, false, false, false, false, 0); if (clientID != null) { - validateClientID(initialSession, clientID); + if (validateClientId) { + validateClientID(initialSession, clientID); + } else { + initialSession.addMetaData(ClientSession.JMS_SESSION_CLIENT_ID_PROPERTY, clientID); + } } addSessionMetaData(initialSession); @@ -718,6 +726,7 @@ public String getDeserializationWhiteList() { return this.factoryReference.getDeserializationWhiteList(); } + // Inner classes -------------------------------------------------------------------------------- private static class JMSFailureListener implements SessionFailureListener { diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java index db1fc782690..17ee6fe302a 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java @@ -70,6 +70,8 @@ public class ActiveMQConnectionFactory extends JNDIStorable implements Connectio private String clientID; + private boolean enableSharedClientID = ActiveMQClient.DEFAULT_ENABLED_SHARED_CLIENT_ID; + private int dupsOKBatchSize = ActiveMQClient.DEFAULT_ACK_BATCH_SIZE; private int transactionBatchSize = ActiveMQClient.DEFAULT_ACK_BATCH_SIZE; @@ -452,6 +454,14 @@ public synchronized void setClientID(final String clientID) { this.clientID = clientID; } + public boolean isEnableSharedClientID() { + return enableSharedClientID; + } + + public void setEnableSharedClientID(boolean enableSharedClientID) { + this.enableSharedClientID = enableSharedClientID; + } + public synchronized int getDupsOKBatchSize() { return dupsOKBatchSize; } @@ -857,7 +867,7 @@ protected synchronized ActiveMQConnection createConnectionInternal(final String connection.setReference(this); try { - connection.authorize(); + connection.authorize(!isEnableSharedClientID()); } catch (JMSException e) { try { connection.close(); @@ -882,6 +892,8 @@ public String toString() { transactionBatchSize + ", readOnly=" + readOnly + + "EnableSharedClientID=" + + enableSharedClientID + "]"; } diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/ConnectionFactoryConfiguration.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/ConnectionFactoryConfiguration.java index 43b9904b21a..eee74314317 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/ConnectionFactoryConfiguration.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/ConnectionFactoryConfiguration.java @@ -193,4 +193,8 @@ public interface ConnectionFactoryConfiguration extends EncodingSupport { boolean isEnable1xPrefixes(); ConnectionFactoryConfiguration setEnable1xPrefixes(boolean enable1xPrefixes); + + boolean isEnableSharedClientID(); + + ConnectionFactoryConfiguration setEnableSharedClientID(boolean enabled); } diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java index 2f546ec163f..ae71ecaa8db 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java @@ -126,6 +126,9 @@ public class ConnectionFactoryConfigurationImpl implements ConnectionFactoryConf private boolean enable1xPrefixes = ActiveMQClient.DEFAULT_ENABLE_1X_PREFIXES; + private boolean enableSharedClientID = ActiveMQClient.DEFAULT_ENABLED_SHARED_CLIENT_ID; + + // Static -------------------------------------------------------- // Constructors -------------------------------------------------- @@ -638,6 +641,9 @@ public void decode(final ActiveMQBuffer buffer) { deserializationWhiteList = BufferHelper.readNullableSimpleStringAsString(buffer); enable1xPrefixes = buffer.readableBytes() > 0 ? buffer.readBoolean() : null; + + enableSharedClientID = buffer.readableBytes() > 0 ? buffer.readBoolean() : ActiveMQClient.DEFAULT_ENABLED_SHARED_CLIENT_ID; + } @Override @@ -729,6 +735,8 @@ public void encode(final ActiveMQBuffer buffer) { BufferHelper.writeAsNullableSimpleString(buffer, deserializationWhiteList); buffer.writeBoolean(enable1xPrefixes); + + BufferHelper.writeNullableBoolean(buffer, enableSharedClientID); } @Override @@ -844,9 +852,11 @@ public int getEncodeSize() { BufferHelper.sizeOfNullableSimpleString(deserializationWhiteList) + - DataConstants.SIZE_BOOLEAN; + DataConstants.SIZE_BOOLEAN + // enable1xPrefixes; + BufferHelper.sizeOfNullableBoolean(enableSharedClientID); + return size; } @@ -914,6 +924,16 @@ public ConnectionFactoryConfiguration setInitialMessagePacketSize(int size) { return this; } + @Override + public ConnectionFactoryConfiguration setEnableSharedClientID(boolean enabled) { + this.enableSharedClientID = enabled; + return this; + } + + @Override + public boolean isEnableSharedClientID() { + return enableSharedClientID; + } // Public -------------------------------------------------------- diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/impl/JMSServerManagerImpl.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/impl/JMSServerManagerImpl.java index 58acbbceda3..27abbbe8af4 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/impl/JMSServerManagerImpl.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/impl/JMSServerManagerImpl.java @@ -1221,6 +1221,7 @@ protected ActiveMQConnectionFactory internalCreateCFPOJO(final ConnectionFactory cf.setDeserializationWhiteList(cfConfig.getDeserializationWhiteList()); cf.setInitialMessagePacketSize(cfConfig.getInitialMessagePacketSize()); cf.setEnable1xPrefixes(cfConfig.isEnable1xPrefixes()); + cf.setEnableSharedClientID(cfConfig.isEnableSharedClientID()); return cf; } diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java index 8c31c2abb52..f3e4614683c 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java @@ -1792,6 +1792,7 @@ public ActiveMQConnectionFactory newConnectionFactory(ConnectionFactoryPropertie throw new IllegalArgumentException("must provide either TransportType or DiscoveryGroupAddress and DiscoveryGroupPort for ResourceAdapter Connection Factory"); } + cf.setEnableSharedClientID(true); setParams(cf, overrideProperties); return cf; } @@ -1858,6 +1859,7 @@ public ActiveMQConnectionFactory createRecoveryActiveMQConnectionFactory(final C cf.setReconnectAttempts(0); cf.setInitialConnectAttempts(0); + cf.setEnableSharedClientID(true); return cf; } diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ConnectionFactoryProperties.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ConnectionFactoryProperties.java index cda5afcec9e..c0a7702eda7 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ConnectionFactoryProperties.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ConnectionFactoryProperties.java @@ -130,6 +130,8 @@ public class ConnectionFactoryProperties implements ConnectionFactoryOptions { private String deserializationWhiteList; + private Boolean enableSharedClientID; + /** * @return the transportType */ @@ -755,6 +757,14 @@ public boolean isHasBeenUpdated() { return hasBeenUpdated; } + public void setEnableSharedClientID(boolean enable) { + this.enableSharedClientID = enable; + } + + public boolean isEnableSharedClientID() { + return enableSharedClientID; + } + @Override public boolean equals(Object obj) { if (this == obj) @@ -999,6 +1009,12 @@ else if (!protocolManagerFactoryStr.equals(other.protocolManagerFactoryStr)) } else if (!this.enable1xPrefixes.equals(other.enable1xPrefixes)) return false; + if (enableSharedClientID == null) { + if (other.enableSharedClientID != null) + return false; + } else if (!enableSharedClientID == other.enableSharedClientID) + return false; + return true; } @@ -1052,6 +1068,7 @@ public int hashCode() { result = prime * result + ((deserializationBlackList == null) ? 0 : deserializationBlackList.hashCode()); result = prime * result + ((deserializationWhiteList == null) ? 0 : deserializationWhiteList.hashCode()); result = prime * result + ((enable1xPrefixes == null) ? 0 : enable1xPrefixes.hashCode()); + result = prime * result + ((enableSharedClientID == null) ? 0 : enableSharedClientID.hashCode()); return result; } } diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java index 3a893696bd3..204d5d0aace 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java @@ -454,12 +454,14 @@ protected void setupCF() throws Exception { // This will clone the connection factory // to make sure we won't close anyone's connection factory when we stop the MDB factory = ActiveMQJMSClient.createConnectionFactory(((ActiveMQConnectionFactory) fac).toURI().toString(), "internalConnection"); + factory.setEnableSharedClientID(true); } else { factory = ra.newConnectionFactory(spec); } } else { factory = ra.newConnectionFactory(spec); } + } /** diff --git a/tests/activemq5-unit-tests/src/main/java/org/apache/activemq/ActiveMQConnectionFactory.java b/tests/activemq5-unit-tests/src/main/java/org/apache/activemq/ActiveMQConnectionFactory.java index 0b1453a8fc9..823c76149a0 100644 --- a/tests/activemq5-unit-tests/src/main/java/org/apache/activemq/ActiveMQConnectionFactory.java +++ b/tests/activemq5-unit-tests/src/main/java/org/apache/activemq/ActiveMQConnectionFactory.java @@ -217,6 +217,7 @@ public ActiveMQConnectionFactory(String brokerURL) { } public ActiveMQConnectionFactory(URI brokerURL) { + setBrokerURL(brokerURL.toString()); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ActiveMQConnectionFactoryTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ActiveMQConnectionFactoryTest.java index f44e3170918..c235c5c6c8e 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ActiveMQConnectionFactoryTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ActiveMQConnectionFactoryTest.java @@ -35,6 +35,7 @@ import org.apache.activemq.artemis.api.core.BroadcastGroupConfiguration; import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.api.core.UDPBroadcastEndpointFactory; @@ -45,7 +46,6 @@ import org.apache.activemq.artemis.core.config.ha.SharedStoreMasterPolicyConfiguration; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; -import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.integration.jms.serializables.TestClass1; @@ -444,6 +444,7 @@ private void testSettersThrowException(final ActiveMQConnectionFactory cf) { long retryInterval = RandomUtil.randomPositiveLong(); double retryIntervalMultiplier = RandomUtil.randomDouble(); int reconnectAttempts = RandomUtil.randomPositiveInt(); + boolean enableSharedClientID = true; try { cf.setClientID(clientID); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ConnectionTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ConnectionTest.java index 8ea65dbc7f0..f53de937467 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ConnectionTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ConnectionTest.java @@ -131,6 +131,27 @@ public void testTwoConnectionsSameIDThroughCF() throws Exception { session2.close(); } + @Test + public void testTwoConnectionsSameIDThroughCFWithShareClientIDEnabeld() throws Exception { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616?clientID=myid;enableSharedClientID=true"); + + conn = connectionFactory.createConnection(); + try { + conn2 = connectionFactory.createConnection(); + } catch (InvalidClientIDException expected) { + Assert.fail("Should allow sharing of client IDs among the same CF"); + } + + Session session1 = conn.createSession(); + Session session2 = conn.createSession(); + Session session3 = conn2.createSession(); + Session session4 = conn2.createSession(); + + session1.close(); + session2.close(); + session3.close(); + session4.close(); + } @Test public void testGetSetConnectionFactory() throws Exception { diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ConnectionFactoryPropertiesTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ConnectionFactoryPropertiesTest.java index 3901e1b43f9..b463e303832 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ConnectionFactoryPropertiesTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ConnectionFactoryPropertiesTest.java @@ -46,6 +46,7 @@ public class ConnectionFactoryPropertiesTest extends ActiveMQTestBase { UNSUPPORTED_CF_PROPERTIES.add("user"); UNSUPPORTED_CF_PROPERTIES.add("userName"); UNSUPPORTED_CF_PROPERTIES.add("password"); + UNSUPPORTED_CF_PROPERTIES.add("enableSharedClientID"); UNSUPPORTED_RA_PROPERTIES = new TreeSet<>(); UNSUPPORTED_RA_PROPERTIES.add("HA"); From 4dd116ee04400c2e24f0e5737f4cfa5b97cb05a5 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Mon, 13 Aug 2018 18:10:24 -0400 Subject: [PATCH 148/207] NO-JIRA fixing broken test --- .../integration/jms/jms2client/BodyTest.java | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/jms2client/BodyTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/jms2client/BodyTest.java index 3409ee0544d..826a4c6bde0 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/jms2client/BodyTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/jms2client/BodyTest.java @@ -20,12 +20,12 @@ import javax.jms.Connection; import javax.jms.Message; import javax.jms.MessageConsumer; +import javax.jms.MessageFormatException; import javax.jms.MessageProducer; import javax.jms.Session; import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.tests.util.JMSTestBase; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -48,6 +48,7 @@ public void testBodyConversion() throws Throwable { try ( Connection conn = cf.createConnection(); ) { + Session sess = conn.createSession(); MessageProducer producer = sess.createProducer(queue); @@ -55,27 +56,17 @@ public void testBodyConversion() throws Throwable { conn.start(); BytesMessage bytesMessage = sess.createBytesMessage(); - BytesMessage bytesMessage2 = sess.createBytesMessage(); - bytesMessage2.writeInt(42); - bytesMessage2.reset(); - producer.send(bytesMessage); - Message msg = cons.receiveNoWait(); - - producer.send(bytesMessage2); - Message msg2 = cons.receiveNoWait(); + Message msg = cons.receiveNoWait(); assertNotNull(msg); - assertNotNull(msg2); - - // message body is empty. getBody parameter may be set to any type - Assert.assertNull(msg.getBody(java.lang.Object.class)); - Assert.assertNull(msg.getBody(byte[].class)); - Assert.assertNull(msg.getBody(String.class)); - // message body is not empty. getBody parameter must be set to byte[].class (or java.lang.Object.class) - Assert.assertNotNull(msg2.getBody(byte[].class)); - Assert.assertNotNull(msg2.getBody(java.lang.Object.class)); + try { + msg.getBody(String.class); + fail("Exception expected"); + } catch (MessageFormatException e) { + } } + } } From 87fdff51e1178c86f8c3a3bfe9166728fe41e16f Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Mon, 13 Aug 2018 11:49:19 -0400 Subject: [PATCH 149/207] ARTEMIS-2029 Fixing wire checks after reconnects --- .../client/impl/ClientSessionFactoryImpl.java | 10 ++- .../resources/clients/artemisClient.groovy | 4 +- .../main/resources/clients/artemisFail.groovy | 41 ++++++++++ .../resources/meshTest/sendMessages.groovy | 74 ++++++++++++++++++- .../resources/metrics/queueMetrics.groovy | 4 +- 5 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 tests/compatibility-tests/src/main/resources/clients/artemisFail.groovy diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java index 81b6c44f4e4..4723c88e0e6 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java @@ -237,7 +237,7 @@ public Lock lockFailover() { public void connect(final int initialConnectAttempts, final boolean failoverOnInitialConnection) throws ActiveMQException { // Get the connection - getConnectionWithRetry(initialConnectAttempts); + getConnectionWithRetry(initialConnectAttempts, null); if (connection == null) { StringBuilder msg = new StringBuilder("Unable to connect to server using configuration ").append(currentConnectorConfig); @@ -743,7 +743,7 @@ private void reconnectSessions(final RemotingConnection oldConnection, session.preHandleFailover(connection); } - getConnectionWithRetry(reconnectAttempts); + getConnectionWithRetry(reconnectAttempts, oldConnection); if (connection == null) { if (!clientProtocolManager.isAlive()) @@ -774,7 +774,7 @@ private void reconnectSessions(final RemotingConnection oldConnection, } } - private void getConnectionWithRetry(final int reconnectAttempts) { + private void getConnectionWithRetry(final int reconnectAttempts, RemotingConnection oldConnection) { if (!clientProtocolManager.isAlive()) return; if (logger.isTraceEnabled()) { @@ -795,6 +795,10 @@ private void getConnectionWithRetry(final int reconnectAttempts) { } if (getConnection() != null) { + if (oldConnection != null && oldConnection instanceof CoreRemotingConnection) { + // transferring old connection version into the new connection + ((CoreRemotingConnection)connection).setChannelVersion(((CoreRemotingConnection)oldConnection).getChannelVersion()); + } if (logger.isDebugEnabled()) { logger.debug("Reconnection successful"); } diff --git a/tests/compatibility-tests/src/main/resources/clients/artemisClient.groovy b/tests/compatibility-tests/src/main/resources/clients/artemisClient.groovy index 54cd10a1b51..eb9137f09f5 100644 --- a/tests/compatibility-tests/src/main/resources/clients/artemisClient.groovy +++ b/tests/compatibility-tests/src/main/resources/clients/artemisClient.groovy @@ -22,9 +22,9 @@ import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory import org.apache.activemq.artemis.tests.compatibility.GroovyRun; if (serverArg[0].startsWith("HORNETQ")) { - cf = new ActiveMQConnectionFactory("tcp://localhost:61616?protocolManagerFactoryStr=org.apache.activemq.artemis.core.protocol.hornetq.client.HornetQClientProtocolManagerFactory&confirmationWindowSize=1048576&blockOnDurableSend=false"); + cf = new ActiveMQConnectionFactory("tcp://localhost:61616?protocolManagerFactoryStr=org.apache.activemq.artemis.core.protocol.hornetq.client.HornetQClientProtocolManagerFactory&confirmationWindowSize=1048576&blockOnDurableSend=false&reconnectAttempts=-1&retryInterval=100"); } else { - cf = new ActiveMQConnectionFactory("tcp://localhost:61616?confirmationWindowSize=1048576&blockOnDurableSend=false"); + cf = new ActiveMQConnectionFactory("tcp://localhost:61616?confirmationWindowSize=1048576&blockOnDurableSend=false&ha=true&reconnectAttempts=-1&retryInterval=100"); } diff --git a/tests/compatibility-tests/src/main/resources/clients/artemisFail.groovy b/tests/compatibility-tests/src/main/resources/clients/artemisFail.groovy new file mode 100644 index 00000000000..100a6e9dc67 --- /dev/null +++ b/tests/compatibility-tests/src/main/resources/clients/artemisFail.groovy @@ -0,0 +1,41 @@ +package clients + +import org.apache.activemq.artemis.api.core.ActiveMQException +import org.apache.activemq.artemis.api.core.client.FailoverEventListener +import org.apache.activemq.artemis.api.core.client.FailoverEventType +import org.apache.activemq.artemis.jms.client.ActiveMQConnection + +/* + * 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. + */ + +// Create a client connection factory + +import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory +import org.apache.activemq.artemis.tests.compatibility.GroovyRun + +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit; + +CountDownLatch latch = new CountDownLatch(1); +((ActiveMQConnection)connectionToFail).setFailoverListener(new FailoverEventListener() { + @Override + void failoverEvent(FailoverEventType eventType) { + latch.countDown(); + } +}) +((ActiveMQConnection)connectionToFail).getSessionFactory().getConnection().fail(new ActiveMQException("fail")); +GroovyRun.assertTrue(latch.await(10, TimeUnit.SECONDS)); diff --git a/tests/compatibility-tests/src/main/resources/meshTest/sendMessages.groovy b/tests/compatibility-tests/src/main/resources/meshTest/sendMessages.groovy index 6a5e3709e36..87e8027cc1d 100644 --- a/tests/compatibility-tests/src/main/resources/meshTest/sendMessages.groovy +++ b/tests/compatibility-tests/src/main/resources/meshTest/sendMessages.groovy @@ -26,7 +26,6 @@ String serverType = arg[0]; String clientType = arg[1]; String operation = arg[2]; - try { legacyOption = legacy; } catch (Throwable e) { @@ -127,8 +126,60 @@ if (operation.equals("sendAckMessages") || operation.equals("sendTopic")) { plain.setStringProperty("plain", "doce"); plain.setIntProperty("order", 15) producer.send(plain); - session.commit(); + session.close(); + + Session newSession = connection.createSession(true, Session.SESSION_TRANSACTED); + connectionToFail = connection; + if (clientType.equals("ARTEMIS-SNAPSHOT")) { + // this is validating a bug that could only be fixed in snapshot + GroovyRun.evaluate("clients/artemisFail.groovy", "serverArg", serverType); + } + MessageProducer newProducer = newSession.createProducer(destination); + for (int i = 0 ; i < 10; i++) { + String bodyText = "This is message " + i; + TextMessage textMessage = newSession.createTextMessage(bodyText); + int size = 5 + i % 10; + StringBuffer variableSize = new StringBuffer(); + for (int s = 0; s < size; s++) { + variableSize.append(" " + i); + } + textMessage.setStringProperty("inMessageId", variableSize.toString()); + newProducer.send(textMessage); + newSession.commit(); + + newSession.close(); + newSession = connection.createSession(true, Session.SESSION_TRANSACTED); + newProducer = newSession.createProducer(destination); + if (i % 2 == 0) { + // failing half of the sessions for the snapshots + if (clientType.equals("ARTEMIS-SNAPSHOT")) { + // this is validating a bug that could only be fixed in snapshot + GroovyRun.evaluate("clients/artemisFail.groovy", "serverArg", serverType); + } + } + + } + + // even if topic, will send a few on queue + newProducer = newSession.createProducer(queue); + + for (int i = 0; i < 7; i++) { + String bodyText = "This is message " + i; + TextMessage textMessage = newSession.createTextMessage(bodyText); + int size = 5 + i % 10; + StringBuffer variableSize = new StringBuffer(); + for (int s = 0; s < size; s++) { + variableSize.append(" " + i); + } + textMessage.setStringProperty("inMessageId", variableSize.toString()); + newProducer.send(textMessage); + newSession.commit(); + } + + newSession.commit(); + newSession.close(); + connection.close(); } @@ -194,7 +245,26 @@ if (operation.equals("receiveMessages") || operation.equals("receiveNonDurableSu GroovyRun.assertNotNull(plain); GroovyRun.assertEquals("doce", plain.getStringProperty("plain")); + + for (int i = 0 ; i < 10; i++) { + TextMessage recMessage = consumer.receive(5000); + GroovyRun.assertNotNull(recMessage); + GroovyRun.assertEquals("This is message " + i, recMessage.getText()); + } + session.commit(); + + consumer.close(); + + // force a few on the queue even if the test is for topics + consumer = session.createConsumer(queue); + + for (int i = 0; i < 7; i++) { + TextMessage recMessage = consumer.receive(5000); + GroovyRun.assertNotNull(recMessage); + GroovyRun.assertEquals("This is message " + i, recMessage.getText()); + } + connection.close(); } diff --git a/tests/compatibility-tests/src/main/resources/metrics/queueMetrics.groovy b/tests/compatibility-tests/src/main/resources/metrics/queueMetrics.groovy index 4ef4425a5c9..9ac89856b82 100644 --- a/tests/compatibility-tests/src/main/resources/metrics/queueMetrics.groovy +++ b/tests/compatibility-tests/src/main/resources/metrics/queueMetrics.groovy @@ -32,6 +32,6 @@ for (Object o : queueControls) { QueueControl c = (QueueControl) o; GroovyRun.assertTrue(c.getPersistentSize() > 0); GroovyRun.assertTrue(c.getDurablePersistentSize() > 0); - GroovyRun.assertEquals(16l, c.getMessageCount()); - GroovyRun.assertEquals(16l, c.getDurableMessageCount()); + GroovyRun.assertEquals(33l, c.getMessageCount()); + GroovyRun.assertEquals(33l, c.getDurableMessageCount()); } From 99d091a0eaa07f042ce5d689bff9a3bd5880f1b3 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Mon, 13 Aug 2018 20:30:54 -0400 Subject: [PATCH 150/207] ARTEMIS-2030 only use interrupt during shutdown on RA --- .../core/client/impl/ClientConsumerImpl.java | 9 ++ .../client/impl/ClientConsumerInternal.java | 2 + .../artemis/ra/inflow/ActiveMQActivation.java | 113 ++++++++++++------ .../ra/inflow/ActiveMQMessageHandler.java | 16 ++- .../client/impl/LargeMessageBufferTest.java | 5 + 5 files changed, 105 insertions(+), 40 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerImpl.java index 19987ff6bee..5e3d95e07f9 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerImpl.java @@ -409,6 +409,15 @@ public MessageHandler getMessageHandler() throws ActiveMQException { return handler; } + @Override + public Thread getCurrentThread() { + if (onMessageThread != null) { + return onMessageThread; + } + return receiverThread; + } + + // Must be synchronized since messages may be arriving while handler is being set and might otherwise end // up not queueing enough executors - so messages get stranded @Override diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerInternal.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerInternal.java index 177732e24dd..986f397d25b 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerInternal.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerInternal.java @@ -41,6 +41,8 @@ public interface ClientConsumerInternal extends ClientConsumer { void clear(boolean waitForOnMessage) throws ActiveMQException; + Thread getCurrentThread(); + /** * To be called by things like MDBs during shutdown of the server * diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java index 204d5d0aace..9d4b096577e 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java @@ -26,9 +26,12 @@ import javax.resource.ResourceException; import javax.resource.spi.endpoint.MessageEndpointFactory; import javax.resource.spi.work.Work; +import javax.resource.spi.work.WorkException; import javax.resource.spi.work.WorkManager; import javax.transaction.xa.XAResource; import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -56,6 +59,7 @@ import org.apache.activemq.artemis.ra.ActiveMQRaUtils; import org.apache.activemq.artemis.ra.ActiveMQResourceAdapter; import org.apache.activemq.artemis.service.extensions.xa.recovery.XARecoveryConfig; +import org.apache.activemq.artemis.utils.ActiveMQThreadFactory; import org.apache.activemq.artemis.utils.FutureLatch; import org.apache.activemq.artemis.utils.PasswordMaskingUtil; import org.jboss.logging.Logger; @@ -242,7 +246,7 @@ public void start() throws ResourceException { logger.trace("start()"); } deliveryActive.set(true); - ra.getWorkManager().scheduleWork(new SetupActivation()); + scheduleWork(new SetupActivation()); } /** @@ -282,7 +286,7 @@ public void stop() { } deliveryActive.set(false); - teardown(); + teardown(true); } /** @@ -348,7 +352,7 @@ protected synchronized void setup() throws Exception { /** * Teardown the activation */ - protected synchronized void teardown() { + protected synchronized void teardown(boolean useInterrupt) { logger.debug("Tearing down " + spec); long timeout = factory == null ? ActiveMQClient.DEFAULT_CALL_TIMEOUT : factory.getCallTimeout(); @@ -369,28 +373,27 @@ protected synchronized void teardown() { handlers.clear(); FutureLatch future = new FutureLatch(handlersCopy.length); - List interruptThreads = new ArrayList<>(); for (ActiveMQMessageHandler handler : handlersCopy) { - Thread thread = handler.interruptConsumer(future); - if (thread != null) { - interruptThreads.add(thread); - } + handler.interruptConsumer(future); } //wait for all the consumers to complete any onmessage calls boolean stuckThreads = !future.await(timeout); //if any are stuck then we need to interrupt them - if (stuckThreads) { - for (Thread interruptThread : interruptThreads) { - try { - interruptThread.interrupt(); - } catch (Exception e) { - //ok + if (stuckThreads && useInterrupt) { + for (ActiveMQMessageHandler handler : handlersCopy) { + Thread interruptThread = handler.getCurrentThread(); + if (interruptThread != null) { + try { + interruptThread.interrupt(); + } catch (Throwable e) { + //ok + } } } } - Thread threadTearDown = new Thread("TearDown/ActiveMQActivation") { + Runnable runTearDown = new Runnable() { @Override public void run() { for (ActiveMQMessageHandler handler : handlersCopy) { @@ -399,10 +402,7 @@ public void run() { } }; - // We will first start a new thread that will call tearDown on all the instances, trying to graciously shutdown everything. - // We will then use the call-timeout to determine a timeout. - // if that failed we will then close the connection factory, and interrupt the thread - threadTearDown.start(); + Thread threadTearDown = startThread("TearDown/HornetQActivation", runTearDown); try { threadTearDown.join(timeout); @@ -550,9 +550,7 @@ protected void setupDestination() throws Exception { calculatedDestinationName = spec.getQueuePrefix() + calculatedDestinationName; } - logger.debug("Unable to retrieve " + destinationName + - " from JNDI. Creating a new " + destinationType.getName() + - " named " + calculatedDestinationName + " to be used by the MDB."); + logger.debug("Unable to retrieve " + destinationName + " from JNDI. Creating a new " + destinationType.getName() + " named " + calculatedDestinationName + " to be used by the MDB."); // If there is no binding on naming, we will just create a new instance if (isTopic) { @@ -602,18 +600,41 @@ public String toString() { return buffer.toString(); } - public void startReconnectThread(final String threadName) { + public void startReconnectThread(final String cause) { if (logger.isTraceEnabled()) { - logger.trace("Starting reconnect Thread " + threadName + " on MDB activation " + this); + logger.trace("Starting reconnect Thread " + cause + " on MDB activation " + this); } - Runnable runnable = new Runnable() { - @Override - public void run() { - reconnect(null); - } - }; - Thread t = new Thread(runnable, threadName); + try { + // We have to use the worker otherwise we may get the wrong classLoader + scheduleWork(new ReconnectWork(cause)); + } catch (Exception e) { + logger.warn("Could not reconnect because worker is down", e); + } + } + + private static Thread startThread(String name, Runnable run) { + ClassLoader tccl; + + try { + tccl = AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override + public ClassLoader run() { + return ActiveMQActivation.class.getClassLoader(); + } + }); + } catch (Throwable e) { + logger.warn(e.getMessage(), e); + tccl = null; + } + + ActiveMQThreadFactory factory = new ActiveMQThreadFactory(name, true, tccl); + Thread t = factory.newThread(run); t.start(); + return t; + } + + private void scheduleWork(Work run) throws WorkException { + ra.getWorkManager().scheduleWork(run); } /** @@ -621,7 +642,7 @@ public void run() { * * @param failure if reconnecting in the event of a failure */ - public void reconnect(Throwable failure) { + public void reconnect(Throwable failure, boolean useInterrupt) { if (logger.isTraceEnabled()) { logger.trace("reconnecting activation " + this); } @@ -644,7 +665,7 @@ public void reconnect(Throwable failure) { try { Throwable lastException = failure; while (deliveryActive.get() && (setupAttempts == -1 || reconnectCount < setupAttempts)) { - teardown(); + teardown(useInterrupt); try { Thread.sleep(setupInterval); @@ -697,7 +718,7 @@ public void run() { try { setup(); } catch (Throwable t) { - reconnect(t); + reconnect(t, false); } } @@ -706,6 +727,30 @@ public void release() { } } + /** + * Handles reconnecting + */ + private class ReconnectWork implements Work { + + final String cause; + + ReconnectWork(String cause) { + this.cause = cause; + } + + @Override + public void release() { + + } + + @Override + public void run() { + logger.tracef("Starting reconnect for %s", cause); + reconnect(null, false); + } + + } + private class RebalancingListener implements ClusterTopologyListener { @Override diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java index f343ec9601d..018389610e7 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java @@ -124,17 +124,13 @@ public void setup() throws Exception { if (!spec.isShareSubscriptions()) { throw ActiveMQRALogger.LOGGER.canNotCreatedNonSharedSubscriber(); } else if (ActiveMQRALogger.LOGGER.isDebugEnabled()) { - logger.debug("the mdb on destination " + queueName + " already had " + - subResponse.getConsumerCount() + - " consumers but the MDB is configured to share subscriptions, so no exceptions are thrown"); + logger.debug("the mdb on destination " + queueName + " already had " + subResponse.getConsumerCount() + " consumers but the MDB is configured to share subscriptions, so no exceptions are thrown"); } } SimpleString oldFilterString = subResponse.getFilterString(); - boolean selectorChanged = selector == null && oldFilterString != null || - oldFilterString == null && selector != null || - (oldFilterString != null && selector != null && !oldFilterString.toString().equals(selector)); + boolean selectorChanged = selector == null && oldFilterString != null || oldFilterString == null && selector != null || (oldFilterString != null && selector != null && !oldFilterString.toString().equals(selector)); SimpleString oldTopicName = subResponse.getAddress(); @@ -198,6 +194,14 @@ XAResource getXAResource() { return useXA ? session : null; } + public Thread getCurrentThread() { + if (consumer == null) { + return null; + } + + return consumer.getCurrentThread(); + } + public Thread interruptConsumer(FutureLatch future) { try { if (consumer != null) { diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/client/impl/LargeMessageBufferTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/client/impl/LargeMessageBufferTest.java index d78f070174a..c1790d0d609 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/client/impl/LargeMessageBufferTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/client/impl/LargeMessageBufferTest.java @@ -675,6 +675,11 @@ public ClientMessage receive() throws ActiveMQException { return null; } + @Override + public Thread getCurrentThread() { + return null; + } + @Override public ClientMessage receive(final long timeout) throws ActiveMQException { return null; From 71e664a705e8a54adb530e3c769ce936fb851fb7 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 14 Aug 2018 12:39:53 -0400 Subject: [PATCH 151/207] ARTEMIS-2023 test showing NPE --- .../src/main/resources/serial/cfserial.groovy | 72 +++++++++++++ ...FactoryConfigurationSerializationTest.java | 100 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 tests/compatibility-tests/src/main/resources/serial/cfserial.groovy create mode 100644 tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java diff --git a/tests/compatibility-tests/src/main/resources/serial/cfserial.groovy b/tests/compatibility-tests/src/main/resources/serial/cfserial.groovy new file mode 100644 index 00000000000..26085cd8d5a --- /dev/null +++ b/tests/compatibility-tests/src/main/resources/serial/cfserial.groovy @@ -0,0 +1,72 @@ +package clients + +import io.netty.buffer.Unpooled +import org.apache.activemq.artemis.api.core.ActiveMQBuffer +import org.apache.activemq.artemis.api.core.ActiveMQBuffers +import org.apache.activemq.artemis.core.buffers.impl.ChannelBufferWrapper +import org.apache.activemq.artemis.jms.server.config.impl.ConnectionFactoryConfigurationImpl + +/* + * 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. + */ + +// Create a client connection factory + +import org.apache.activemq.artemis.tests.compatibility.GroovyRun; +import javax.jms.*; +import org.apache.activemq.artemis.jms.client.* + +import java.nio.ByteBuffer + +file = arg[0] +method = arg[1] +version = arg[2] + +if (method.equals("write")) { + List transportConfigurations = new ArrayList<>(); + transportConfigurations.add("tst"); cfConfiguration = new ConnectionFactoryConfigurationImpl(); + cfConfiguration.setName("np").setConnectorNames(transportConfigurations); + ActiveMQBuffer buffer = ActiveMQBuffers.dynamicBuffer(1024); + cfConfiguration.encode(buffer); + byte[] bytes = new byte[buffer.readableBytes()]; + buffer.readBytes(bytes); + + + + ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file)); + objectOutputStream.write(bytes); + objectOutputStream.close(); +} else { + ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file)) + ActiveMQBuffer buffer = ActiveMQBuffers.dynamicBuffer(1024); + while (true) { + int byteRead = inputStream.read() + if (byteRead < 0) { + break; + } + + buffer.writeByte((byte)byteRead); + } + cfConfiguration = new ConnectionFactoryConfigurationImpl(); + cfConfiguration.decode(buffer); + + inputStream.close(); +} + +GroovyRun.assertEquals("np", cfConfiguration.getName()) + + + diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java new file mode 100644 index 00000000000..d40a8da87c3 --- /dev/null +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java @@ -0,0 +1,100 @@ +/* + * 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.artemis.tests.compatibility; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.activemq.artemis.utils.FileUtil; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.ONE_FIVE; +import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.SNAPSHOT; +import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.TWO_FOUR; + +/** + * To run this test on the IDE and debug it, run the compatibility-tests through a command line once: + * + * cd /compatibility-tests + * mvn install -Ptests | tee output.log + * + * on the output.log you will see the output generated by {@link #getClasspathProperty(String)} + * + * On your IDE, edit the Run Configuration to your test and add those -D as parameters to your test. + * On Idea you would do the following: + * + * Run->Edit Configuration->Add ArtemisMeshTest and add your properties. + */ +@RunWith(Parameterized.class) +public class ConnectionFactoryConfigurationSerializationTest extends VersionedBaseTest { + + // this will ensure that all tests in this class are run twice, + // once with "true" passed to the class' constructor and once with "false" + @Parameterized.Parameters(name = "server={0}, producer={1}, consumer={2}") + public static Collection getParameters() { + // we don't need every single version ever released.. + // if we keep testing current one against 2.4 and 1.4.. we are sure the wire and API won't change over time + List combinations = new ArrayList<>(); + + /* + // during development sometimes is useful to comment out the combinations + // and add the ones you are interested.. example: + */ + // combinations.add(new Object[]{SNAPSHOT, ONE_FIVE, ONE_FIVE}); + // combinations.add(new Object[]{ONE_FIVE, ONE_FIVE, ONE_FIVE}); + + combinations.addAll(combinatory(new Object[]{null}, new Object[]{ONE_FIVE, SNAPSHOT, TWO_FOUR}, new Object[]{ONE_FIVE, SNAPSHOT, TWO_FOUR})); + return combinations; + } + + public ConnectionFactoryConfigurationSerializationTest(String server, String sender, String receiver) throws Exception { + super(server, sender, receiver); + } + + @Before + public void beforeTest() throws Throwable { + FileUtil.deleteDirectory(serverFolder.getRoot()); + serverFolder.getRoot().mkdirs(); + setVariable(senderClassloader, "persistent", false); + startServer(serverFolder.getRoot(), senderClassloader, "1"); + } + + @After + public void afterTest() { + try { + stopServer(senderClassloader); + } catch (Throwable ignored) { + ignored.printStackTrace(); + } + } + + @Test + public void testSerializeFactory() throws Throwable { + File file = serverFolder.newFile("objects.ser"); + evaluate(senderClassloader, "serial/cfserial.groovy", file.getAbsolutePath(), "write", sender); + evaluate(receiverClassloader, "serial/cfserial.groovy", file.getAbsolutePath(), "read", receiver); + } + +} + From 4c51848b58fc14bd63ba0a764c31d3ca1dbffc5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Tue, 14 Aug 2018 07:04:06 +0100 Subject: [PATCH 152/207] ARTEMIS-2023 Fix NPE --- .../server/config/impl/ConnectionFactoryConfigurationImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java index ae71ecaa8db..c2ac0b6ad0e 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java @@ -640,7 +640,7 @@ public void decode(final ActiveMQBuffer buffer) { deserializationWhiteList = BufferHelper.readNullableSimpleStringAsString(buffer); - enable1xPrefixes = buffer.readableBytes() > 0 ? buffer.readBoolean() : null; + enable1xPrefixes = buffer.readableBytes() > 0 ? buffer.readBoolean() : ActiveMQClient.DEFAULT_ENABLE_1X_PREFIXES; enableSharedClientID = buffer.readableBytes() > 0 ? buffer.readBoolean() : ActiveMQClient.DEFAULT_ENABLED_SHARED_CLIENT_ID; From 94e34febf6a96d3266c99403917be2b5f1298577 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 14 Aug 2018 12:45:00 -0400 Subject: [PATCH 153/207] ARTEMIS-2023 small tweak on test. Server not needed --- .../ConnectionFactoryConfigurationSerializationTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java index d40a8da87c3..9b7dab8483f 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java @@ -77,16 +77,10 @@ public void beforeTest() throws Throwable { FileUtil.deleteDirectory(serverFolder.getRoot()); serverFolder.getRoot().mkdirs(); setVariable(senderClassloader, "persistent", false); - startServer(serverFolder.getRoot(), senderClassloader, "1"); } @After public void afterTest() { - try { - stopServer(senderClassloader); - } catch (Throwable ignored) { - ignored.printStackTrace(); - } } @Test From 550c79b1341462f0cb4721018451abeee414d9dc Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Sun, 12 Aug 2018 18:16:10 -0300 Subject: [PATCH 154/207] ARTEMIS-2033 add spring-boot-integration example --- examples/features/standard/pom.xml | 1 + .../spring-boot-integration/README.md | 7 ++ .../standard/spring-boot-integration/pom.xml | 102 ++++++++++++++++++ .../jms/example/springboot/Application.java | 70 ++++++++++++ .../example/springboot/ExampleListener.java | 29 +++++ .../jms/example/springboot/MessageSender.java | 41 +++++++ ...itional-spring-configuration-metadata.json | 44 ++++++++ .../src/main/resources/application.properties | 23 ++++ .../src/main/resources/broker.xml | 42 ++++++++ .../src/main/resources/logback.xml | 38 +++++++ 10 files changed, 397 insertions(+) create mode 100644 examples/features/standard/spring-boot-integration/README.md create mode 100644 examples/features/standard/spring-boot-integration/pom.xml create mode 100644 examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/Application.java create mode 100644 examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/ExampleListener.java create mode 100644 examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/MessageSender.java create mode 100644 examples/features/standard/spring-boot-integration/src/main/resources/META-INF/additional-spring-configuration-metadata.json create mode 100644 examples/features/standard/spring-boot-integration/src/main/resources/application.properties create mode 100644 examples/features/standard/spring-boot-integration/src/main/resources/broker.xml create mode 100644 examples/features/standard/spring-boot-integration/src/main/resources/logback.xml diff --git a/examples/features/standard/pom.xml b/examples/features/standard/pom.xml index 0493457d8bf..6ede89768d6 100644 --- a/examples/features/standard/pom.xml +++ b/examples/features/standard/pom.xml @@ -94,6 +94,7 @@ under the License. shared-consumer slow-consumer spring-integration + spring-boot-integration ssl-enabled ssl-enabled-crl-mqtt ssl-enabled-dual-authentication diff --git a/examples/features/standard/spring-boot-integration/README.md b/examples/features/standard/spring-boot-integration/README.md new file mode 100644 index 00000000000..075bbd0d796 --- /dev/null +++ b/examples/features/standard/spring-boot-integration/README.md @@ -0,0 +1,7 @@ +# ActiveMQ Artemis Spring Boot Example + +To run the example, simply type **mvn verify -Pexample** from this directory. + +This example shows how to setup and run an embedded broker within a Spring Boot Application relying on [AMQP 1.0 JMS Spring Boot project](https://github.com/amqphub/amqp-10-jms-spring-boot) for configuration. + + diff --git a/examples/features/standard/spring-boot-integration/pom.xml b/examples/features/standard/spring-boot-integration/pom.xml new file mode 100644 index 00000000000..10d4d76e41f --- /dev/null +++ b/examples/features/standard/spring-boot-integration/pom.xml @@ -0,0 +1,102 @@ + + + + 4.0.0 + + org.apache.activemq.examples.broker + jms-examples + 2.7.0-SNAPSHOT + + spring-boot-integration + ActiveMQ Artemis JMS Spring Boot Integration Example + + + ${project.basedir}/../../../.. + + + 2.0.1 + + + + + org.apache.activemq + artemis-amqp-protocol + ${project.version} + + + org.amqphub.spring + amqp-10-jms-spring-boot-starter + ${amqp-10-jms-spring.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.0.4.RELEASE + + + + + + example + + + + org.apache.activemq + artemis-maven-plugin + + + runClient + + runClient + + + org.apache.activemq.artemis.jms.example.springboot.Application + + + + + + org.apache.activemq.examples.broker + spring-boot-integration + ${project.version} + + + + + org.apache.maven.plugins + maven-clean-plugin + + + + + + release + + + + com.vladsch.flexmark + markdown-page-generator-plugin + + + + + + \ No newline at end of file diff --git a/examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/Application.java b/examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/Application.java new file mode 100644 index 00000000000..a42c6ceed50 --- /dev/null +++ b/examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/Application.java @@ -0,0 +1,70 @@ +/* + * 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.artemis.jms.example.springboot; + +import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration; +import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ; +import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; +import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.jms.annotation.EnableJms; + +/** + * @see Spring JMS Messaging Guide + */ +@SpringBootApplication +@EnableJms +public class Application { + public Application() { + + } + + public static void main(String[] args) throws InterruptedException { + final ConfigurableApplicationContext context = SpringApplication.run(Application.class); + System.out.println("********************* Sending message..."); + MessageSender sender = context.getBean(MessageSender.class); + sender.send("Hello Artemis!"); + Thread.sleep(1000); + context.close(); + } + + @Bean + public ActiveMQJAASSecurityManager securityManager(@Value("${amq.broker.user}") String user, + @Value("${amq.broker.password}") String password, + @Value("${amq.broker.role}") String role) { + final SecurityConfiguration configuration = new SecurityConfiguration(); + final ActiveMQJAASSecurityManager securityManager = + new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), configuration); + configuration.addUser(user, password); + configuration.addRole(user, role); + configuration.setDefaultUser(user); + + return securityManager; + } + + @Bean(initMethod = "start", destroyMethod = "stop") + public EmbeddedActiveMQ embeddedActiveMQ(ActiveMQJAASSecurityManager securityManager) { + final EmbeddedActiveMQ embeddedActiveMQ = new EmbeddedActiveMQ(); + embeddedActiveMQ.setSecurityManager(securityManager); + return embeddedActiveMQ; + } + +} diff --git a/examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/ExampleListener.java b/examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/ExampleListener.java new file mode 100644 index 00000000000..82bdc3e95a2 --- /dev/null +++ b/examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/ExampleListener.java @@ -0,0 +1,29 @@ +/* + * 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.artemis.jms.example.springboot; + +import org.springframework.jms.annotation.JmsListener; +import org.springframework.stereotype.Component; + +@Component +public class ExampleListener { + + @JmsListener(destination = "${amq.queue}") + public void receiveMessage(String message) { + System.out.println("*********************** MESSAGE RECEIVED: " + message); + } +} diff --git a/examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/MessageSender.java b/examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/MessageSender.java new file mode 100644 index 00000000000..70f11245621 --- /dev/null +++ b/examples/features/standard/spring-boot-integration/src/main/java/org/apache/activemq/artemis/jms/example/springboot/MessageSender.java @@ -0,0 +1,41 @@ +/* + * 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.artemis.jms.example.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.jms.core.JmsTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MessageSender { + + @Autowired + private JmsTemplate jmsTemplate; + + @Value("${amq.queue}") + private String queue; + + public MessageSender() { + + } + + public void send(String message) { + jmsTemplate.convertAndSend(queue, message); + } + +} diff --git a/examples/features/standard/spring-boot-integration/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/examples/features/standard/spring-boot-integration/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 00000000000..fbe55634908 --- /dev/null +++ b/examples/features/standard/spring-boot-integration/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,44 @@ +/** + * 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. +*/ + +{"properties": [ + { + "name": "amq.queue", + "type": "java.lang.String", + "description": "A description for 'amq.queue'" + }, + { + "name": "amq.broker.password", + "type": "java.lang.String", + "description": "A description for 'amq.broker.password'" + }, + { + "name": "amq.broker.user", + "type": "java.lang.String", + "description": "A description for 'amq.broker.user'" + }, + { + "name": "amq.broker.user", + "type": "java.lang.String", + "description": "A description for 'amq.broker.user'" + }, + { + "name": "amq.broker.role", + "type": "java.lang.String", + "description": "A description for 'amq.broker.role'" + } +]} \ No newline at end of file diff --git a/examples/features/standard/spring-boot-integration/src/main/resources/application.properties b/examples/features/standard/spring-boot-integration/src/main/resources/application.properties new file mode 100644 index 00000000000..14f0c2ef8d4 --- /dev/null +++ b/examples/features/standard/spring-boot-integration/src/main/resources/application.properties @@ -0,0 +1,23 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- +amqphub.amqp10jms.remote-url=amqp://localhost:61616 +amqphub.amqp10jms.username=guest +amqphub.amqp10jms.password=guest +amq.broker.password=guest +amq.broker.user=guest +amq.broker.role=guest +amq.queue=exampleQueue \ No newline at end of file diff --git a/examples/features/standard/spring-boot-integration/src/main/resources/broker.xml b/examples/features/standard/spring-boot-integration/src/main/resources/broker.xml new file mode 100644 index 00000000000..099363e9292 --- /dev/null +++ b/examples/features/standard/spring-boot-integration/src/main/resources/broker.xml @@ -0,0 +1,42 @@ + + + + + + false + + + tcp://0.0.0.0:61616?protocols=AMQP,CORE + + + + + + + + + + + + + + + + diff --git a/examples/features/standard/spring-boot-integration/src/main/resources/logback.xml b/examples/features/standard/spring-boot-integration/src/main/resources/logback.xml new file mode 100644 index 00000000000..2f867cdab83 --- /dev/null +++ b/examples/features/standard/spring-boot-integration/src/main/resources/logback.xml @@ -0,0 +1,38 @@ + + + + + + + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + From a0b4c4dd1923497a03255d4a27d0ed9b7948931e Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Mon, 13 Aug 2018 21:40:52 -0500 Subject: [PATCH 155/207] ARTEMIS-2023 extend 1x naming to other ops --- .../jms/client/ActiveMQDestination.java | 61 ++++++++++++++----- .../artemis/jms/client/ActiveMQMessage.java | 54 ++++++++++++++-- .../jms/client/ActiveMQMessageConsumer.java | 4 ++ .../artemis/jms/client/ActiveMQQueue.java | 2 +- .../jms/client/ActiveMQQueueBrowser.java | 11 +++- .../artemis/jms/client/ActiveMQSession.java | 6 +- .../jms/client/JMSMessageListenerWrapper.java | 4 ++ .../artemis/ra/ActiveMQResourceAdapter.java | 2 + .../artemis/ra/inflow/ActiveMQActivation.java | 1 + .../integration/jms/SimpleJNDIClientTest.java | 1 - 10 files changed, 121 insertions(+), 25 deletions(-) diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java index 81aada16ffe..6d1b4096f99 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java @@ -53,6 +53,10 @@ private static String escape(final String input) { return input.replace("\\", "\\\\").replace(".", "\\."); } + protected void setName(String name) { + this.name = name; + } + /** * Static helper method for working with destinations. */ @@ -84,21 +88,43 @@ public static ActiveMQDestination createDestination(String name, TYPE defaultTyp } public static Destination fromPrefixedName(final String name) { + return fromPrefixedName(name, name); + } + + public static Destination fromPrefixedName(final String addr, final String name) { + + ActiveMQDestination destination; + if (addr.startsWith(ActiveMQDestination.QUEUE_QUALIFIED_PREFIX)) { + String address = addr.substring(ActiveMQDestination.QUEUE_QUALIFIED_PREFIX.length()); + destination = createQueue(address); + } else if (addr.startsWith(ActiveMQDestination.TOPIC_QUALIFIED_PREFIX)) { + String address = addr.substring(ActiveMQDestination.TOPIC_QUALIFIED_PREFIX.length()); + destination = createTopic(address); + } else if (addr.startsWith(ActiveMQDestination.TEMP_QUEUE_QUALIFED_PREFIX)) { + String address = addr.substring(ActiveMQDestination.TEMP_QUEUE_QUALIFED_PREFIX.length()); + destination = new ActiveMQTemporaryQueue(address, null); + } else if (addr.startsWith(ActiveMQDestination.TEMP_TOPIC_QUALIFED_PREFIX)) { + String address = addr.substring(ActiveMQDestination.TEMP_TOPIC_QUALIFED_PREFIX.length()); + destination = new ActiveMQTemporaryTopic(address, null); + } else { + destination = new ActiveMQDestination(addr, TYPE.DESTINATION, null); + } + + String unprefixedName = name; + if (name.startsWith(ActiveMQDestination.QUEUE_QUALIFIED_PREFIX)) { - String address = name.substring(ActiveMQDestination.QUEUE_QUALIFIED_PREFIX.length()); - return createQueue(address); + unprefixedName = name.substring(ActiveMQDestination.QUEUE_QUALIFIED_PREFIX.length()); } else if (name.startsWith(ActiveMQDestination.TOPIC_QUALIFIED_PREFIX)) { - String address = name.substring(ActiveMQDestination.TOPIC_QUALIFIED_PREFIX.length()); - return createTopic(address); + unprefixedName = name.substring(ActiveMQDestination.TOPIC_QUALIFIED_PREFIX.length()); } else if (name.startsWith(ActiveMQDestination.TEMP_QUEUE_QUALIFED_PREFIX)) { - String address = name.substring(ActiveMQDestination.TEMP_QUEUE_QUALIFED_PREFIX.length()); - return new ActiveMQTemporaryQueue(address, null); + unprefixedName = name.substring(ActiveMQDestination.TEMP_QUEUE_QUALIFED_PREFIX.length()); } else if (name.startsWith(ActiveMQDestination.TEMP_TOPIC_QUALIFED_PREFIX)) { - String address = name.substring(ActiveMQDestination.TEMP_TOPIC_QUALIFED_PREFIX.length()); - return new ActiveMQTemporaryTopic(address, null); - } else { - return new ActiveMQDestination(name, TYPE.DESTINATION, null); + unprefixedName = name.substring(ActiveMQDestination.TEMP_TOPIC_QUALIFED_PREFIX.length()); } + + destination.setName(unprefixedName); + + return destination; } public static SimpleString createQueueNameForSubscription(final boolean isDurable, @@ -274,10 +300,6 @@ public static ActiveMQTemporaryTopic createTemporaryTopic(String address) { @Deprecated private String address; - /** - * The "JMS" name of the destination. Needed for serialization backwards compatibility. - */ - @Deprecated private String name; private final boolean temporary; @@ -313,7 +335,6 @@ protected ActiveMQDestination(final SimpleString address, this.queue = TYPE.isQueue(type); } - @Deprecated protected ActiveMQDestination(final String address, final String name, final TYPE type, @@ -337,6 +358,16 @@ public void setAddress(String address) { setSimpleAddress(SimpleString.toSimpleString(address)); } + @Override + public String toString() { + return "ActiveMQDestination [address=" + simpleAddress.toString() + + ", name=" + + name + + ", type =" + + thetype + + "]"; + } + public void setSimpleAddress(SimpleString address) { if (address == null) { throw new IllegalArgumentException("address cannot be null"); diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessage.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessage.java index 18f8000582c..ff7da00a316 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessage.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessage.java @@ -46,6 +46,7 @@ import org.apache.activemq.artemis.core.client.ActiveMQClientMessageBundle; import org.apache.activemq.artemis.core.client.impl.ClientMessageInternal; import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl; import org.apache.activemq.artemis.reader.MessageUtil; import org.apache.activemq.artemis.utils.UUID; @@ -65,6 +66,11 @@ public class ActiveMQMessage implements javax.jms.Message { // Constants ----------------------------------------------------- public static final byte TYPE = org.apache.activemq.artemis.api.core.Message.DEFAULT_TYPE; + public static final SimpleString OLD_QUEUE_QUALIFIED_PREFIX = SimpleString.toSimpleString(ActiveMQDestination.QUEUE_QUALIFIED_PREFIX + PacketImpl.OLD_QUEUE_PREFIX); + public static final SimpleString OLD_TEMP_QUEUE_QUALIFED_PREFIX = SimpleString.toSimpleString(ActiveMQDestination.TEMP_QUEUE_QUALIFED_PREFIX + PacketImpl.OLD_TEMP_QUEUE_PREFIX); + public static final SimpleString OLD_TOPIC_QUALIFIED_PREFIX = SimpleString.toSimpleString(ActiveMQDestination.TOPIC_QUALIFIED_PREFIX + PacketImpl.OLD_TOPIC_PREFIX); + public static final SimpleString OLD_TEMP_TOPIC_QUALIFED_PREFIX = SimpleString.toSimpleString(ActiveMQDestination.TEMP_TOPIC_QUALIFED_PREFIX + PacketImpl.OLD_TEMP_TOPIC_PREFIX); + public static Map coreMaptoJMSMap(final Map coreMessage) { Map jmsMessage = new HashMap<>(); @@ -203,6 +209,8 @@ public static ActiveMQMessage createMessage(final ClientMessage message, private boolean clientAck; + private boolean enable1xPrefixes; + private long jmsDeliveryTime; // Constructors -------------------------------------------------- @@ -358,11 +366,23 @@ public String getJMSCorrelationID() throws JMSException { @Override public Destination getJMSReplyTo() throws JMSException { if (replyTo == null) { - - SimpleString repl = MessageUtil.getJMSReplyTo(message); - - if (repl != null) { - replyTo = ActiveMQDestination.fromPrefixedName(repl.toString()); + SimpleString address = MessageUtil.getJMSReplyTo(message); + if (address != null) { + String name = address.toString(); + + // swap the old prefixes for the new ones so the proper destination type gets created + if (enable1xPrefixes) { + if (address.startsWith(OLD_QUEUE_QUALIFIED_PREFIX)) { + name = address.subSeq(OLD_QUEUE_QUALIFIED_PREFIX.length(), address.length()).toString(); + } else if (address.startsWith(OLD_TEMP_QUEUE_QUALIFED_PREFIX)) { + name = address.subSeq(OLD_TEMP_QUEUE_QUALIFED_PREFIX.length(), address.length()).toString(); + } else if (address.startsWith(OLD_TOPIC_QUALIFIED_PREFIX)) { + name = address.subSeq(OLD_TOPIC_QUALIFIED_PREFIX.length(), address.length()).toString(); + } else if (address.startsWith(OLD_TEMP_TOPIC_QUALIFED_PREFIX)) { + name = address.subSeq(OLD_TEMP_TOPIC_QUALIFED_PREFIX.length(), address.length()).toString(); + } + } + replyTo = ActiveMQDestination.fromPrefixedName(address.toString(), name); } } return replyTo; @@ -401,6 +421,20 @@ public void setJMSReplyTo(final Destination dest) throws JMSException { public Destination getJMSDestination() throws JMSException { if (dest == null) { SimpleString address = message.getAddressSimpleString(); + SimpleString name = address; + + if (address != null & enable1xPrefixes) { + if (address.startsWith(PacketImpl.OLD_QUEUE_PREFIX)) { + name = address.subSeq(PacketImpl.OLD_QUEUE_PREFIX.length(), address.length()); + } else if (address.startsWith(PacketImpl.OLD_TEMP_QUEUE_PREFIX)) { + name = address.subSeq(PacketImpl.OLD_TEMP_QUEUE_PREFIX.length(), address.length()); + } else if (address.startsWith(PacketImpl.OLD_TOPIC_PREFIX)) { + name = address.subSeq(PacketImpl.OLD_TOPIC_PREFIX.length(), address.length()); + } else if (address.startsWith(PacketImpl.OLD_TEMP_TOPIC_PREFIX)) { + name = address.subSeq(PacketImpl.OLD_TEMP_TOPIC_PREFIX.length(), address.length()); + } + } + if (address == null) { dest = null; } else if (RoutingType.ANYCAST.equals(message.getRoutingType())) { @@ -408,7 +442,11 @@ public Destination getJMSDestination() throws JMSException { } else if (RoutingType.MULTICAST.equals(message.getRoutingType())) { dest = ActiveMQDestination.createTopic(address); } else { - dest = ActiveMQDestination.fromPrefixedName(address.toString()); + dest = (ActiveMQDestination) ActiveMQDestination.fromPrefixedName(address.toString()); + } + + if (name != null) { + ((ActiveMQDestination) dest).setName(name.toString()); } } @@ -865,6 +903,10 @@ public boolean waitCompletionOnStream(final long timeWait) throws JMSException { } } + public void setEnable1xPrefixes(boolean enable1xPrefixes) { + this.enable1xPrefixes = enable1xPrefixes; + } + @Override public String toString() { StringBuffer sb = new StringBuffer("ActiveMQMessage["); diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageConsumer.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageConsumer.java index 4664bb9f0cf..8fabe8b1fd3 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageConsumer.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageConsumer.java @@ -220,6 +220,10 @@ private ActiveMQMessage getMessage(final long timeout, final boolean noWait) thr coreMessage.getType() == ActiveMQObjectMessage.TYPE; jmsMsg = ActiveMQMessage.createMessage(coreMessage, needSession ? coreSession : null, options); + if (session.isEnable1xPrefixes()) { + jmsMsg.setEnable1xPrefixes(true); + } + try { jmsMsg.doBeforeReceive(); } catch (IndexOutOfBoundsException ioob) { diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueue.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueue.java index f2680f80070..f1b69d76695 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueue.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueue.java @@ -72,7 +72,7 @@ public ActiveMQQueue(String address, boolean temporary, ActiveMQSession session) @Override public String getQueueName() { - return getAddress(); + return getName(); } @Override diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueueBrowser.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueueBrowser.java index bb58f3dde62..716d044d45a 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueueBrowser.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueueBrowser.java @@ -49,18 +49,22 @@ public final class ActiveMQQueueBrowser implements QueueBrowser { private SimpleString filterString; + private final boolean enable1xPrefixes; + // Constructors --------------------------------------------------------------------------------- protected ActiveMQQueueBrowser(final ConnectionFactoryOptions options, final ActiveMQQueue queue, final String messageSelector, - final ClientSession session) throws JMSException { + final ClientSession session, + final boolean enable1xPrefixes) throws JMSException { this.options = options; this.session = session; this.queue = queue; if (messageSelector != null) { filterString = new SimpleString(SelectorTranslator.convertToActiveMQFilterString(messageSelector)); } + this.enable1xPrefixes = enable1xPrefixes; } // QueueBrowser implementation ------------------------------------------------------------------- @@ -138,6 +142,11 @@ public ActiveMQMessage nextElement() { ClientMessage next = current; current = null; msg = ActiveMQMessage.createMessage(next, session, options); + + if (enable1xPrefixes) { + msg.setEnable1xPrefixes(true); + } + try { msg.doBeforeReceive(); } catch (Exception e) { diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java index 8d34ca1adb0..278b4299e69 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java @@ -879,7 +879,7 @@ public QueueBrowser createBrowser(final Queue queue, String filterString) throws throw JMSExceptionHelper.convertFromActiveMQException(e); } - return new ActiveMQQueueBrowser(options, (ActiveMQQueue) activeMQDestination, filterString, session); + return new ActiveMQQueueBrowser(options, (ActiveMQQueue) activeMQDestination, filterString, session, enable1xPrefixes); } @@ -1126,6 +1126,10 @@ public void removeConsumer(final ActiveMQMessageConsumer consumer) { consumers.remove(consumer); } + public boolean isEnable1xPrefixes() { + return enable1xPrefixes; + } + // Package protected --------------------------------------------- void deleteQueue(final SimpleString queueName) throws JMSException { diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/JMSMessageListenerWrapper.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/JMSMessageListenerWrapper.java index 5d9f6ed259e..0d2420b68d7 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/JMSMessageListenerWrapper.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/JMSMessageListenerWrapper.java @@ -82,6 +82,10 @@ public void onMessage(final ClientMessage message) { msg.setClientAcknowledge(); } + if (session.isEnable1xPrefixes()) { + msg.setEnable1xPrefixes(true); + } + try { msg.doBeforeReceive(); } catch (Exception e) { diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java index f3e4614683c..0b4cbb4a9ad 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java @@ -1793,6 +1793,7 @@ public ActiveMQConnectionFactory newConnectionFactory(ConnectionFactoryPropertie } cf.setEnableSharedClientID(true); + cf.setEnable1xPrefixes(raProperties.isEnable1xPrefixes() == null ? false : raProperties.isEnable1xPrefixes()); setParams(cf, overrideProperties); return cf; } @@ -1859,6 +1860,7 @@ public ActiveMQConnectionFactory createRecoveryActiveMQConnectionFactory(final C cf.setReconnectAttempts(0); cf.setInitialConnectAttempts(0); + cf.setEnable1xPrefixes(raProperties.isEnable1xPrefixes() == null ? false : raProperties.isEnable1xPrefixes()); cf.setEnableSharedClientID(true); return cf; } diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java index 9d4b096577e..863a252f018 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java @@ -455,6 +455,7 @@ protected void setupCF() throws Exception { // to make sure we won't close anyone's connection factory when we stop the MDB factory = ActiveMQJMSClient.createConnectionFactory(((ActiveMQConnectionFactory) fac).toURI().toString(), "internalConnection"); factory.setEnableSharedClientID(true); + factory.setEnable1xPrefixes(((ActiveMQResourceAdapter) fac).isEnable1xPrefixes()); } else { factory = ra.newConnectionFactory(spec); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java index dc48d9a13d3..2acdb0bff73 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java @@ -676,7 +676,6 @@ public void test1xNaming() throws NamingException, JMSException { Context ctx = new InitialContext(props); ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("ConnectionFactory"); - ((ActiveMQConnectionFactory)connectionFactory).setEnable1xPrefixes(true); Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(); From 0dff36e00b53b0984644c089a6ae174c224c53c0 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Thu, 16 Aug 2018 17:41:52 -0400 Subject: [PATCH 156/207] NO-JIRA: Renaming trace logging --- .../activemq/artemis/ra/inflow/ActiveMQMessageHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java index 018389610e7..2de6988cbee 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java @@ -290,7 +290,7 @@ public void onMessage(final ClientMessage message) { } if (logger.isTraceEnabled()) { - logger.trace("HornetQMessageHandler::calling beforeDelivery on message " + message); + logger.trace("ActiveMQMessageHandler::calling beforeDelivery on message " + message); } endpoint.beforeDelivery(ActiveMQActivation.ONMESSAGE); @@ -310,7 +310,7 @@ public void onMessage(final ClientMessage message) { } if (logger.isTraceEnabled()) { - logger.trace("HornetQMessageHandler::calling afterDelivery on message " + message); + logger.trace("ActiveMQMessageHandler::calling afterDelivery on message " + message); } try { From 3952bcdbe8b383901fc500a146673ae5facbeee3 Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Fri, 17 Aug 2018 15:55:39 +0100 Subject: [PATCH 157/207] ARTEMIS-2023 Fix missing setEnable1x methods --- .../artemis/jms/client/ActiveMQSession.java | 26 +++++++++++++------ .../ra/inflow/ActiveMQMessageHandler.java | 2 ++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java index 278b4299e69..528310fa62e 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java @@ -144,28 +144,36 @@ protected ActiveMQSession(final ConnectionFactoryOptions options, public BytesMessage createBytesMessage() throws JMSException { checkClosed(); - return new ActiveMQBytesMessage(session); + ActiveMQBytesMessage message = new ActiveMQBytesMessage(session); + message.setEnable1xPrefixes(enable1xPrefixes); + return message; } @Override public MapMessage createMapMessage() throws JMSException { checkClosed(); - return new ActiveMQMapMessage(session); + ActiveMQMapMessage message = new ActiveMQMapMessage(session); + message.setEnable1xPrefixes(enable1xPrefixes); + return message; } @Override public Message createMessage() throws JMSException { checkClosed(); - return new ActiveMQMessage(session); + ActiveMQMessage message = new ActiveMQMessage(session); + message.setEnable1xPrefixes(enable1xPrefixes); + return message; } @Override public ObjectMessage createObjectMessage() throws JMSException { checkClosed(); - return new ActiveMQObjectMessage(session, options); + ActiveMQObjectMessage message = new ActiveMQObjectMessage(session, options); + message.setEnable1xPrefixes(enable1xPrefixes); + return message; } @Override @@ -173,8 +181,8 @@ public ObjectMessage createObjectMessage(final Serializable object) throws JMSEx checkClosed(); ActiveMQObjectMessage msg = new ActiveMQObjectMessage(session, options); - msg.setObject(object); + msg.setEnable1xPrefixes(enable1xPrefixes); return msg; } @@ -183,7 +191,9 @@ public ObjectMessage createObjectMessage(final Serializable object) throws JMSEx public StreamMessage createStreamMessage() throws JMSException { checkClosed(); - return new ActiveMQStreamMessage(session); + ActiveMQStreamMessage message = new ActiveMQStreamMessage(session); + message.setEnable1xPrefixes(enable1xPrefixes); + return message; } @Override @@ -191,8 +201,8 @@ public TextMessage createTextMessage() throws JMSException { checkClosed(); ActiveMQTextMessage msg = new ActiveMQTextMessage(session); - msg.setText(null); + msg.setEnable1xPrefixes(enable1xPrefixes); return msg; } @@ -202,8 +212,8 @@ public TextMessage createTextMessage(final String text) throws JMSException { checkClosed(); ActiveMQTextMessage msg = new ActiveMQTextMessage(session); - msg.setText(text); + msg.setEnable1xPrefixes(enable1xPrefixes); return msg; } diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java index 2de6988cbee..33c64454de4 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java @@ -282,6 +282,8 @@ public void onMessage(final ClientMessage message) { } ActiveMQMessage msg = ActiveMQMessage.createMessage(message, session, options); + msg.setEnable1xPrefixes(activation.getConnectionFactory().isEnable1xPrefixes()); + boolean beforeDelivery = false; try { From 7119f10d62a02233414051706cfd9b846abf4ba9 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Fri, 17 Aug 2018 11:33:07 -0400 Subject: [PATCH 158/207] ARTEMIS-2036 Re-compiling libaio on older kernel version Used RHEL6 --- artemis-native/bin/libartemis-native-64.so | Bin 28724 -> 25151 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/artemis-native/bin/libartemis-native-64.so b/artemis-native/bin/libartemis-native-64.so index 1360295bf2069e42b7d7b14a3f22b9d4c10662f9..e981ef318ea19cdbab31135ff9484ab8d3d5e98c 100755 GIT binary patch literal 25151 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 Ih$M~w2?R*ONdN!< literal 28724 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%0AYl?u@+wR6Ly8V}s9)xYcgx7($ZNKdV zWO@qimf50Z9VgZfIb!d$w{@qLO+J0kb$YOwZKFFApt%k-ID1%q7*_IMw2r~8Wsw#3 zPLs&Bph5qgMp)l|>PCHgc&sCBxBXtiGRK(Rf#Jl^QT-;^g+Jiz_(nQc=z`A7>5K?E zxd2sq?afhrl1oA6-N`lCe3>4+TZo3aa~ixNqhSLO?G!Y}zcj;~fBJlMJT&d6Ky90f zo{J-eU{-hvm_Q9{P##{3rXpX`o-?+8!0XQe<9BgyZSv$LFoK3ouYS{ZVhO%K>5kls zrg8pBQ|S;|e;iHWn~L!H;d z8EAgoA|nx->)l+sM%>Y*>%h3p#R*h3Q-4VmF*=M@$10i#c*ixL7ysj)WtDrZHrg#K zaC*{HHQGTF{(!Gh2vRF<`f8*7^d&8B(vA~1X~(IXv}&No$(yv}=JPwj4sVU`%;@88 z-b#7$o!_q|G6_nvF$x~9uX#nS=EsF!59~n`H_e1ONcM39Tn)w2P?~SIilN4D=lF>6 z(a^jekK3;WeA;pk2A1E9q+BCi8Fy<{O)Huka4}Z2&^vIkQ47?1G@C~Et~JTE#@Ezq zugaLXnp2FfwyG{ri($ddb*%{(oUwcHu7oIV#jzQZj8z?O^4!^62Qp1ula!6td2yAy z%B}foG`cXUOu?n^VwgPSh+yuA{VHt5=(wtW~rqxz1)&tYSHYLTo6pRWRbkT z#b+_m>WR=sUjS=`bLBAO`V8b(8V6iTi?YV*92R#36j^dO4Yr#gR+M@pbpxcUN z$6TNI(r-_CfTzn64EBU#g%2J|6|gES=lRqFX>ak&IP9Sr@}6BVdMO_JiPs~ zSo>wM_U*F$j3Mbs+bPdZe==!-o_$ABd z)E`hj3FS{vIphnGe(ooGSU12yb~FQaR5Cc;kV>Hqxw+jpb= zu>%-;8@`F(no1aN-ha%?bpz#iH)ae^KHg>7H(Msn>SI>mPl?)j813}n(2xI?Dxn>2 zSG=8=o@Y`&fl4s!8QJrbnJ;fe_?gpaCzrkuAY3{5iy6XS6s{d?$%R3|VU%ZA;<-f5 zcri~WkS|8P2~^Kvu~x#~`1}WcBk*vwv6P1=H}IXn&mt_h)47&SX^X}D7t@LRrTam* z6?A$W-A!?H)aHMJt_O5@9pH9-e>&XYx(qm{wgEFOThi_x%pD_If@5^>>B@g!|0(L_ z zd|992nJXHi{UWAtDeCu$I5Oagep^XN<>|MRlyKQdBzYh+L}pbSDclv| zNyUv(zg1GdpS)0(tMeRwW&xb~9VNZDQ&Qz$eHX=PDbB8>Ab3ULNgj(Z^)AJKnS&Db z+qX)|K)$XpenS!EctsYeQsWRu06cZc0(wuQB)f~qAR=|j!z+bX@!^!5JyJg0UEoQz zhc6wt{Kb+nzQN#m)h=FRd4A(?Q4dFQJRg%&^85b{sJMBF{aPmt;t>g-l<-*z_egj^ z!jB|8A>lx2V520wQo@N6mP@!u!Ws!zO1MtKMRbTl(1$(5UqIQW!)mMJ07&zjs>MMV$pz@y};Yo_4vOHEI`7%{s`7Z@GOG+ib zqEm1o%E>pXyz-9m@+$${cu-}8Q~p>%<&RbSiT(E`)K~spLA4%GeOglz>$d<$ zI#KJ7TK^Q3?g$hjk*wzAN5)*gQC3ipf2U%qV3p4b-jh(De=$Wg^aT~tUbXOwtO|D} zb2wAOlevAhUMr~9F;yW^{vXNu<0Zk{6U9ReD#gokr9Wjy56b##{1fAxektoIkl6mF zg!-GO2ucNCPy-_wsP<)+{R|JZmr-J>{fUZynn<6Re-?FfO!d|NrL_V`q(u6}{0`8N zE>!#LD>UTrebO1$?*^^8zS_Tao?-nrP@hz!+E?qbif`K^2TJK($*=71j|uI!%+Y{B zT-`Ywu=N7O{#ErAJd6^v{7ml0Sf!}4ShlYisCo(>K>x*%_6r|Zd_@d zxf&nUZ{@#fekN;wl1J1(tv13U+oyerP6{$Wt=2I!0Y!LG>r*^FL;Nz{jKp&WJhWO* z%taY2lc{wg9zT#N{~wPZ6!Y)#_$;RUbv!;voIDU@z>_Qg8cz>pOTQV9?;HP;$}`wd zruIUb+GlwXR+TVwI3>L7 zo+iWNB}dIe6Qc~g1*!RELPHPA8hf#1*8|uW(O!&rqW8o!L^e#6pO&d~+Q}s7zd+&( z6YzG4Pkev9iSzH3Q_Z&;j_<3_kmRG@hmqy!QT&^R)l#ho63w>aAz8mt_4iZGCxl^hL!eG*-_C;1cOj4qM*#Q2&&Nx_Hi+gOw6 z?N^ddVqDVGz$c44I~YzmS=?MJ$M+GR<#Bx9xVc5Z6aU2cvc)O*RHwkVrofAM*x4*G zzUdpXT{ce>!pe2@k16IH@Uo{;asi^ilm2_=ic(~Gvf=o$@WaF}0(jSxlRvve;2U5`g zA_ac8frW_B(8%O%RBMtNZmCv(Rl)1`gmr`VF-dpwT_WNAo1~zk*-aOl0 z;V1zG0$nTW8O}M=?M~O6^78p*3tS8A)176m{>JAtM=<5Fq>GDHBu$E$*(a`bV$9xn z@>uZNc(e%Q9FO};alCO*i@4YE^mK8#A3>c(q~U&6HpOD*K~Uo*l5tD>$vZwSGfbLp ziHrN%GSWG;fRNzvZSqjy=ATNKf^NqLa+blcQyf23j_TRPDO`449hi5^27H*0MZ`sxITfcvF0R^l*dJuf%*Se z+7+7SvC9#Q-Pa^T`Nt_;M*2_u(*@>u&He0kTyW@q@-PJzPyV+WcpU6_Wx*MUj+;Dz zE$;6c^j8=Pm`(mEzP)6BZX)K2=?lX+IryIsh-V=GCPO^3pECA|@h$>}SP=MlLcWZvy;$5IIL From 581ee5bc46ec630265fa35a05262127f0d56a74d Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Fri, 17 Aug 2018 12:39:30 -0400 Subject: [PATCH 159/207] ARTEMIS-2037 Adding 32 bits back on libaio --- artemis-native/bin/libartemis-native-32.so | Bin 0 -> 20114 bytes artemis-native/pom.xml | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100755 artemis-native/bin/libartemis-native-32.so diff --git a/artemis-native/bin/libartemis-native-32.so b/artemis-native/bin/libartemis-native-32.so new file mode 100755 index 0000000000000000000000000000000000000000..2917fc35fb1e697a8c145e3c6011fdf096c2b496 GIT binary patch literal 20114 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 literal 0 HcmV?d00001 diff --git a/artemis-native/pom.xml b/artemis-native/pom.xml index 793f4708cb2..6fa4d7d929e 100644 --- a/artemis-native/pom.xml +++ b/artemis-native/pom.xml @@ -81,6 +81,24 @@ maven-resources-plugin + + copy-resources-32 + validate + + copy-resources + + + ${basedir}/target/output/lib/linux-i686/ + + + bin/ + + libartemis-native-32.so + + + + + copy-resources-64 validate From e6dddf823119a8d9317f3043232846c59a1fe104 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 17 Aug 2018 14:06:47 -0500 Subject: [PATCH 160/207] NO-JIRA Tweak RedeployTest 1) Remove use of deprecated EmbeddedJMS. 2) Change test config to use static clustering as discovery may not work in some CI environments. --- .../tests/integration/jms/RedeployTest.java | 105 +++++++++--------- .../test/resources/reload-backup-changed.xml | 24 +--- .../test/resources/reload-backup-original.xml | 24 +--- .../test/resources/reload-live-changed.xml | 24 +--- .../test/resources/reload-live-original.xml | 24 +--- 5 files changed, 72 insertions(+), 129 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java index 2df59cee997..bb3060665a4 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java @@ -39,7 +39,6 @@ import org.apache.activemq.artemis.core.server.impl.AddressInfo; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; -import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS; import org.apache.activemq.artemis.junit.Wait; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.utils.ReusableLatch; @@ -104,8 +103,8 @@ public void run() { @Test public void testRedeployWithFailover() throws Exception { - EmbeddedJMS live = new EmbeddedJMS(); - EmbeddedJMS backup = new EmbeddedJMS(); + EmbeddedActiveMQ live = new EmbeddedActiveMQ(); + EmbeddedActiveMQ backup = new EmbeddedActiveMQ(); try { // set these system properties to use in the relevant broker.xml files @@ -129,7 +128,7 @@ public void testRedeployWithFailover() throws Exception { backup.setConfigResourcePath(backupBrokerXML.toUri().toString()); backup.start(); - Wait.waitFor(() -> backup.getActiveMQServer().isReplicaSync(), 10000, 200); + assertTrue(Wait.waitFor(() -> backup.getActiveMQServer().isReplicaSync(), 15000, 200)); final ReusableLatch liveReloadLatch = new ReusableLatch(1); Runnable liveTick = () -> liveReloadLatch.countDown(); @@ -163,7 +162,7 @@ public void testRedeployWithFailover() throws Exception { live.stop(); - Wait.waitFor(() -> (backup.getActiveMQServer().isActive()), 5000, 100); + assertTrue(Wait.waitFor(() -> (backup.getActiveMQServer().isActive()), 5000, 100)); factory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61617"); try (Connection connection = factory.createConnection()) { @@ -281,91 +280,91 @@ public void testRedeployStopAndRestart() throws Exception { URL url2 = RedeployTest.class.getClassLoader().getResource("reload-changed.xml"); Files.copy(url1.openStream(), brokerXML); - EmbeddedJMS embeddedJMS = new EmbeddedJMS(); - embeddedJMS.setConfigResourcePath(brokerXML.toUri().toString()); - embeddedJMS.start(); + EmbeddedActiveMQ embeddedActiveMQ = new EmbeddedActiveMQ(); + embeddedActiveMQ.setConfigResourcePath(brokerXML.toUri().toString()); + embeddedActiveMQ.start(); final ReusableLatch latch = new ReusableLatch(1); Runnable tick = latch::countDown; - embeddedJMS.getActiveMQServer().getReloadManager().setTick(tick); + embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); try { latch.await(10, TimeUnit.SECONDS); - Assert.assertEquals(getSecurityRoles(embeddedJMS, "security_address").size(), 1); - Assert.assertEquals(getSecurityRoles(embeddedJMS, "security_address").iterator().next().getName(), "b"); + Assert.assertEquals(getSecurityRoles(embeddedActiveMQ, "security_address").size(), 1); + Assert.assertEquals(getSecurityRoles(embeddedActiveMQ, "security_address").iterator().next().getName(), "b"); - Assert.assertEquals(getAddressSettings(embeddedJMS, "address_settings_address").getDeadLetterAddress(), SimpleString.toSimpleString("OriginalDLQ")); - Assert.assertEquals(getAddressSettings(embeddedJMS, "address_settings_address").getExpiryAddress(), SimpleString.toSimpleString("OriginalExpiryQueue")); + Assert.assertEquals(getAddressSettings(embeddedActiveMQ, "address_settings_address").getDeadLetterAddress(), SimpleString.toSimpleString("OriginalDLQ")); + Assert.assertEquals(getAddressSettings(embeddedActiveMQ, "address_settings_address").getExpiryAddress(), SimpleString.toSimpleString("OriginalExpiryQueue")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_address_removal_no_queue")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_address_removal")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_queue_removal")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_removal").contains("config_test_queue_removal_queue_1")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_removal").contains("config_test_queue_removal_queue_2")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal_no_queue")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_removal")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_removal").contains("config_test_queue_removal_queue_1")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_removal").contains("config_test_queue_removal_queue_2")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_queue_change")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_change").contains("config_test_queue_change_queue")); - Assert.assertEquals(10, getQueue(embeddedJMS, "config_test_queue_change_queue").getMaxConsumers()); - Assert.assertEquals(false, getQueue(embeddedJMS, "config_test_queue_change_queue").isPurgeOnNoConsumers()); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_change")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_change").contains("config_test_queue_change_queue")); + Assert.assertEquals(10, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").getMaxConsumers()); + Assert.assertEquals(false, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").isPurgeOnNoConsumers()); Files.copy(url2.openStream(), brokerXML, StandardCopyOption.REPLACE_EXISTING); brokerXML.toFile().setLastModified(System.currentTimeMillis() + 1000); latch.setCount(1); - embeddedJMS.getActiveMQServer().getReloadManager().setTick(tick); + embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); latch.await(10, TimeUnit.SECONDS); //Assert that the security settings change applied - Assert.assertEquals(getSecurityRoles(embeddedJMS, "security_address").size(), 1); - Assert.assertEquals(getSecurityRoles(embeddedJMS, "security_address").iterator().next().getName(), "c"); + Assert.assertEquals(getSecurityRoles(embeddedActiveMQ, "security_address").size(), 1); + Assert.assertEquals(getSecurityRoles(embeddedActiveMQ, "security_address").iterator().next().getName(), "c"); //Assert that the address settings change applied - Assert.assertEquals(getAddressSettings(embeddedJMS, "address_settings_address").getDeadLetterAddress(), SimpleString.toSimpleString("NewDLQ")); - Assert.assertEquals(getAddressSettings(embeddedJMS, "address_settings_address").getExpiryAddress(), SimpleString.toSimpleString("NewExpiryQueue")); + Assert.assertEquals(getAddressSettings(embeddedActiveMQ, "address_settings_address").getDeadLetterAddress(), SimpleString.toSimpleString("NewDLQ")); + Assert.assertEquals(getAddressSettings(embeddedActiveMQ, "address_settings_address").getExpiryAddress(), SimpleString.toSimpleString("NewExpiryQueue")); //Assert the address and queue changes applied - Assert.assertNull(getAddressInfo(embeddedJMS, "config_test_address_removal_no_queue")); - Assert.assertNull(getAddressInfo(embeddedJMS, "config_test_address_removal")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_queue_removal")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_removal").contains("config_test_queue_removal_queue_1")); - Assert.assertFalse(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_removal").contains("config_test_queue_removal_queue_2")); - - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_queue_change")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_change").contains("config_test_queue_change_queue")); - Assert.assertEquals(1, getQueue(embeddedJMS, "config_test_queue_change_queue").getMaxConsumers()); - Assert.assertEquals(true, getQueue(embeddedJMS, "config_test_queue_change_queue").isPurgeOnNoConsumers()); + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal_no_queue")); + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_removal")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_removal").contains("config_test_queue_removal_queue_1")); + Assert.assertFalse(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_removal").contains("config_test_queue_removal_queue_2")); + + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_change")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_change").contains("config_test_queue_change_queue")); + Assert.assertEquals(1, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").getMaxConsumers()); + Assert.assertEquals(true, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").isPurgeOnNoConsumers()); } finally { - embeddedJMS.stop(); + embeddedActiveMQ.stop(); } try { - embeddedJMS.start(); + embeddedActiveMQ.start(); //Assert that the security settings changes persist a stop and start server (e.g. like what occurs if network health check stops the node), but JVM remains up. - Assert.assertEquals(getSecurityRoles(embeddedJMS, "security_address").size(), 1); - Assert.assertEquals(getSecurityRoles(embeddedJMS, "security_address").iterator().next().getName(), "c"); + Assert.assertEquals(getSecurityRoles(embeddedActiveMQ, "security_address").size(), 1); + Assert.assertEquals(getSecurityRoles(embeddedActiveMQ, "security_address").iterator().next().getName(), "c"); //Assert that the address settings changes persist a stop and start server (e.g. like what occurs if network health check stops the node), but JVM remains up. - Assert.assertEquals(getAddressSettings(embeddedJMS, "address_settings_address").getDeadLetterAddress(), SimpleString.toSimpleString("NewDLQ")); - Assert.assertEquals(getAddressSettings(embeddedJMS, "address_settings_address").getExpiryAddress(), SimpleString.toSimpleString("NewExpiryQueue")); + Assert.assertEquals(getAddressSettings(embeddedActiveMQ, "address_settings_address").getDeadLetterAddress(), SimpleString.toSimpleString("NewDLQ")); + Assert.assertEquals(getAddressSettings(embeddedActiveMQ, "address_settings_address").getExpiryAddress(), SimpleString.toSimpleString("NewExpiryQueue")); //Assert that the address and queue changes persist a stop and start server (e.g. like what occurs if network health check stops the node), but JVM remains up. - Assert.assertNull(getAddressInfo(embeddedJMS, "config_test_address_removal_no_queue")); - Assert.assertNull(getAddressInfo(embeddedJMS, "config_test_address_removal")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_queue_removal")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_removal").contains("config_test_queue_removal_queue_1")); - Assert.assertFalse(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_removal").contains("config_test_queue_removal_queue_2")); + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal_no_queue")); + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_removal")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_removal").contains("config_test_queue_removal_queue_1")); + Assert.assertFalse(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_removal").contains("config_test_queue_removal_queue_2")); - Assert.assertNotNull(getAddressInfo(embeddedJMS, "config_test_queue_change")); - Assert.assertTrue(listQueuesNamesForAddress(embeddedJMS, "config_test_queue_change").contains("config_test_queue_change_queue")); - Assert.assertEquals(1, getQueue(embeddedJMS, "config_test_queue_change_queue").getMaxConsumers()); - Assert.assertEquals(true, getQueue(embeddedJMS, "config_test_queue_change_queue").isPurgeOnNoConsumers()); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_change")); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_change").contains("config_test_queue_change_queue")); + Assert.assertEquals(1, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").getMaxConsumers()); + Assert.assertEquals(true, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").isPurgeOnNoConsumers()); } finally { - embeddedJMS.stop(); + embeddedActiveMQ.stop(); } } diff --git a/tests/integration-tests/src/test/resources/reload-backup-changed.xml b/tests/integration-tests/src/test/resources/reload-backup-changed.xml index 101871b1705..8ea924120dd 100644 --- a/tests/integration-tests/src/test/resources/reload-backup-changed.xml +++ b/tests/integration-tests/src/test/resources/reload-backup-changed.xml @@ -39,11 +39,12 @@ under the License. ${backup-data-dir}/large-messages - tcp://0.0.0.0:61617 + tcp://127.0.0.1:61617 tcp://127.0.0.1:61617 + tcp://127.0.0.1:61616 @@ -54,29 +55,14 @@ under the License. - - - 231.7.7.7 - 9876 - 5000 - artemis - - - - - - 231.7.7.7 - 9876 - 10000 - - - artemis STRICT 1 - + + other + diff --git a/tests/integration-tests/src/test/resources/reload-backup-original.xml b/tests/integration-tests/src/test/resources/reload-backup-original.xml index 0061be0469b..54a01a9293c 100644 --- a/tests/integration-tests/src/test/resources/reload-backup-original.xml +++ b/tests/integration-tests/src/test/resources/reload-backup-original.xml @@ -39,11 +39,12 @@ under the License. ${backup-data-dir}/large-messages - tcp://0.0.0.0:61617 + tcp://127.0.0.1:61617 tcp://127.0.0.1:61617 + tcp://127.0.0.1:61616 @@ -54,29 +55,14 @@ under the License. - - - 231.7.7.7 - 9876 - 5000 - artemis - - - - - - 231.7.7.7 - 9876 - 10000 - - - artemis STRICT 1 - + + other + diff --git a/tests/integration-tests/src/test/resources/reload-live-changed.xml b/tests/integration-tests/src/test/resources/reload-live-changed.xml index 74a2ef215cb..17120ce9ea0 100644 --- a/tests/integration-tests/src/test/resources/reload-live-changed.xml +++ b/tests/integration-tests/src/test/resources/reload-live-changed.xml @@ -39,11 +39,12 @@ under the License. ${live-data-dir}/large-messages - tcp://0.0.0.0:61616 + tcp://127.0.0.1:61616 tcp://127.0.0.1:61616 + tcp://127.0.0.1:61617 @@ -54,29 +55,14 @@ under the License. - - - 231.7.7.7 - 9876 - 5000 - artemis - - - - - - 231.7.7.7 - 9876 - 10000 - - - artemis STRICT 1 - + + other + diff --git a/tests/integration-tests/src/test/resources/reload-live-original.xml b/tests/integration-tests/src/test/resources/reload-live-original.xml index 1cbfe7509f1..9051d5803b5 100644 --- a/tests/integration-tests/src/test/resources/reload-live-original.xml +++ b/tests/integration-tests/src/test/resources/reload-live-original.xml @@ -39,11 +39,12 @@ under the License. ${live-data-dir}/large-messages - tcp://0.0.0.0:61616 + tcp://127.0.0.1:61616 tcp://127.0.0.1:61616 + tcp://127.0.0.1:61617 @@ -54,29 +55,14 @@ under the License. - - - 231.7.7.7 - 9876 - 5000 - artemis - - - - - - 231.7.7.7 - 9876 - 10000 - - - artemis STRICT 1 - + + other + From 63e6cd98f856ba8900782b7488c3ce4cf9e48257 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Fri, 17 Aug 2018 16:45:07 -0400 Subject: [PATCH 161/207] ARTEMIS-2046 Fixing issues with JournalStorageManager.stop in replication, JDBC and shared storage --- .../api/core/ActiveMQExceptionType.java | 6 + .../api/core/ActiveMQShutdownException.java | 31 ++++ .../core/client/impl/ClientSessionImpl.java | 4 +- .../jdbc/store/journal/JDBCJournalImpl.java | 12 +- .../core/journal/impl/JournalImpl.java | 41 +++-- .../AbstractJournalStorageManager.java | 16 +- .../impl/journal/JournalStorageManager.java | 63 +++++--- .../core/replication/ReplicationEndpoint.java | 14 ++ .../core/replication/ReplicationManager.java | 9 +- .../failover/NettyReplicationStopTest.java | 150 ++++++++++++++++++ .../tests/integration/xa/BasicXaTest.java | 100 ++++++++++++ 11 files changed, 407 insertions(+), 39 deletions(-) create mode 100644 artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ActiveMQShutdownException.java create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NettyReplicationStopTest.java diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ActiveMQExceptionType.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ActiveMQExceptionType.java index 9120d796186..7cec2e46f4b 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ActiveMQExceptionType.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ActiveMQExceptionType.java @@ -249,6 +249,12 @@ public ActiveMQException createException(String msg) { public ActiveMQException createException(String msg) { return new ActiveMQNullRefException(msg); } + }, + SHUTDOWN_ERROR(219) { + @Override + public ActiveMQException createException(String msg) { + return new ActiveMQShutdownException(msg); + } }; private static final Map TYPE_MAP; diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ActiveMQShutdownException.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ActiveMQShutdownException.java new file mode 100644 index 00000000000..03797a82730 --- /dev/null +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ActiveMQShutdownException.java @@ -0,0 +1,31 @@ +/* + * 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.artemis.api.core; + +/** + * An operation failed because an address exists on the server. + */ +public final class ActiveMQShutdownException extends ActiveMQException { + + public ActiveMQShutdownException() { + super(ActiveMQExceptionType.SHUTDOWN_ERROR); + } + + public ActiveMQShutdownException(String msg) { + super(ActiveMQExceptionType.SHUTDOWN_ERROR, msg); + } +} diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java index ab9888e452d..711d7cec4f2 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java @@ -1500,9 +1500,11 @@ public void commit(final Xid xid, final boolean onePhase) throws XAException { XAException xaException = null; if (onePhase) { + logger.debug("Throwing oneFase RMFAIL on xid=" + xid, t); //we must return XA_RMFAIL xaException = new XAException(XAException.XAER_RMFAIL); } else { + logger.debug("Throwing twoFase Retry on xid=" + xid, t); // Any error on commit -> RETRY // We can't rollback a Prepared TX for definition xaException = new XAException(XAException.XA_RETRY); @@ -1753,7 +1755,7 @@ public void rollback(final Xid xid) throws XAException { } catch (XAException xae) { throw xae; } catch (ActiveMQException e) { - if (e.getType() == ActiveMQExceptionType.UNBLOCKED || e.getType() == ActiveMQExceptionType.CONNECTION_TIMEDOUT) { + if (e.getType() == ActiveMQExceptionType.UNBLOCKED || e.getType() == ActiveMQExceptionType.CONNECTION_TIMEDOUT || e.getType() == ActiveMQExceptionType.SHUTDOWN_ERROR) { // Unblocked on failover throw new XAException(XAException.XA_RETRY); } diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/journal/JDBCJournalImpl.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/journal/JDBCJournalImpl.java index f997b3cf3d1..334bc46f908 100644 --- a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/journal/JDBCJournalImpl.java +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/journal/JDBCJournalImpl.java @@ -31,7 +31,9 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; +import org.apache.activemq.artemis.api.core.ActiveMQShutdownException; import org.apache.activemq.artemis.core.io.IOCriticalErrorListener; import org.apache.activemq.artemis.core.io.SequentialFileFactory; import org.apache.activemq.artemis.core.journal.EncoderPersister; @@ -334,19 +336,19 @@ public void run() { } - private void checkStatus() { + private void checkStatus() throws Exception { checkStatus(null); } - private void checkStatus(IOCompletion callback) { + private void checkStatus(IOCompletion callback) throws Exception { if (!started) { if (callback != null) callback.onError(-1, "JDBC Journal is not loaded"); - throw new IllegalStateException("JDBCJournal is not loaded"); + throw new ActiveMQShutdownException("JDBCJournal is not loaded"); } if (failed.get()) { if (callback != null) callback.onError(-1, "JDBC Journal failed"); - throw new IllegalStateException("JDBCJournal Failed"); + throw new ActiveMQException("JDBCJournal Failed"); } } @@ -388,7 +390,7 @@ private void appendRecord(JDBCJournalRecord record) throws Exception { if (callback != null) callback.waitCompletion(); } - private synchronized void addTxRecord(JDBCJournalRecord record) { + private synchronized void addTxRecord(JDBCJournalRecord record) throws Exception { if (logger.isTraceEnabled()) { logger.trace("addTxRecord " + record + ", started=" + started + ", failed=" + failed); diff --git a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java index 55b92c5470d..30ed6e33e2b 100644 --- a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java +++ b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java @@ -48,6 +48,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; +import org.apache.activemq.artemis.api.core.ActiveMQShutdownException; import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.core.io.IOCallback; import org.apache.activemq.artemis.core.io.IOCriticalErrorListener; @@ -823,6 +824,9 @@ public void run() { usedFile); } result.set(true); + } catch (ActiveMQShutdownException e) { + result.fail(e); + logger.error("appendPrepareRecord:" + e, e); } catch (Throwable e) { result.fail(e); setErrorCondition(callback, null, e); @@ -882,7 +886,10 @@ public void run() { } result.set(true); - } catch (Exception e) { + } catch (ActiveMQShutdownException e) { + result.fail(e); + logger.error("appendUpdateRecord:" + e, e); + } catch (Throwable e) { result.fail(e); setErrorCondition(callback, null, e); logger.error("appendUpdateRecord:" + e, e); @@ -933,7 +940,10 @@ record = records.remove(id); record.delete(usedFile); } result.set(true); - } catch (Exception e) { + } catch (ActiveMQShutdownException e) { + result.fail(e); + logger.error("appendDeleteRecord:" + e, e); + } catch (Throwable e) { result.fail(e); logger.error("appendDeleteRecord:" + e, e); } finally { @@ -993,7 +1003,7 @@ public void run() { } tx.addPositive(usedFile, id, addRecord.getEncodeSize()); - } catch (Exception e) { + } catch (Throwable e) { logger.error("appendAddRecordTransactional:" + e, e); setErrorCondition(null, tx, e); } finally { @@ -1031,9 +1041,9 @@ public void run() { } } - private void checkJournalIsLoaded() { + private void checkJournalIsLoaded() throws Exception { if (state != JournalState.LOADED && state != JournalState.SYNCING) { - throw new IllegalStateException("Journal must be in state=" + JournalState.LOADED + ", was [" + state + "]"); + throw new ActiveMQShutdownException("Journal must be in state=" + JournalState.LOADED + ", was [" + state + "]"); } } @@ -1085,7 +1095,7 @@ public void run() { } tx.addPositive( usedFile, id, updateRecordTX.getEncodeSize() ); - } catch ( Exception e ) { + } catch (Throwable e ) { logger.error("appendUpdateRecordTransactional:" + e.getMessage(), e ); setErrorCondition(null, tx, e ); } finally { @@ -1132,7 +1142,7 @@ public void run() { } tx.addNegative(usedFile, id); - } catch (Exception e) { + } catch (Throwable e) { logger.error("appendDeleteRecordTransactional:" + e, e); setErrorCondition(null, tx, e); } finally { @@ -1185,7 +1195,10 @@ public void run() { } tx.prepare(usedFile); - } catch (Exception e) { + } catch (ActiveMQShutdownException e) { + result.fail(e); + logger.error("appendPrepareRecord:" + e, e); + } catch (Throwable e) { result.fail(e); logger.error("appendPrepareRecord:" + e, e); setErrorCondition(callback, tx, e); @@ -1267,6 +1280,9 @@ public void run() { tx.commit(usedFile); + } catch (ActiveMQShutdownException e) { + result.fail(e); + logger.error("appendCommitRecord:" + e, e); } catch (Throwable e) { result.fail(e); logger.error("appendCommitRecord:" + e, e); @@ -1317,6 +1333,9 @@ public void run() { JournalFile usedFile = appendRecord(rollbackRecord, false, sync, tx, callback); tx.rollback(usedFile); + } catch (ActiveMQShutdownException e) { + result.fail(e); + logger.error("appendRollbackRecord:" + e, e); } catch (Throwable e) { result.fail(e); logger.error("appendRollbackRecord:" + e, e); @@ -2360,10 +2379,10 @@ public synchronized void stop() throws Exception { return; } - setJournalState(JournalState.STOPPED); - flush(); + setJournalState(JournalState.STOPPED); + if (providedIOThreadPool == null) { threadPool.shutdown(); @@ -2681,6 +2700,8 @@ private JournalFile appendRecord(final JournalInternalRecord encoder, final JournalTransaction tx, final IOCallback parameterCallback) throws Exception { + checkJournalIsLoaded(); + final IOCallback callback; final int size = encoder.getEncodeSize(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java index 7c821a98f69..d28eec87fd2 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java @@ -126,8 +126,10 @@ */ public abstract class AbstractJournalStorageManager extends CriticalComponentImpl implements StorageManager { - private static final int CRITICAL_PATHS = 1; - private static final int CRITICAL_STORE = 0; + protected static final int CRITICAL_PATHS = 3; + protected static final int CRITICAL_STORE = 0; + protected static final int CRITICAL_STOP = 1; + protected static final int CRITICAL_STOP_2 = 2; private static final Logger logger = Logger.getLogger(AbstractJournalStorageManager.class); @@ -405,6 +407,16 @@ public void readUnLock() { leaveCritical(CRITICAL_STORE); } + /** for internal use and testsuite, don't use it outside of tests */ + public void writeLock() { + storageManagerLock.writeLock().lock(); + } + + /** for internal use and testsuite, don't use it outside of tests */ + public void writeUnlock() { + storageManagerLock.writeLock().unlock(); + } + @Override public void storeAcknowledge(final long queueID, final long messageID) throws Exception { readLock(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java index dd8bb22a7e1..867f2d43ece 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java @@ -229,9 +229,21 @@ private void cleanupIncompleteFiles() throws Exception { } @Override - public synchronized void stop(boolean ioCriticalError, boolean sendFailover) throws Exception { + public void stop(boolean ioCriticalError, boolean sendFailover) throws Exception { + try { + enterCritical(CRITICAL_STOP); + synchronized (this) { + if (internalStop(ioCriticalError, sendFailover)) + return; + } + } finally { + leaveCritical(CRITICAL_STOP); + } + } + + private boolean internalStop(boolean ioCriticalError, boolean sendFailover) throws Exception { if (!started) { - return; + return true; } if (!ioCriticalError) { @@ -255,30 +267,41 @@ public void run() { // that's ok } - // We cache the variable as the replicator could be changed between here and the time we call stop - // since sendLiveIsStopping may issue a close back from the channel - // and we want to ensure a stop here just in case - ReplicationManager replicatorInUse = replicator; - if (replicatorInUse != null) { - if (sendFailover) { - final OperationContext token = replicator.sendLiveIsStopping(ReplicationLiveIsStoppingMessage.LiveStopping.FAIL_OVER); - if (token != null) { - try { - token.waitCompletion(5000); - } catch (Exception e) { - // ignore it + enterCritical(CRITICAL_STOP_2); + storageManagerLock.writeLock().lock(); + try { + + // We cache the variable as the replicator could be changed between here and the time we call stop + // since sendLiveIsStopping may issue a close back from the channel + // and we want to ensure a stop here just in case + ReplicationManager replicatorInUse = replicator; + if (replicatorInUse != null) { + if (sendFailover) { + final OperationContext token = replicator.sendLiveIsStopping(ReplicationLiveIsStoppingMessage.LiveStopping.FAIL_OVER); + if (token != null) { + try { + token.waitCompletion(5000); + } catch (Exception e) { + // ignore it + } } } + // we cannot clear replication tokens, otherwise clients will eventually be informed of completion during a server's shutdown + // while the backup will never receive then + replicatorInUse.stop(false); } - replicatorInUse.stop(); - } - bindingsJournal.stop(); + bindingsJournal.stop(); - messageJournal.stop(); + messageJournal.stop(); - journalLoaded = false; + journalLoaded = false; - started = false; + started = false; + } finally { + storageManagerLock.writeLock().unlock(); + leaveCritical(CRITICAL_STOP_2); + } + return false; } /** diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationEndpoint.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationEndpoint.java index 15d53116e1a..998bbcfde83 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationEndpoint.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationEndpoint.java @@ -150,6 +150,20 @@ public synchronized void registerJournal(final byte id, final Journal journal) { journals[id] = journal; } + /** + * This is for tests basically, do not use it as its API is not guaranteed for future usage. + */ + public void pause() { + started = false; + } + + /** + * This is for tests basically, do not use it as its API is not guaranteed for future usage. + */ + public void resume() { + started = true; + } + @Override public void handlePacket(final Packet packet) { if (logger.isTraceEnabled()) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java index fbf7c6c539b..69737065052 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java @@ -282,6 +282,10 @@ public synchronized void start() throws ActiveMQException { @Override public void stop() throws Exception { + stop(true); + } + + public void stop(boolean clearTokens) throws Exception { synchronized (this) { if (!started) { logger.trace("Stopping being ignored as it hasn't been started"); @@ -297,7 +301,10 @@ public void stop() throws Exception { enabled = false; writable.set(true); - clearReplicationTokens(); + + if (clearTokens) { + clearReplicationTokens(); + } RemotingConnection toStop = remotingConnection; if (toStop != null) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NettyReplicationStopTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NettyReplicationStopTest.java new file mode 100644 index 00000000000..64343e308ee --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NettyReplicationStopTest.java @@ -0,0 +1,150 @@ +/* + * 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.artemis.tests.integration.cluster.failover; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.activemq.artemis.api.core.TransportConfiguration; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientProducer; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.api.core.client.ServerLocator; +import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal; +import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; +import org.apache.activemq.artemis.core.replication.ReplicationEndpoint; +import org.apache.activemq.artemis.core.server.NodeManager; +import org.apache.activemq.artemis.core.server.impl.InVMNodeManager; +import org.apache.activemq.artemis.tests.integration.cluster.util.SameProcessActiveMQServer; +import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer; +import org.junit.Assert; +import org.junit.Test; + +public class NettyReplicationStopTest extends FailoverTestBase { + + @Override + protected TestableServer createTestableServer(Configuration config) { + return new SameProcessActiveMQServer(createServer(true, config)); + } + + @Override + protected void createConfigs() throws Exception { + createReplicatedConfigs(); + } + + @Override + protected NodeManager createNodeManager() throws Exception { + return new InVMNodeManager(false); + } + + @Override + protected TransportConfiguration getAcceptorTransportConfiguration(final boolean live) { + return getNettyAcceptorTransportConfiguration(live); + } + + @Override + protected TransportConfiguration getConnectorTransportConfiguration(final boolean live) { + return getNettyConnectorTransportConfiguration(live); + } + + @Override + protected final void crash(boolean waitFailure, ClientSession... sessions) throws Exception { + if (sessions.length > 0) { + for (ClientSession session : sessions) { + waitForRemoteBackup(session.getSessionFactory(), 5, true, backupServer.getServer()); + } + } else { + waitForRemoteBackup(null, 5, true, backupServer.getServer()); + } + super.crash(waitFailure, sessions); + } + + @Override + protected final void crash(ClientSession... sessions) throws Exception { + crash(true, sessions); + } + + @Test + public void testReplicaStop() throws Exception { + + Map params = new HashMap<>(); + params.put(TransportConstants.HOST_PROP_NAME, "127.0.0.1"); + TransportConfiguration tc = createTransportConfiguration(true, false, params); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithHA(tc)).setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setBlockOnAcknowledge(true).setReconnectAttempts(15); + + final ClientSessionFactoryInternal sf = createSessionFactoryAndWaitForTopology(locator, 2); + + ClientSession session = sf.createSession(true, true); + + session.createQueue(ADDRESS, ADDRESS, null, true); + + ClientProducer producer = session.createProducer(ADDRESS); + + final int numMessages = 10; + + ReplicationEndpoint endpoint = backupServer.getServer().getReplicationEndpoint(); + + endpoint.pause(); + + ArrayList threads = new ArrayList<>(); + final ArrayList codesSent = new ArrayList<>(); + + CountDownLatch alignedOnSend = new CountDownLatch(10); + + for (int i = 0; i < numMessages; i++) { + final int code = i; + Thread t = new Thread("WillSend " + code) { + @Override + public void run() { + try { + ClientSession session = sf.createSession(true, true); + + ClientProducer producer = session.createProducer(ADDRESS); + + ClientMessage message = session.createMessage(true).putIntProperty("i", code); + alignedOnSend.countDown(); + System.out.println("blocking!!"); + producer.send(message); + codesSent.add(code); + + System.out.println("Sent!"); + + } catch (Exception e) { + // that's ok; + e.printStackTrace(); // logging just for debug & reference + } + } + }; + + t.start(); + + threads.add(t); + } + + Assert.assertTrue(alignedOnSend.await(10, TimeUnit.SECONDS)); + liveServer.stop(); + + Assert.assertEquals(0, codesSent.size()); + + } +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/xa/BasicXaTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/xa/BasicXaTest.java index 04fd1a98e63..d0d8ce3bc1c 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/xa/BasicXaTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/xa/BasicXaTest.java @@ -37,6 +37,7 @@ import org.apache.activemq.artemis.api.core.client.ServerLocator; import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.StoreConfiguration; +import org.apache.activemq.artemis.core.persistence.StorageManager; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; @@ -364,6 +365,105 @@ public void testReceivePrepareDoesntRollbackOnClose() throws Exception { } + @Test + public void testPrepareError() throws Exception { + Xid xid = newXID(); + + ClientSession clientSession2 = sessionFactory.createSession(false, true, true); + ClientProducer clientProducer = clientSession2.createProducer(atestq); + ClientMessage m1 = createTextMessage(clientSession2, "m1"); + ClientMessage m2 = createTextMessage(clientSession2, "m2"); + ClientMessage m3 = createTextMessage(clientSession2, "m3"); + ClientMessage m4 = createTextMessage(clientSession2, "m4"); + clientProducer.send(m1); + clientProducer.send(m2); + clientProducer.send(m3); + clientProducer.send(m4); + + clientSession.start(xid, XAResource.TMNOFLAGS); + clientSession.start(); + ClientConsumer clientConsumer = clientSession.createConsumer(atestq); + ClientMessage m = clientConsumer.receive(1000); + Assert.assertNotNull(m); + m.acknowledge(); + Assert.assertEquals(m.getBodyBuffer().readString(), "m1"); + m = clientConsumer.receive(1000); + Assert.assertNotNull(m); + m.acknowledge(); + Assert.assertEquals(m.getBodyBuffer().readString(), "m2"); + m = clientConsumer.receive(1000); + Assert.assertNotNull(m); + m.acknowledge(); + Assert.assertEquals(m.getBodyBuffer().readString(), "m3"); + m = clientConsumer.receive(1000); + Assert.assertNotNull(m); + m.acknowledge(); + Assert.assertEquals(m.getBodyBuffer().readString(), "m4"); + clientSession.end(xid, XAResource.TMSUCCESS); + + StorageManager journalStorageManager = messagingService.getStorageManager(); + + clientSession.prepare(xid); + + journalStorageManager.getMessageJournal().stop(); + try { + clientSession.commit(xid, false); + Assert.fail("Exception exptected"); + } catch (XAException e) { + Assert.assertTrue(e.errorCode == XAException.XA_RETRY); + } + } + + + @Test + public void testRollbackError() throws Exception { + Xid xid = newXID(); + + ClientSession clientSession2 = sessionFactory.createSession(false, true, true); + ClientProducer clientProducer = clientSession2.createProducer(atestq); + ClientMessage m1 = createTextMessage(clientSession2, "m1"); + ClientMessage m2 = createTextMessage(clientSession2, "m2"); + ClientMessage m3 = createTextMessage(clientSession2, "m3"); + ClientMessage m4 = createTextMessage(clientSession2, "m4"); + clientProducer.send(m1); + clientProducer.send(m2); + clientProducer.send(m3); + clientProducer.send(m4); + + clientSession.start(xid, XAResource.TMNOFLAGS); + clientSession.start(); + ClientConsumer clientConsumer = clientSession.createConsumer(atestq); + ClientMessage m = clientConsumer.receive(1000); + Assert.assertNotNull(m); + m.acknowledge(); + Assert.assertEquals(m.getBodyBuffer().readString(), "m1"); + m = clientConsumer.receive(1000); + Assert.assertNotNull(m); + m.acknowledge(); + Assert.assertEquals(m.getBodyBuffer().readString(), "m2"); + m = clientConsumer.receive(1000); + Assert.assertNotNull(m); + m.acknowledge(); + Assert.assertEquals(m.getBodyBuffer().readString(), "m3"); + m = clientConsumer.receive(1000); + Assert.assertNotNull(m); + m.acknowledge(); + Assert.assertEquals(m.getBodyBuffer().readString(), "m4"); + clientSession.end(xid, XAResource.TMSUCCESS); + + StorageManager journalStorageManager = messagingService.getStorageManager(); + + clientSession.prepare(xid); + + journalStorageManager.getMessageJournal().stop(); + try { + clientSession.rollback(xid); + Assert.fail("Exception exptected"); + } catch (XAException e) { + Assert.assertTrue(e.errorCode == XAException.XA_RETRY); + } + } + @Test public void testReceiveRollback() throws Exception { int numSessions = 100; From 55b0d5b0ea00f7eae6bdbf7391f03dd586d7924c Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 21 Aug 2018 11:50:34 -0400 Subject: [PATCH 162/207] ARTEMIS-2047 Compatible option for ActiveMQJMSClient --- .../artemis/utils/ClassloadingUtil.java | 32 +++- .../api/core/client/ActiveMQClient.java | 2 - .../artemis/api/jms/ActiveMQJMSClient.java | 48 +++++- .../jms/client/ActiveMQConnectionFactory.java | 3 +- .../ConnectionFactoryConfigurationImpl.java | 5 +- .../validateClient.groovy | 28 ++++ .../ActiveMQJMSClientCompatibilityTest.java | 92 +++++++++++ .../compatibility/ClasspathBaseTest.java | 152 ++++++++++++++++++ ...FactoryConfigurationSerializationTest.java | 2 +- .../tests/compatibility/ExportImportTest.java | 2 +- .../JournalCompatibilityTest.java | 2 +- .../artemis/tests/compatibility/MeshTest.java | 2 +- .../tests/compatibility/SendAckTest.java | 2 +- .../compatibility/SerializationTest.java | 2 +- .../compatibility/VersionedBaseTest.java | 137 ++-------------- 15 files changed, 371 insertions(+), 140 deletions(-) create mode 100644 tests/compatibility-tests/src/main/resources/ActiveMQJMSClientCompatibilityTest/validateClient.groovy create mode 100644 tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ActiveMQJMSClientCompatibilityTest.java create mode 100644 tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ClasspathBaseTest.java diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ClassloadingUtil.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ClassloadingUtil.java index e27ce0cca73..9ea1b610500 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ClassloadingUtil.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ClassloadingUtil.java @@ -17,6 +17,9 @@ package org.apache.activemq.artemis.utils; import java.net.URL; +import java.util.Properties; + +import org.jboss.logging.Logger; /** * This class will be used to perform generic class-loader operations, @@ -27,6 +30,8 @@ public final class ClassloadingUtil { + private static final Logger logger = Logger.getLogger(ClassloadingUtil.class); + private static final String INSTANTIATION_EXCEPTION_MESSAGE = "Your class must have a constructor without arguments. If it is an inner class, it must be static!"; public static Object newInstanceFromClassLoader(final String className) { @@ -84,7 +89,10 @@ public static Object newInstanceFromClassLoader(final String className, Object.. } public static URL findResource(final String resourceName) { - ClassLoader loader = ClassloadingUtil.class.getClassLoader(); + return findResource(ClassloadingUtil.class.getClassLoader(), resourceName); + } + + public static URL findResource(ClassLoader loader, final String resourceName) { try { URL resource = loader.getResource(resourceName); if (resource != null) @@ -98,4 +106,26 @@ public static URL findResource(final String resourceName) { return loader.getResource(resourceName); } + + + public static String loadProperty(ClassLoader loader, String propertiesFile, String name) { + Properties properties = loadProperties(loader, propertiesFile); + + return (String)properties.get(name); + } + + public static Properties loadProperties(ClassLoader loader, String propertiesFile) { + Properties properties = new Properties(); + + try { + URL url = findResource(loader, propertiesFile); + if (url != null) { + properties.load(url.openStream()); + } + } catch (Throwable ignored) { + logger.warn(ignored); + } + return properties; + } + } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java index ab647a31705..f4de508d646 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ActiveMQClient.java @@ -137,8 +137,6 @@ public final class ActiveMQClient { public static final boolean DEFAULT_USE_TOPOLOGY_FOR_LOADBALANCING = true; - public static final boolean DEFAULT_ENABLE_1X_PREFIXES = false; - public static final String THREAD_POOL_MAX_SIZE_PROPERTY_KEY = "activemq.artemis.client.global.thread.pool.max.size"; public static final String SCHEDULED_THREAD_POOL_SIZE_PROPERTY_KEY = "activemq.artemis.client.global.scheduled.thread.pool.core.size"; diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/api/jms/ActiveMQJMSClient.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/api/jms/ActiveMQJMSClient.java index 9cf0c4da0fd..0119e35d2c8 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/api/jms/ActiveMQJMSClient.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/api/jms/ActiveMQJMSClient.java @@ -21,14 +21,42 @@ import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration; import org.apache.activemq.artemis.api.core.TransportConfiguration; +import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQDestination; import org.apache.activemq.artemis.uri.ConnectionFactoryParser; +import org.apache.activemq.artemis.utils.ClassloadingUtil; +import org.jboss.logging.Logger; /** * A utility class for creating ActiveMQ Artemis client-side JMS managed resources. */ public class ActiveMQJMSClient { + private static final Logger logger = Logger.getLogger(ActiveMQJMSClient.class); + + public static final boolean DEFAULT_ENABLE_1X_PREFIXES; + + + static { + + String value1X = System.getProperty(ActiveMQJMSClient.class.getName() + ".enable1xPrefixes"); + + if (value1X == null) { + value1X = ClassloadingUtil.loadProperty(ActiveMQJMSClient.class.getClassLoader(), ActiveMQJMSClient.class.getName() + ".properties", "enable1xPrefixes"); + } + + boolean prefixes = false; + + + if (value1X != null) { + try { + prefixes = Boolean.parseBoolean(value1X); + } catch (Throwable e) { + logger.warn(e); + } + } + DEFAULT_ENABLE_1X_PREFIXES = prefixes; + } /** * Creates an ActiveMQConnectionFactory; @@ -115,21 +143,37 @@ public static ActiveMQConnectionFactory createConnectionFactoryWithoutHA(JMSFact /** * Creates a client-side representation of a JMS Topic. * + * This method is deprecated. Use {@link org.apache.activemq.artemis.jms.client.ActiveMQSession#createTopic(String)} as that method will know the proper + * prefix used at the target server. + * * @param name the name of the topic * @return The Topic */ + @Deprecated public static Topic createTopic(final String name) { - return ActiveMQDestination.createTopic(name); + if (DEFAULT_ENABLE_1X_PREFIXES) { + return ActiveMQDestination.createTopic(PacketImpl.OLD_TOPIC_PREFIX + name, name); + } else { + return ActiveMQDestination.createTopic(name); + } } /** * Creates a client-side representation of a JMS Queue. * + * This method is deprecated. Use {@link org.apache.activemq.artemis.jms.client.ActiveMQSession#createQueue(String)} (String)} as that method will know the proper + * prefix used at the target server. + * * * @param name the name of the queue * @return The Queue */ + @Deprecated public static Queue createQueue(final String name) { - return ActiveMQDestination.createQueue(name); + if (DEFAULT_ENABLE_1X_PREFIXES) { + return ActiveMQDestination.createQueue(PacketImpl.OLD_QUEUE_PREFIX + name, name); + } else { + return ActiveMQDestination.createQueue(name); + } } private ActiveMQJMSClient() { diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java index 17ee6fe302a..a0d036c0ffe 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java @@ -48,6 +48,7 @@ import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; import org.apache.activemq.artemis.api.core.client.ServerLocator; +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.api.jms.ActiveMQJMSConstants; import org.apache.activemq.artemis.api.jms.JMSFactoryType; import org.apache.activemq.artemis.core.client.impl.ServerLocatorImpl; @@ -94,7 +95,7 @@ public class ActiveMQConnectionFactory extends JNDIStorable implements Connectio private boolean ignoreJTA; - private boolean enable1xPrefixes = ActiveMQClient.DEFAULT_ENABLE_1X_PREFIXES; + private boolean enable1xPrefixes = ActiveMQJMSClient.DEFAULT_ENABLE_1X_PREFIXES; @Override public void writeExternal(ObjectOutput out) throws IOException { diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java index c2ac0b6ad0e..e1718bd0776 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/config/impl/ConnectionFactoryConfigurationImpl.java @@ -23,6 +23,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.client.ActiveMQClient; +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.api.jms.JMSFactoryType; import org.apache.activemq.artemis.jms.server.config.ConnectionFactoryConfiguration; import org.apache.activemq.artemis.utils.BufferHelper; @@ -124,7 +125,7 @@ public class ConnectionFactoryConfigurationImpl implements ConnectionFactoryConf private int initialMessagePacketSize = ActiveMQClient.DEFAULT_INITIAL_MESSAGE_PACKET_SIZE; - private boolean enable1xPrefixes = ActiveMQClient.DEFAULT_ENABLE_1X_PREFIXES; + private boolean enable1xPrefixes = ActiveMQJMSClient.DEFAULT_ENABLE_1X_PREFIXES; private boolean enableSharedClientID = ActiveMQClient.DEFAULT_ENABLED_SHARED_CLIENT_ID; @@ -640,7 +641,7 @@ public void decode(final ActiveMQBuffer buffer) { deserializationWhiteList = BufferHelper.readNullableSimpleStringAsString(buffer); - enable1xPrefixes = buffer.readableBytes() > 0 ? buffer.readBoolean() : ActiveMQClient.DEFAULT_ENABLE_1X_PREFIXES; + enable1xPrefixes = buffer.readableBytes() > 0 ? buffer.readBoolean() : ActiveMQJMSClient.DEFAULT_ENABLE_1X_PREFIXES; enableSharedClientID = buffer.readableBytes() > 0 ? buffer.readBoolean() : ActiveMQClient.DEFAULT_ENABLED_SHARED_CLIENT_ID; diff --git a/tests/compatibility-tests/src/main/resources/ActiveMQJMSClientCompatibilityTest/validateClient.groovy b/tests/compatibility-tests/src/main/resources/ActiveMQJMSClientCompatibilityTest/validateClient.groovy new file mode 100644 index 00000000000..400a69eb824 --- /dev/null +++ b/tests/compatibility-tests/src/main/resources/ActiveMQJMSClientCompatibilityTest/validateClient.groovy @@ -0,0 +1,28 @@ +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient +import org.apache.activemq.artemis.jms.client.ActiveMQQueue +import org.apache.activemq.artemis.jms.client.ActiveMQTopic +import org.apache.activemq.artemis.tests.compatibility.GroovyRun + +/* + * 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. + */ + +ActiveMQQueue queue = (ActiveMQQueue) ActiveMQJMSClient.createQueue("q1"); +GroovyRun.assertEquals("jms.queue.q1", queue.getAddress()); +GroovyRun.assertEquals("q1", queue.getQueueName()); +ActiveMQTopic topic = (ActiveMQTopic) ActiveMQJMSClient.createTopic("t1"); +GroovyRun.assertEquals("jms.topic.t1", topic.getAddress()); +GroovyRun.assertEquals("t1", topic.getTopicName()); diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ActiveMQJMSClientCompatibilityTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ActiveMQJMSClientCompatibilityTest.java new file mode 100644 index 00000000000..91e0e2277bb --- /dev/null +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ActiveMQJMSClientCompatibilityTest.java @@ -0,0 +1,92 @@ +/* + * 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.artemis.tests.compatibility; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; + +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; +import org.apache.activemq.artemis.jms.client.ActiveMQQueue; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; +import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.SNAPSHOT; +import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.ONE_FIVE; + +public class ActiveMQJMSClientCompatibilityTest extends ClasspathBaseTest { + + @Test + public void testActiveMQJMSCompatibility_1XPrefix_SNAPSHOT() throws Exception { + + Assert.assertFalse(ActiveMQJMSClient.DEFAULT_ENABLE_1X_PREFIXES); + ActiveMQQueue queue = (ActiveMQQueue)ActiveMQJMSClient.createQueue("t1"); + // this step is to guarantee the class is not affected when there's no property in place + Assert.assertEquals("t1", queue.getAddress()); + + ClassLoader loader = getClasspath(SNAPSHOT, true); + + System.setProperty(ActiveMQJMSClient.class.getName() + ".enable1xPrefixes", "true"); + + try { + + evaluate(loader, "ActiveMQJMSClientCompatibilityTest/validateClient.groovy"); + + } finally { + System.clearProperty(ActiveMQJMSClient.class.getName() + ".enable1xPrefixes"); + } + + } + + @Test + public void testActiveMQJMSCompatibility_1XPrefix_SNAPSHOT_with_properties() throws Exception { + + Assert.assertFalse(ActiveMQJMSClient.DEFAULT_ENABLE_1X_PREFIXES); + ActiveMQQueue queue = (ActiveMQQueue)ActiveMQJMSClient.createQueue("t1"); + // this step is to guarantee the class is not affected when there's no property in place + Assert.assertEquals("t1", queue.getAddress()); + + File file = serverFolder.newFile(ActiveMQJMSClient.class.getName() + ".properties"); + + FileOutputStream fileOutputStream = new FileOutputStream(file); + PrintStream stream = new PrintStream(fileOutputStream); + stream.println("enable1xPrefixes=true"); + stream.close(); + + String snapshotPath = System.getProperty(SNAPSHOT); + Assume.assumeNotNull(snapshotPath); + + String path = serverFolder.getRoot().getAbsolutePath() + File.pathSeparator + snapshotPath; + + + ClassLoader loader = defineClassLoader(path); + + evaluate(loader, "ActiveMQJMSClientCompatibilityTest/validateClient.groovy"); + + } + + @Test + + // The purpose here is just to validate the test itself. Nothing to be fixed here + public void testActiveMQJMSCompatibility_1XPrefix_ONE_FIVE() throws Exception { + ClassLoader loader = getClasspath(ONE_FIVE, false); + + evaluate(loader, "ActiveMQJMSClientCompatibilityTest/validateClient.groovy"); + + } +} diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ClasspathBaseTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ClasspathBaseTest.java new file mode 100644 index 00000000000..d2a4b50e38b --- /dev/null +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ClasspathBaseTest.java @@ -0,0 +1,152 @@ +/* + * 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.artemis.tests.compatibility; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import org.junit.Assume; +import org.junit.ClassRule; +import org.junit.rules.TemporaryFolder; + +import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.SNAPSHOT; + +public class ClasspathBaseTest { + + + @ClassRule + public static TemporaryFolder serverFolder; + + static { + File parent = new File("./target/tmp"); + parent.mkdirs(); + serverFolder = new TemporaryFolder(parent); + } + + protected static Map loaderMap = new HashMap<>(); + + private static HashSet printed = new HashSet<>(); + + protected static ClassLoader defineClassLoader(String classPath) throws MalformedURLException { + String[] classPathArray = classPath.split(File.pathSeparator); + URL[] elements = new URL[classPathArray.length]; + for (int i = 0; i < classPathArray.length; i++) { + elements[i] = new File(classPathArray[i]).toPath().toUri().toURL(); + } + + return new URLClassLoader(elements, null); + } + + public static ClassLoader getClasspath(String name) throws Exception { + return getClasspath(name, false); + } + + public static ClassLoader getClasspath(String name, boolean forceNew) throws Exception { + + if (!forceNew) { + if (name.equals(SNAPSHOT)) { + return VersionedBaseTest.class.getClassLoader(); + } + + ClassLoader loader = loaderMap.get(name); + if (loader != null && !forceNew) { + return loader; + } + } + + String value = System.getProperty(name); + + if (!printed.contains(name)) { + boolean ok = value != null && !value.trim().isEmpty(); + if (!ok) { + System.out.println("Add \"-D" + name + "=\'CLASSPATH\'\" into your VM settings"); + System.out.println("You will see it in the output from mvn install at the compatibility-tests"); + System.out.println("... look for output from dependency-scan"); + + // our dependency scan used at the pom under compatibility-tests/pom.xml will generate these, example: + // [INFO] dependency-scan setting: -DARTEMIS-140="/Users/someuser/....." + // copy that into your IDE setting and you should be able to debug it + } + Assume.assumeTrue("Cannot run these tests, no classpath found", ok); + } + + ClassLoader loader = defineClassLoader(value); + if (!forceNew) { + // if we are forcing a new one, there's no point in caching it + loaderMap.put(name, loader); + } + + return loader; + } + + protected static Object evaluate(ClassLoader loader, String script, String... arguments) throws Exception { + return tclCall(loader, () -> { + Class clazz = loader.loadClass(GroovyRun.class.getName()); + Method method = clazz.getMethod("evaluate", String.class, String[].class); + return method.invoke(null, script, arguments); + }); + } + + protected static void setVariable(ClassLoader loader, String name, Object object) throws Exception { + tclCall(loader, () -> { + Class clazz = loader.loadClass(GroovyRun.class.getName()); + Method method = clazz.getMethod("setVariable", String.class, Object.class); + method.invoke(null, name, object); + return null; + }); + } + + protected static Object setVariable(ClassLoader loader, String name) throws Exception { + return tclCall(loader, () -> { + Class clazz = loader.loadClass(GroovyRun.class.getName()); + Method method = clazz.getMethod("getVariable", String.class); + return method.invoke(null, name); + }); + } + + protected static Object execute(ClassLoader loader, String script) throws Exception { + return tclCall(loader, () -> { + Class clazz = loader.loadClass(GroovyRun.class.getName()); + Method method = clazz.getMethod("execute", String.class); + return method.invoke(null, script); + }); + } + + protected static Object tclCall(ClassLoader loader, CallIt run) throws Exception { + + ClassLoader original = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(loader); + try { + return run.run(); + } finally { + Thread.currentThread().setContextClassLoader(original); + } + } + + public interface CallIt { + + Object run() throws Exception; + } + +} diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java index 9b7dab8483f..93894525fdf 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java @@ -39,7 +39,7 @@ * cd /compatibility-tests * mvn install -Ptests | tee output.log * - * on the output.log you will see the output generated by {@link #getClasspathProperty(String)} + * on the output.log you will see the output generated by {@link #getClasspath(String)} * * On your IDE, edit the Run Configuration to your test and add those -D as parameters to your test. * On Idea you would do the following: diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ExportImportTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ExportImportTest.java index e70d02ce398..854ef1e0966 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ExportImportTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ExportImportTest.java @@ -37,7 +37,7 @@ * cd /compatibility-tests * mvn install -Ptests | tee output.log * - * on the output.log you will see the output generated by {@link #getClasspathProperty(String)} + * on the output.log you will see the output generated by {@link #getClasspath(String)} * * On your IDE, edit the Run Configuration to your test and add those -D as parameters to your test. * On Idea you would do the following: diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java index e63f70b27f4..27ebdd02c3d 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java @@ -39,7 +39,7 @@ * cd /compatibility-tests * mvn install -Ptests | tee output.log * - * on the output.log you will see the output generated by {@link #getClasspathProperty(String)} + * on the output.log you will see the output generated by {@link #getClasspath(String)} * * On your IDE, edit the Run Configuration to your test and add those -D as parameters to your test. * On Idea you would do the following: diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/MeshTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/MeshTest.java index 8d4939ea235..37219792a7d 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/MeshTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/MeshTest.java @@ -42,7 +42,7 @@ * cd /compatibility-tests * mvn install -Ptests | tee output.log * - * on the output.log you will see the output generated by {@link #getClasspathProperty(String)} + * on the output.log you will see the output generated by {@link #getClasspath(String)} * * On your IDE, edit the Run Configuration to your test and add those -D as parameters to your test. * On Idea you would do the following: diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SendAckTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SendAckTest.java index 7fe9eb4ca61..168a56f6956 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SendAckTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SendAckTest.java @@ -36,7 +36,7 @@ * cd /compatibility-tests * mvn install -Ptests | tee output.log * - * on the output.log you will see the output generated by {@link #getClasspathProperty(String)} + * on the output.log you will see the output generated by {@link #getClasspath(String)} * * On your IDE, edit the Run Configuration to your test and add those -D as parameters to your test. * On Idea you would do the following: diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SerializationTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SerializationTest.java index cd7daf2eeb1..bb238164f79 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SerializationTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SerializationTest.java @@ -38,7 +38,7 @@ * cd /compatibility-tests * mvn install -Ptests | tee output.log * - * on the output.log you will see the output generated by {@link #getClasspathProperty(String)} + * on the output.log you will see the output generated by {@link #getClasspath(String)} * * On your IDE, edit the Run Configuration to your test and add those -D as parameters to your test. * On Idea you would do the following: diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java index 19bafd06de3..ab5331d5d29 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java @@ -18,24 +18,12 @@ package org.apache.activemq.artemis.tests.compatibility; import java.io.File; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Map; import org.junit.AfterClass; -import org.junit.Assume; -import org.junit.ClassRule; -import org.junit.rules.TemporaryFolder; -import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.SNAPSHOT; - -public abstract class VersionedBaseTest { +public abstract class VersionedBaseTest extends ClasspathBaseTest { protected final String server; protected final String sender; @@ -45,8 +33,6 @@ public abstract class VersionedBaseTest { protected ClassLoader senderClassloader; protected ClassLoader receiverClassloader; - protected static Map loaderMap = new HashMap<>(); - public VersionedBaseTest(String server, String sender, String receiver) throws Exception { if (server == null) { server = sender; @@ -54,25 +40,9 @@ public VersionedBaseTest(String server, String sender, String receiver) throws E this.server = server; this.sender = sender; this.receiver = receiver; - this.serverClassloader = getClasspathProperty(server); - this.senderClassloader = getClasspathProperty(sender); - this.receiverClassloader = getClasspathProperty(receiver); - } - - // This is a test optimization.. - // if false it will span a new VM for each classLoader used. - // this can be a bit faster - public static final boolean USE_CLASSLOADER = true; - - private static HashSet printed = new HashSet<>(); - - @ClassRule - public static TemporaryFolder serverFolder; - - static { - File parent = new File("./target/tmp"); - parent.mkdirs(); - serverFolder = new TemporaryFolder(parent); + this.serverClassloader = getClasspath(server); + this.senderClassloader = getClasspath(sender); + this.receiverClassloader = getClasspath(receiver); } @AfterClass @@ -80,97 +50,6 @@ public static void cleanup() { loaderMap.clear(); } - protected static Object evaluate(ClassLoader loader, String script, String... arguments) throws Exception { - return tclCall(loader, () -> { - Class clazz = loader.loadClass(GroovyRun.class.getName()); - Method method = clazz.getMethod("evaluate", String.class, String[].class); - return method.invoke(null, script, arguments); - }); - } - - protected static void setVariable(ClassLoader loader, String name, Object object) throws Exception { - tclCall(loader, () -> { - Class clazz = loader.loadClass(GroovyRun.class.getName()); - Method method = clazz.getMethod("setVariable", String.class, Object.class); - method.invoke(null, name, object); - return null; - }); - } - - protected static Object setVariable(ClassLoader loader, String name) throws Exception { - return tclCall(loader, () -> { - Class clazz = loader.loadClass(GroovyRun.class.getName()); - Method method = clazz.getMethod("getVariable", String.class); - return method.invoke(null, name); - }); - } - - protected static Object execute(ClassLoader loader, String script) throws Exception { - return tclCall(loader, () -> { - Class clazz = loader.loadClass(GroovyRun.class.getName()); - Method method = clazz.getMethod("execute", String.class); - return method.invoke(null, script); - }); - } - - protected static Object tclCall(ClassLoader loader, CallIt run) throws Exception { - - ClassLoader original = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(loader); - try { - return run.run(); - } finally { - Thread.currentThread().setContextClassLoader(original); - } - } - - public interface CallIt { - Object run() throws Exception; - } - - protected static ClassLoader defineClassLoader(String classPath) throws MalformedURLException { - String[] classPathArray = classPath.split(File.pathSeparator); - URL[] elements = new URL[classPathArray.length]; - for (int i = 0; i < classPathArray.length; i++) { - elements[i] = new File(classPathArray[i]).toPath().toUri().toURL(); - } - - return new URLClassLoader(elements, null); - } - - protected static ClassLoader getClasspathProperty(String name) throws Exception { - - if (name.equals(SNAPSHOT)) { - return VersionedBaseTest.class.getClassLoader(); - } - - ClassLoader loader = loaderMap.get(name); - if (loader != null) { - return loader; - } - - String value = System.getProperty(name); - - if (!printed.contains(name)) { - boolean ok = value != null && !value.trim().isEmpty(); - if (!ok) { - System.out.println("Add \"-D" + name + "=\'CLASSPATH\'\" into your VM settings"); - System.out.println("You will see it in the output from mvn install at the compatibility-tests"); - System.out.println("... look for output from dependency-scan"); - - // our dependency scan used at the pom under compatibility-tests/pom.xml will generate these, example: - // [INFO] dependency-scan setting: -DARTEMIS-140="/Users/someuser/....." - // copy that into your IDE setting and you should be able to debug it - } - Assume.assumeTrue("Cannot run these tests, no classpath found", ok); - } - - loader = defineClassLoader(value); - loaderMap.put(name, loader); - - return loader; - } - protected static List combinatory(Object[] rootSide, Object[] sideLeft, Object[] sideRight) { LinkedList combinations = new LinkedList<>(); @@ -193,7 +72,12 @@ public void startServer(File folder, ClassLoader loader, String serverName, Stri startServer(folder, loader, serverName, globalMaxSize, false); } - public void startServer(File folder, ClassLoader loader, String serverName, String globalMaxSize, boolean setAddressSettings) throws Throwable { + + public void startServer(File folder, + ClassLoader loader, + String serverName, + String globalMaxSize, + boolean setAddressSettings) throws Throwable { folder.mkdirs(); String scriptToUse; @@ -208,6 +92,7 @@ public void startServer(File folder, ClassLoader loader, String serverName, Stri setVariable(loader, "setAddressSettings", setAddressSettings); evaluate(loader, scriptToUse, folder.getAbsolutePath(), serverName, server, sender, receiver, globalMaxSize); } + public void stopServer(ClassLoader loader) throws Throwable { execute(loader, "server.stop()"); } From 7558997d02565c2760bb3c4f5985e2c0ff52ce0c Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 21 Aug 2018 21:59:13 -0400 Subject: [PATCH 163/207] NO-JIRA Adding trace on RA Scan --- .../protocol/core/impl/ActiveMQSessionContext.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java index 1268bbac401..4ee7ee9a79b 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java @@ -577,6 +577,17 @@ public Xid[] xaScan() throws ActiveMQException { Xid[] xidArray = xids.toArray(new Xid[xids.size()]); + if (logger.isTraceEnabled()) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < xidArray.length; i++) { + buffer.append(xidArray[i].toString()); + if (i + 1 < xidArray.length) { + buffer.append(","); + } + } + logger.trace("xaScan returning " + xidArray.length + " xids = [" + buffer.toString() + "]"); + } + return xidArray; } From 5a36b516e075b21b652c0ddac819c1c3f1998bdc Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Wed, 22 Aug 2018 13:52:12 +0100 Subject: [PATCH 164/207] ARTEMIS-2023 Fix cast on ActiveMQActivation --- .../apache/activemq/artemis/ra/inflow/ActiveMQActivation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java index 863a252f018..d6013e32b23 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java @@ -455,7 +455,7 @@ protected void setupCF() throws Exception { // to make sure we won't close anyone's connection factory when we stop the MDB factory = ActiveMQJMSClient.createConnectionFactory(((ActiveMQConnectionFactory) fac).toURI().toString(), "internalConnection"); factory.setEnableSharedClientID(true); - factory.setEnable1xPrefixes(((ActiveMQResourceAdapter) fac).isEnable1xPrefixes()); + factory.setEnable1xPrefixes(((ActiveMQConnectionFactory) fac).isEnable1xPrefixes()); } else { factory = ra.newConnectionFactory(spec); } From 95ec8ea433d1bd5cf8294b0018c1edb7d45c07f0 Mon Sep 17 00:00:00 2001 From: Carsten Lohmann Date: Wed, 22 Aug 2018 10:06:30 +0200 Subject: [PATCH 165/207] ARTEMIS-2044 Add onSendException, onMessageRouteException to ActiveMQServerMessagePlugin --- .../core/postoffice/impl/PostOfficeImpl.java | 89 ++++++++++--------- .../core/server/impl/ServerSessionImpl.java | 78 ++++++++-------- .../plugin/ActiveMQServerMessagePlugin.java | 30 +++++++ .../impl/LoggingActiveMQServerPlugin.java | 46 ++++++++++ .../LoggingActiveMQServerPluginLogger.java | 28 ++++++ .../plugin/MethodCalledVerifier.java | 19 ++++ 6 files changed, 213 insertions(+), 77 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index 9a3e84478f0..598c32b46b0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -877,62 +877,69 @@ public RoutingStatus route(final Message message, logger.trace("Message after routed=" + message); } - if (context.getQueueCount() == 0) { - // Send to DLA if appropriate + try { + if (context.getQueueCount() == 0) { + // Send to DLA if appropriate - AddressSettings addressSettings = addressSettingsRepository.getMatch(address.toString()); + AddressSettings addressSettings = addressSettingsRepository.getMatch(address.toString()); - boolean sendToDLA = addressSettings.isSendToDLAOnNoRoute(); + boolean sendToDLA = addressSettings.isSendToDLAOnNoRoute(); - if (sendToDLA) { - // Send to the DLA for the address + if (sendToDLA) { + // Send to the DLA for the address - SimpleString dlaAddress = addressSettings.getDeadLetterAddress(); + SimpleString dlaAddress = addressSettings.getDeadLetterAddress(); - if (logger.isDebugEnabled()) { - logger.debug("sending message to dla address = " + dlaAddress + ", message=" + message); - } + if (logger.isDebugEnabled()) { + logger.debug("sending message to dla address = " + dlaAddress + ", message=" + message); + } - if (dlaAddress == null) { - result = RoutingStatus.NO_BINDINGS; - ActiveMQServerLogger.LOGGER.noDLA(address); - } else { - message.referenceOriginalMessage(message, null); + if (dlaAddress == null) { + result = RoutingStatus.NO_BINDINGS; + ActiveMQServerLogger.LOGGER.noDLA(address); + } else { + message.referenceOriginalMessage(message, null); - message.setAddress(dlaAddress); + message.setAddress(dlaAddress); - message.reencode(); + message.reencode(); - route(message, context.getTransaction(), false); - result = RoutingStatus.NO_BINDINGS_DLA; - } - } else { - result = RoutingStatus.NO_BINDINGS; + route(message, context.getTransaction(), false); + result = RoutingStatus.NO_BINDINGS_DLA; + } + } else { + result = RoutingStatus.NO_BINDINGS; - if (logger.isDebugEnabled()) { - logger.debug("Message " + message + " is not going anywhere as it didn't have a binding on address:" + address); - } + if (logger.isDebugEnabled()) { + logger.debug("Message " + message + " is not going anywhere as it didn't have a binding on address:" + address); + } - if (message.isLargeMessage()) { - ((LargeServerMessage) message).deleteFile(); + if (message.isLargeMessage()) { + ((LargeServerMessage) message).deleteFile(); + } } - } - } else { - result = RoutingStatus.OK; - try { - processRoute(message, context, direct); - } catch (ActiveMQAddressFullException e) { - if (startedTX.get()) { - context.getTransaction().rollback(); - } else if (context.getTransaction() != null) { - context.getTransaction().markAsRollbackOnly(e); + } else { + result = RoutingStatus.OK; + try { + processRoute(message, context, direct); + } catch (ActiveMQAddressFullException e) { + if (startedTX.get()) { + context.getTransaction().rollback(); + } else if (context.getTransaction() != null) { + context.getTransaction().markAsRollbackOnly(e); + } + throw e; } - throw e; } - } - if (startedTX.get()) { - context.getTransaction().commit(); + if (startedTX.get()) { + context.getTransaction().commit(); + } + } catch (Exception e) { + if (server.hasBrokerMessagePlugins()) { + server.callBrokerMessagePlugins(plugin -> plugin.onMessageRouteException(message, context, direct, rejectDuplicates, e)); + } + throw e; } if (server.hasBrokerMessagePlugins()) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java index 4910e6621b1..d868a2fb913 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java @@ -1426,54 +1426,60 @@ public synchronized RoutingStatus send(Transaction tx, server.callBrokerMessagePlugins(plugin -> plugin.beforeSend(this, tx, message, direct, noAutoCreateQueue)); } - // If the protocol doesn't support flow control, we have no choice other than fail the communication - if (!this.getRemotingConnection().isSupportsFlowControl() && pagingManager.isDiskFull()) { - ActiveMQIOErrorException exception = ActiveMQMessageBundle.BUNDLE.diskBeyondLimit(); - this.getRemotingConnection().fail(exception); - throw exception; - } - final RoutingStatus result; - //large message may come from StompSession directly, in which - //case the id header already generated. - if (!message.isLargeMessage()) { - long id = storageManager.generateID(); - // This will re-encode the message - message.setMessageID(id); - } + try { + // If the protocol doesn't support flow control, we have no choice other than fail the communication + if (!this.getRemotingConnection().isSupportsFlowControl() && pagingManager.isDiskFull()) { + ActiveMQIOErrorException exception = ActiveMQMessageBundle.BUNDLE.diskBeyondLimit(); + this.getRemotingConnection().fail(exception); + throw exception; + } - SimpleString address = message.getAddressSimpleString(); + //large message may come from StompSession directly, in which + //case the id header already generated. + if (!message.isLargeMessage()) { + long id = storageManager.generateID(); + // This will re-encode the message + message.setMessageID(id); + } - if (defaultAddress == null && address != null) { - defaultAddress = address; - } + SimpleString address = message.getAddressSimpleString(); - if (address == null) { - // We don't want to force a re-encode when the message gets sent to the consumer - message.setAddress(defaultAddress); - } + if (defaultAddress == null && address != null) { + defaultAddress = address; + } - if (logger.isTraceEnabled()) { - logger.trace("send(message=" + message + ", direct=" + direct + ") being called"); - } + if (address == null) { + // We don't want to force a re-encode when the message gets sent to the consumer + message.setAddress(defaultAddress); + } - if (message.getAddress() == null) { - // This could happen with some tests that are ignoring messages - throw ActiveMQMessageBundle.BUNDLE.noAddress(); - } + if (logger.isTraceEnabled()) { + logger.trace("send(message=" + message + ", direct=" + direct + ") being called"); + } - if (message.getAddressSimpleString().equals(managementAddress)) { - // It's a management message + if (message.getAddress() == null) { + // This could happen with some tests that are ignoring messages + throw ActiveMQMessageBundle.BUNDLE.noAddress(); + } - result = handleManagementMessage(tx, message, direct); - } else { - result = doSend(tx, message, address, direct, noAutoCreateQueue); - } + if (message.getAddressSimpleString().equals(managementAddress)) { + // It's a management message + result = handleManagementMessage(tx, message, direct); + } else { + result = doSend(tx, message, address, direct, noAutoCreateQueue); + } + + } catch (Exception e) { + if (server.hasBrokerMessagePlugins()) { + server.callBrokerMessagePlugins(plugin -> plugin.onSendException(this, tx, message, direct, noAutoCreateQueue, e)); + } + throw e; + } if (server.hasBrokerMessagePlugins()) { server.callBrokerMessagePlugins(plugin -> plugin.afterSend(this, tx, message, direct, noAutoCreateQueue, result)); } - return result; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerMessagePlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerMessagePlugin.java index aef0970a4f6..404e8a4f9c2 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerMessagePlugin.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerMessagePlugin.java @@ -65,6 +65,21 @@ default void afterSend(ServerSession session, Transaction tx, Message message, b this.afterSend(tx, message, direct, noAutoCreateQueue, result); } + /** + * When there was an exception sending the message + * + * @param session + * @param tx + * @param message + * @param direct + * @param noAutoCreateQueue + * @param e the exception that occurred when sending the message + * @throws ActiveMQException + */ + default void onSendException(ServerSession session, Transaction tx, Message message, boolean direct, boolean noAutoCreateQueue, + Exception e) throws ActiveMQException { + + } /** * Before a message is sent @@ -128,6 +143,21 @@ default void afterMessageRoute(Message message, RoutingContext context, boolean } + /** + * When there was an error routing the message + * + * @param message + * @param context + * @param direct + * @param rejectDuplicates + * @param e the exception that occurred during message routing + * @throws ActiveMQException + */ + default void onMessageRouteException(Message message, RoutingContext context, boolean direct, boolean rejectDuplicates, + Exception e) throws ActiveMQException { + + } + /** * Before a message is delivered to a client consumer * diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/impl/LoggingActiveMQServerPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/impl/LoggingActiveMQServerPlugin.java index ff23b59cb52..3483472ce0a 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/impl/LoggingActiveMQServerPlugin.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/impl/LoggingActiveMQServerPlugin.java @@ -491,6 +491,32 @@ public void afterSend(ServerSession session, } } + @Override + public void onSendException(ServerSession session, + Transaction tx, + Message message, + boolean direct, + boolean noAutoCreateQueue, + Exception e) throws ActiveMQException { + if (logAll || logSendingEvents) { + + if (LoggingActiveMQServerPluginLogger.LOGGER.isDebugEnabled()) { + //details - debug level + LoggingActiveMQServerPluginLogger.LOGGER.onSendErrorDetails((message == null ? UNAVAILABLE : Long.toString(message.getMessageID())), + message, (session == null ? UNAVAILABLE : session.getName()), + tx, session, direct, noAutoCreateQueue); + } + + if (LoggingActiveMQServerPluginLogger.LOGGER.isInfoEnabled()) { + //info level log + LoggingActiveMQServerPluginLogger.LOGGER.onSendError((message == null ? UNAVAILABLE : Long.toString(message.getMessageID())), + (session == null ? UNAVAILABLE : session.getName()), + (session == null ? UNAVAILABLE : session.getConnectionID().toString()), + e); + } + } + } + /** * Before a message is routed * @@ -540,6 +566,26 @@ public void afterMessageRoute(Message message, } } + @Override + public void onMessageRouteException(Message message, + RoutingContext context, + boolean direct, + boolean rejectDuplicates, + Exception e) throws ActiveMQException { + if (logAll || logSendingEvents) { + + //details - debug level logging + LoggingActiveMQServerPluginLogger.LOGGER.onMessageRouteErrorDetails(message, context, direct, rejectDuplicates); + + if (LoggingActiveMQServerPluginLogger.LOGGER.isInfoEnabled()) { + //info level log + LoggingActiveMQServerPluginLogger.LOGGER.onMessageRouteError((message == null ? UNAVAILABLE : Long.toString(message.getMessageID())), + e); + } + + } + } + /** * Before a message is delivered to a client consumer * diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/impl/LoggingActiveMQServerPluginLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/impl/LoggingActiveMQServerPluginLogger.java index f519dd0e0e7..fa697ba6241 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/impl/LoggingActiveMQServerPluginLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/impl/LoggingActiveMQServerPluginLogger.java @@ -141,6 +141,15 @@ void afterDeliver(String messageID, @Message(id = 841016, value = "criticalFailure called with criticalComponent: {0}", format = Message.Format.MESSAGE_FORMAT) void criticalFailure(CriticalComponent components); + @LogMessage(level = Logger.Level.INFO) + @Message(id = 841017, value = "error sending message with ID: {0}, session name: {1}, session connectionID: {2}," + + " exception: {3}", format = Message.Format.MESSAGE_FORMAT) + void onSendError(String messageID, String sessionName, String sessionConnectionID, Exception e); + + @LogMessage(level = Logger.Level.INFO) + @Message(id = 841018, value = "error routing message with ID: {0}, exception: {1}", format = Message.Format.MESSAGE_FORMAT) + void onMessageRouteError(String messageID, Exception e); + //DEBUG messages @LogMessage(level = Logger.Level.DEBUG) @@ -258,4 +267,23 @@ void afterDeliverDetails(String messageID, @Message(id = 843015, value = "beforeDeployBridge called with bridgeConfiguration: {0}", format = Message.Format.MESSAGE_FORMAT) void beforeDeployBridge(BridgeConfiguration config); + @LogMessage(level = Logger.Level.DEBUG) + @Message(id = 843016, value = "onSendError message ID: {0}, message {1}, session name: {2} with tx: {3}, session: {4}, direct: {5}," + + " noAutoCreateQueue: {6}", format = Message.Format.MESSAGE_FORMAT) + void onSendErrorDetails(String messageID, + org.apache.activemq.artemis.api.core.Message message, + String sessionName, + Transaction tx, + ServerSession session, + boolean direct, + boolean noAutoCreateQueue); + + @LogMessage(level = Logger.Level.DEBUG) + @Message(id = 843017, value = "onMessageRouteError message: {0}, with context: {1}, direct: {2}, rejectDuplicates: {3}", + format = Message.Format.MESSAGE_FORMAT) + void onMessageRouteErrorDetails(org.apache.activemq.artemis.api.core.Message message, + RoutingContext context, + boolean direct, + boolean rejectDuplicates); + } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/MethodCalledVerifier.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/MethodCalledVerifier.java index 9c245059461..0d802cf073f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/MethodCalledVerifier.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/plugin/MethodCalledVerifier.java @@ -90,8 +90,10 @@ public class MethodCalledVerifier implements ActiveMQServerPlugin { public static final String MESSAGE_ACKED = "messageAcknowledged"; public static final String BEFORE_SEND = "beforeSend"; public static final String AFTER_SEND = "afterSend"; + public static final String ON_SEND_EXCEPTION = "onSendException"; public static final String BEFORE_MESSAGE_ROUTE = "beforeMessageRoute"; public static final String AFTER_MESSAGE_ROUTE = "afterMessageRoute"; + public static final String ON_MESSAGE_ROUTE_EXCEPTION = "onMessageRouteException"; public static final String BEFORE_DELIVER = "beforeDeliver"; public static final String AFTER_DELIVER = "afterDeliver"; public static final String BEFORE_DEPLOY_BRIDGE = "beforeDeployBridge"; @@ -304,6 +306,14 @@ public void afterSend(ServerSession session, Transaction tx, Message message, bo methodCalled(AFTER_SEND); } + @Override + public void onSendException(ServerSession session, Transaction tx, Message message, boolean direct, + boolean noAutoCreateQueue, Exception e) { + Preconditions.checkNotNull(message); + Preconditions.checkNotNull(e); + methodCalled(ON_SEND_EXCEPTION); + } + @Override public void beforeMessageRoute(Message message, RoutingContext context, boolean direct, boolean rejectDuplicates) { Preconditions.checkNotNull(message); @@ -320,6 +330,15 @@ public void afterMessageRoute(Message message, RoutingContext context, boolean d methodCalled(AFTER_MESSAGE_ROUTE); } + @Override + public void onMessageRouteException(Message message, RoutingContext context, boolean direct, boolean rejectDuplicates, + Exception e) { + Preconditions.checkNotNull(message); + Preconditions.checkNotNull(context); + Preconditions.checkNotNull(e); + methodCalled(ON_MESSAGE_ROUTE_EXCEPTION); + } + @Override public void beforeDeliver(ServerConsumer consumer, MessageReference reference) { Preconditions.checkNotNull(reference); From 2c7896cef6e08494b89848de7b76ea948defd691 Mon Sep 17 00:00:00 2001 From: Timothy Bish Date: Fri, 24 Aug 2018 17:18:29 -0400 Subject: [PATCH 166/207] ARTEMIS-1938 Update Qpid JMS along with Proton and Netty Update the Qpid JMS and Proton dependencies to lastest and sync Netty with the 4.1.28.Final version used by Qpid JMS to avoid clash that breaks a test. Adds override of new Proton-J WritableBuffer API that allows it to use the Netty String encoder when needed instead of the slower default version. Update Qpid JMS to v0.36.0 Proton-J to v0.29.0 Netty to 4.1.28.Final --- .../activemq/artemis/protocol/amqp/util/NettyWritable.java | 6 ++++++ pom.xml | 7 ++++--- tests/unit-tests/pom.xml | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/util/NettyWritable.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/util/NettyWritable.java index 659f35f7054..bf46e8143ad 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/util/NettyWritable.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/util/NettyWritable.java @@ -17,6 +17,7 @@ package org.apache.activemq.artemis.protocol.amqp.util; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import org.apache.qpid.proton.codec.ReadableBuffer; import org.apache.qpid.proton.codec.WritableBuffer; @@ -103,6 +104,11 @@ public void put(ByteBuf payload) { nettyBuffer.writeBytes(payload); } + @Override + public void put(String value) { + nettyBuffer.writeCharSequence(value, StandardCharsets.UTF_8); + } + @Override public int limit() { return nettyBuffer.capacity(); diff --git a/pom.xml b/pom.xml index fb95a5efc3e..beb9dcd57f2 100644 --- a/pom.xml +++ b/pom.xml @@ -91,11 +91,12 @@ 3.6.13.Final 2.4 2.8.47 - 4.1.24.Final - 0.27.3 + 4.1.28.Final + 2.0.12.Final + 0.29.0 3.0.19.Final 1.7.21 - 0.33.0 + 0.36.0 0.9.5 1.0-alpha-1 1 diff --git a/tests/unit-tests/pom.xml b/tests/unit-tests/pom.xml index 3f00b6ef107..58d1678b1f1 100644 --- a/tests/unit-tests/pom.xml +++ b/tests/unit-tests/pom.xml @@ -163,7 +163,7 @@ io.netty netty-tcnative-boringssl-static - 2.0.7.Final + ${netty-tcnative-version} test From 6b1abd1aadc2d097e3baefeb312c8e68092876ba Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Sun, 26 Aug 2018 15:55:56 -0400 Subject: [PATCH 167/207] ARTEMIS-2053 avoiding data loss after compacting --- .../impl/AbstractJournalUpdateTask.java | 2 +- .../journal/impl/JournalFilesRepository.java | 14 ++- .../core/journal/impl/JournalImpl.java | 36 +++++-- .../core/journal/impl/JournalTransaction.java | 4 +- .../AbstractJournalStorageManager.java | 7 +- .../core/replication/ReplicatedJournal.java | 8 +- .../core/replication/ReplicationManager.java | 11 ++- .../core/server/impl/ActiveMQServerImpl.java | 2 + .../transaction/impl/TransactionImpl.java | 2 +- .../impl/JournalFileRepositoryOrderTest.java | 93 +++++++++++++++++++ 10 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/journal/impl/JournalFileRepositoryOrderTest.java diff --git a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/AbstractJournalUpdateTask.java b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/AbstractJournalUpdateTask.java index d8b5f93b490..7740beff48a 100644 --- a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/AbstractJournalUpdateTask.java +++ b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/AbstractJournalUpdateTask.java @@ -241,7 +241,7 @@ protected void openFile() throws Exception { writingChannel = ActiveMQBuffers.wrappedBuffer(bufferWrite); - currentFile = filesRepository.takeFile(false, false, false, true); + currentFile = filesRepository.openFileCMP(); sequentialFile = currentFile.getFile(); diff --git a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalFilesRepository.java b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalFilesRepository.java index 5896fba68e6..eb4740f41c9 100644 --- a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalFilesRepository.java +++ b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalFilesRepository.java @@ -409,6 +409,16 @@ public int getOpenedFilesCount() { return openedFiles.size(); } + public JournalFile openFileCMP() throws Exception { + JournalFile file = openFile(); + + SequentialFile sequentialFile = file.getFile(); + sequentialFile.close(); + sequentialFile.renameTo(sequentialFile.getFileName() + ".cmp"); + + return file; + } + /** *

This method will instantly return the opened file, and schedule opening and reclaiming.

*

In case there are no cached opened files, this method will block until the file was opened, @@ -468,7 +478,7 @@ private void pushOpen() { /** * Open a file and place it into the openedFiles queue */ - public void pushOpenedFile() throws Exception { + public synchronized void pushOpenedFile() throws Exception { JournalFile nextOpenedFile = takeFile(true, true, true, false); if (logger.isTraceEnabled()) { @@ -505,7 +515,7 @@ public void closeFile(final JournalFile file) throws Exception { * @throws Exception * @see JournalImpl#initFileHeader(SequentialFileFactory, SequentialFile, int, long) */ - public JournalFile takeFile(final boolean keepOpened, + private JournalFile takeFile(final boolean keepOpened, final boolean multiAIO, final boolean initFile, final boolean tmpCompactExtension) throws Exception { diff --git a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java index 30ed6e33e2b..47bdc5b7c41 100644 --- a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java +++ b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalImpl.java @@ -348,7 +348,12 @@ public JournalImpl(final ExecutorFactory ioExecutors, @Override public String toString() { - return "JournalImpl(state=" + state + ", currentFile=[" + currentFile + "], hash=" + super.toString() + ")"; + try { + return "JournalImpl(state=" + state + ", directory=[" + this.fileFactory.getDirectory().toString() + "], hash=" + super.toString() + ")"; + } catch (Throwable e) { + logger.warn(e); + return super.toString(); + } } @Override @@ -1278,6 +1283,9 @@ public void run() { JournalInternalRecord commitRecord = new JournalCompleteRecordTX(TX_RECORD_TYPE.COMMIT, txID, null); JournalFile usedFile = appendRecord(commitRecord, true, sync, tx, callback); + if (logger.isTraceEnabled()) { + logger.trace("appendCommitRecord::txID=" + txID + ", usedFile = " + usedFile); + } tx.commit(usedFile); } catch (ActiveMQShutdownException e) { @@ -1417,7 +1425,7 @@ public synchronized JournalLoadInformation load(final List committed private void checkDeleteSize() { // HORNETQ-482 - Flush deletes only if memory is critical if (recordsToDelete.size() > DELETE_FLUSH && runtime.freeMemory() < runtime.maxMemory() * 0.2) { - ActiveMQJournalLogger.LOGGER.debug("Flushing deletes during loading, deleteCount = " + recordsToDelete.size()); + logger.debug("Flushing deletes during loading, deleteCount = " + recordsToDelete.size()); // Clean up when the list is too large, or it won't be possible to load large sets of files // Done as part of JBMESSAGING-1678 Iterator iter = records.iterator(); @@ -1431,7 +1439,7 @@ private void checkDeleteSize() { recordsToDelete.clear(); - ActiveMQJournalLogger.LOGGER.debug("flush delete done"); + logger.debug("flush delete done"); } } @@ -1529,8 +1537,8 @@ public synchronized void compact() throws Exception { throw new IllegalStateException("There is pending compacting operation"); } - if (ActiveMQJournalLogger.LOGGER.isDebugEnabled()) { - ActiveMQJournalLogger.LOGGER.debug("JournalImpl::compact compacting journal " + (++compactCount)); + if (logger.isDebugEnabled()) { + logger.debug("JournalImpl::compact " + JournalImpl.this + " for its " + (++compactCount) + " time"); } compactorLock.writeLock().lock(); @@ -1540,7 +1548,9 @@ public synchronized void compact() throws Exception { boolean previousReclaimValue = isAutoReclaim(); try { - ActiveMQJournalLogger.LOGGER.debug("Starting compacting operation on journal"); + if (logger.isDebugEnabled()) { + logger.debug("Starting compacting operation on journal " + this); + } onCompactStart(); @@ -1669,9 +1679,14 @@ public synchronized void compact() throws Exception { renameFiles(dataFilesToProcess, newDatafiles); deleteControlFile(controlFile); - ActiveMQJournalLogger.LOGGER.debug("Finished compacting on journal"); + if (logger.isDebugEnabled()) { + logger.debug("Finished compacting on journal " + this); + } } finally { + if (logger.isDebugEnabled()) { + logger.debug("Flushing compacting on journal " + this); + } // An Exception was probably thrown, and the compactor was not cleared if (compactor != null) { try { @@ -1681,12 +1696,15 @@ public synchronized void compact() throws Exception { compactor = null; } + if (logger.isDebugEnabled()) { + logger.debug("since compact finished, setAutoReclaim back into " + previousReclaimValue); + } setAutoReclaim(previousReclaimValue); } } finally { compactorLock.writeLock().unlock(); - if (ActiveMQJournalLogger.LOGGER.isDebugEnabled()) { - ActiveMQJournalLogger.LOGGER.debug("JournalImpl::compact finishing"); + if (logger.isDebugEnabled()) { + logger.debug("JournalImpl::compact finalized"); } diff --git a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalTransaction.java b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalTransaction.java index 8f05d8d5a5f..ffc016a3a5a 100644 --- a/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalTransaction.java +++ b/artemis-journal/src/main/java/org/apache/activemq/artemis/core/journal/impl/JournalTransaction.java @@ -238,13 +238,13 @@ public void commit(final JournalFile file) { // without setting this properly... if (compacting && compactor != null) { if (logger.isTraceEnabled()) { - logger.trace("adding tx " + this.id + " into compacting"); + logger.trace("adding txID=" + this.id + " into compacting"); } compactor.addCommandCommit(this, file); } else { if (logger.isTraceEnabled()) { - logger.trace("no compact commit " + this.id); + logger.trace("there was no compactor on commit txID=" + this.id); } if (pos != null) { for (JournalUpdate trUpdate : pos) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java index d28eec87fd2..8c3cc77f3f4 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java @@ -669,6 +669,9 @@ public void commit(final long txID, final boolean lineUpContext) throws Exceptio try { messageJournal.appendCommitRecord(txID, syncTransactional, getContext(syncTransactional), lineUpContext); if (!lineUpContext && !syncTransactional) { + if (logger.isTraceEnabled()) { + logger.trace("calling getContext(true).done() for txID=" + txID + ",lineupContext=" + lineUpContext + " syncTransactional=" + syncTransactional + "... forcing call on getContext(true).done"); + } /** * If {@code lineUpContext == false}, it means that we have previously lined up a * context somewhere else (specifically see @{link TransactionImpl#asyncAppendCommit}), @@ -1742,7 +1745,9 @@ private void loadPreparedTransactions(final PostOffice postOffice, if (record.isUpdate) { PageTransactionInfo pgTX = pagingManager.getTransaction(pageTransactionInfo.getTransactionID()); - pgTX.reloadUpdate(this, pagingManager, tx, pageTransactionInfo.getNumberOfMessages()); + if (pgTX != null) { + pgTX.reloadUpdate(this, pagingManager, tx, pageTransactionInfo.getNumberOfMessages()); + } } else { pageTransactionInfo.setCommitted(false); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicatedJournal.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicatedJournal.java index 7ad06f5c46a..8a04cc18aaf 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicatedJournal.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicatedJournal.java @@ -149,7 +149,7 @@ public void appendAddRecordTransactional(final long txID, final Persister persister, final Object record) throws Exception { if (log.isTraceEnabled()) { - log.trace("Append record TXid = " + id + " recordType = " + recordType); + log.trace("Append record txID=" + id + " recordType = " + recordType); } replicationManager.appendAddRecordTransactional(journalID, ADD_OPERATION_TYPE.ADD, txID, id, recordType, persister, record); localJournal.appendAddRecordTransactional(txID, id, recordType, persister, record); @@ -164,7 +164,7 @@ public void appendAddRecordTransactional(final long txID, @Override public void appendCommitRecord(final long txID, final boolean sync) throws Exception { if (log.isTraceEnabled()) { - log.trace("AppendCommit " + txID); + log.trace("AppendCommit txID=" + txID); } replicationManager.appendCommitRecord(journalID, txID, sync, true); localJournal.appendCommitRecord(txID, sync); @@ -516,8 +516,8 @@ public void synchronizationUnlock() { } @Override - public void forceMoveNextFile() { - throw new UnsupportedOperationException(); + public void forceMoveNextFile() throws Exception { + localJournal.forceMoveNextFile(); } @Override diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java index 69737065052..b3077892281 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/replication/ReplicationManager.java @@ -27,7 +27,6 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import io.netty.buffer.ByteBuf; import io.netty.buffer.PooledByteBufAllocator; @@ -116,8 +115,6 @@ public static ADD_OPERATION_TYPE toOperation(boolean isUpdate) { private volatile boolean enabled; - private final AtomicBoolean writable = new AtomicBoolean(true); - private final Queue pendingTokens = new ConcurrentLinkedQueue<>(); private final ExecutorFactory ioExecutorFactory; @@ -291,6 +288,12 @@ public void stop(boolean clearTokens) throws Exception { logger.trace("Stopping being ignored as it hasn't been started"); return; } + + started = false; + } + + if (logger.isTraceEnabled()) { + logger.trace("stop(clearTokens=" + clearTokens + ")", new Exception("Trace")); } // This is to avoid the write holding a lock while we are trying to close it @@ -300,7 +303,6 @@ public void stop(boolean clearTokens) throws Exception { } enabled = false; - writable.set(true); if (clearTokens) { clearReplicationTokens(); @@ -312,7 +314,6 @@ public void stop(boolean clearTokens) throws Exception { toStop.destroy(); } remotingConnection = null; - started = false; } /** 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 a8e64477a74..4acc77bab06 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 @@ -976,6 +976,8 @@ void stop(boolean failoverOnServerShutdown, final boolean criticalIOError, boole */ void stop(boolean failoverOnServerShutdown, final boolean criticalIOError, boolean restarting, boolean isShutdown) { + logger.debug("Stopping server"); + synchronized (this) { if (state == SERVER_STATE.STOPPED || state == SERVER_STATE.STOPPING) { return; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/transaction/impl/TransactionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/transaction/impl/TransactionImpl.java index e3a09047405..e925c3b70a0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/transaction/impl/TransactionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/transaction/impl/TransactionImpl.java @@ -616,7 +616,7 @@ private synchronized void afterPrepare() { public String toString() { Date dt = new Date(this.createTime); return "TransactionImpl [xid=" + xid + - ", id=" + + ", txID=" + id + ", xid=" + xid + ", state=" + diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/journal/impl/JournalFileRepositoryOrderTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/journal/impl/JournalFileRepositoryOrderTest.java new file mode 100644 index 00000000000..e223f0906f4 --- /dev/null +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/journal/impl/JournalFileRepositoryOrderTest.java @@ -0,0 +1,93 @@ +/* + * 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.artemis.tests.unit.core.journal.impl; + +import java.util.LinkedList; +import java.util.concurrent.BlockingDeque; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.activemq.artemis.core.journal.impl.JournalFile; +import org.apache.activemq.artemis.core.journal.impl.JournalFilesRepository; +import org.apache.activemq.artemis.core.journal.impl.JournalImpl; +import org.apache.activemq.artemis.junit.Wait; +import org.apache.activemq.artemis.tests.unit.core.journal.impl.fakes.FakeSequentialFileFactory; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.utils.ActiveMQThreadFactory; +import org.apache.activemq.artemis.utils.actors.OrderedExecutorFactory; +import org.junit.Assert; +import org.junit.Test; + +public class JournalFileRepositoryOrderTest extends ActiveMQTestBase { + + @Test + public void testOrder() throws Throwable { + ExecutorService executorService = Executors.newFixedThreadPool(3, new ActiveMQThreadFactory("test", false, JournalFileRepositoryOrderTest.class.getClassLoader())); + final AtomicBoolean running = new AtomicBoolean(true); + Thread t = null; + try { + FakeSequentialFileFactory fakeSequentialFileFactory = new FakeSequentialFileFactory(); + JournalImpl journal = new JournalImpl(new OrderedExecutorFactory(executorService), 10 * 1024, 2, -1, -1, 0, fakeSequentialFileFactory, "file", "file", 1, 0); + + final JournalFilesRepository repository = journal.getFilesRepository(); + final BlockingDeque dataFiles = new LinkedBlockingDeque<>(); + + + // this is simulating how compating would return files into the journal + t = new Thread() { + @Override + public void run() { + while (running.get()) { + try { + Wait.waitFor(() -> !running.get() || dataFiles.size() > 10, 1000, 1); + while (running.get()) { + JournalFile file = dataFiles.poll(); + if (file == null) break; + repository.addFreeFile(file, false); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + }; + t.start(); + JournalFile file = null; + LinkedList values = new LinkedList<>(); + for (int i = 0; i < 5000; i++) { + file = repository.openFile(); + Assert.assertNotNull(file); + values.add(file.getRecordID()); + dataFiles.push(file); + } + + int previous = Integer.MIN_VALUE; + for (Integer v : values) { + Assert.assertTrue(v.intValue() > previous); + previous = v; + } + + } finally { + running.set(false); + executorService.shutdownNow(); + } + + } +} From 87f393e5971d1e7bb39e4cbf7f6aba02f291045a Mon Sep 17 00:00:00 2001 From: "Christopher L. Shannon (cshannon)" Date: Fri, 24 Aug 2018 11:02:22 -0400 Subject: [PATCH 168/207] ARTEMIS-2052 - Fix defaultConsumerWindowSize negotiation First, QueueQuery should use address name for address settings The name used for looking up address settings for a queue now uses the address name if there is a local queue binding Second, make sure sent credits to the server is the correct value --- .../core/client/impl/ClientSessionImpl.java | 4 +-- .../core/server/impl/ActiveMQServerImpl.java | 13 ++++---- .../client/ConsumerWindowSizeTest.java | 30 +++++++++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java index 711d7cec4f2..f1ef526f586 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java @@ -1887,8 +1887,8 @@ private ClientConsumer internalCreateConsumer(final SimpleString queueName, // consumer // TODO: this could semantically change on other servers. I know for instance on stomp this is just an ignore - if (windowSize != 0) { - sessionContext.sendConsumerCredits(consumer, windowSize); + if (consumer.getClientWindowSize() != 0) { + sessionContext.sendConsumerCredits(consumer, consumer.getClientWindowSize()); } return consumer; 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 4acc77bab06..054006b9f11 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 @@ -876,7 +876,14 @@ public QueueQueryResult queueQuery(SimpleString name) { throw ActiveMQMessageBundle.BUNDLE.queueNameIsNull(); } - final AddressSettings addressSettings = getAddressSettingsRepository().getMatch(name.toString()); + QueueQueryResult response; + + Binding binding = getPostOffice().getBinding(name); + + final SimpleString addressName = binding != null && binding.getType() == BindingType.LOCAL_QUEUE + ? binding.getAddress() : name; + + final AddressSettings addressSettings = getAddressSettingsRepository().getMatch(addressName.toString()); boolean autoCreateQueues = addressSettings.isAutoCreateQueues(); boolean defaultPurgeOnNoConsumers = addressSettings.isDefaultPurgeOnNoConsumers(); @@ -885,10 +892,6 @@ public QueueQueryResult queueQuery(SimpleString name) { boolean defaultLastValueQueue = addressSettings.isDefaultLastValueQueue(); int defaultConsumerWindowSize = addressSettings.getDefaultConsumerWindowSize(); - QueueQueryResult response; - - Binding binding = getPostOffice().getBinding(name); - SimpleString managementAddress = getManagementService() != null ? getManagementService().getManagementAddress() : null; if (binding != null && binding.getType() == BindingType.LOCAL_QUEUE) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java index d4298e5087d..e58f4bcaa13 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java @@ -36,15 +36,18 @@ import org.apache.activemq.artemis.api.core.client.ServerLocator; import org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl; import org.apache.activemq.artemis.core.client.impl.ClientConsumerInternal; +import org.apache.activemq.artemis.core.client.impl.ClientSessionImpl; import org.apache.activemq.artemis.core.postoffice.Binding; import org.apache.activemq.artemis.core.postoffice.Bindings; import org.apache.activemq.artemis.core.postoffice.QueueBinding; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.Consumer; +import org.apache.activemq.artemis.core.server.ServerSession; import org.apache.activemq.artemis.core.server.impl.ServerConsumerImpl; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.tests.util.Wait; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; @@ -1427,6 +1430,33 @@ public void testConsumerWindowSizeAddressSettings() throws Exception { assertEquals(defaultConsumerWindowSize / 2, consumer.getClientWindowSize()); } + @Test + public void testConsumerWindowSizeAddressSettingsDifferentAddressAndQueueName() throws Exception { + ActiveMQServer messagingService = createServer(false, isNetty()); + + final int defaultConsumerWindowSize = 1024 * 5; + final AddressSettings settings = new AddressSettings(); + settings.setDefaultConsumerWindowSize(defaultConsumerWindowSize); + messagingService.getConfiguration() + .getAddressesSettings().put(addressA.toString(), settings); + + messagingService.start(); + messagingService.createQueue(addressA, RoutingType.ANYCAST, queueA, null, true, false); + + ClientSessionFactory cf = createSessionFactory(locator); + ClientSession session = cf.createSession(false, true, true); + ClientConsumerImpl consumer = (ClientConsumerImpl) session.createConsumer(queueA); + + session.start(); + + assertEquals(defaultConsumerWindowSize / 2, consumer.getClientWindowSize()); + + ServerSession ss = messagingService.getSessionByID(((ClientSessionImpl)session).getName()); + ServerConsumerImpl cons = (ServerConsumerImpl) ss.locateConsumer(consumer.getConsumerContext().getId()); + + assertTrue(Wait.waitFor(() -> cons.getAvailableCredits().get() == consumer.getClientWindowSize(), 5000, 500)); + } + @Test public void testConsumerWindowSizeAddressSettingsWildCard() throws Exception { ActiveMQServer messagingService = createServer(false, isNetty()); From 490ef71e1dccc88e51d862cc51af468d37d416ce Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Mon, 27 Aug 2018 09:18:02 +0100 Subject: [PATCH 169/207] ARTEMIS-2055 Set Live LM to Null after route The ServerSessionPacketHandler has a close() callback handler which will delete any pending large messages. However, there is a race where a large message can be routed, then the close delete the associated large message resulting in data loss. --- .../core/ServerSessionPacketHandler.java | 4 +- .../byteman/LargeMessageOnShutdownTest.java | 137 ++++++++++++++++++ 2 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java index 36273f8c2b7..3b0433ef664 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java @@ -974,9 +974,9 @@ private void sendContinuations(final int packetSize, currentLargeMessage.putLongProperty(Message.HDR_LARGE_BODY_SIZE, messageBodySize); } - session.doSend(session.getCurrentTransaction(), currentLargeMessage, null, false, false); - + LargeServerMessage message = currentLargeMessage; currentLargeMessage = null; + session.doSend(session.getCurrentTransaction(), message, null, false, false); } } diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java new file mode 100644 index 00000000000..ebbd7dee037 --- /dev/null +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java @@ -0,0 +1,137 @@ +/* + * 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.artemis.tests.extras.byteman; + +import java.io.ByteArrayInputStream; +import java.util.concurrent.TimeUnit; + +import org.apache.activemq.artemis.api.core.ActiveMQBuffers; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; +import org.apache.activemq.artemis.api.core.client.ClientConsumer; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientProducer; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.apache.activemq.artemis.tests.util.Wait; +import org.jboss.byteman.contrib.bmunit.BMRule; +import org.jboss.byteman.contrib.bmunit.BMRules; +import org.jboss.byteman.contrib.bmunit.BMUnitRunner; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(BMUnitRunner.class) +public class LargeMessageOnShutdownTest extends ActiveMQTestBase { + + private static final SimpleString queueName = new SimpleString("largeMessageShutdownQueue"); + private static ActiveMQServer server; + + @Before + public void setUp() throws Exception { + super.setUp(); + + server = createServer(true, createDefaultNettyConfig()); + startServer(); + server.createQueue(queueName, RoutingType.MULTICAST, queueName, null, true, false); + } + + @After + public void tearDown() throws Exception { + super.tearDown(); + stopServer(); + } + + @Test + @BMRules( + rules = { + @BMRule( + name = "BlockOnFinalLargeMessagePacket", + targetClass = "org.apache.activemq.artemis.core.server.impl.ServerSessionImpl", + targetMethod = "doSend(Transaction,Message,SimpleString,boolean,boolean)", + targetLocation = "EXIT", + condition = "!flagged(\"testLargeMessageOnShutdown\")", + action = + "org.apache.activemq.artemis.tests.extras.byteman.LargeMessageOnShutdownTest.stopServer();" + + "waitFor(\"testLargeMessageOnShutdown\");" + + "flag(\"testLargeMessageOnShutdown\")" + ), + @BMRule( + name = "ReleaseBlockOnSessionCleanup", + targetClass = "org.apache.activemq.artemis.core.protocol.core.ServerSessionPacketHandler", + targetMethod = "clearLargeMessage()", + targetLocation = "EXIT", + action = "signalWake(\"testLargeMessageOnShutdown\")" + ) + } + ) + public void testLargeMessageOnShutdown() throws Exception { + + byte[] payload = new byte[ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE * 2]; + + // Send Large Message + ClientSessionFactory csf1 = createSessionFactory(createNettyNonHALocator()); + try { + ClientSession session1 = csf1.createSession(); + ClientProducer producer1 = session1.createProducer(queueName); + ClientMessage message = session1.createMessage(true); + + message.setBodyInputStream(new ByteArrayInputStream(payload)); + producer1.send(message); + } catch (Exception e) { + // Expected due to shutdown. + } + finally { + csf1.close(); + } + + waitForStoppedServer(); + startServer(); + + // Consume Large Message + ClientSessionFactory csf2 = createSessionFactory(createNettyNonHALocator()); + try { + ClientSession session2 = csf2.createSession(); + session2.start(); + ClientConsumer consumer2 = session2.createConsumer(queueName); + ClientMessage rmessage = consumer2.receive(10000); + + assertEquals(payload.length, rmessage.getBodyBuffer().readableBytes()); + assertEqualsBuffers(payload.length, ActiveMQBuffers.wrappedBuffer(payload), rmessage.getBodyBuffer()); + } finally { + csf2.close(); + } + } + + public static void stopServer() throws Exception { + server.stop(); + waitForStoppedServer(); + } + + public static void startServer() throws Exception { + server.start(); + server.waitForActivation(30, TimeUnit.SECONDS); + } + + public static void waitForStoppedServer() throws Exception { + Wait.waitFor(() -> server.getState() == ActiveMQServer.SERVER_STATE.STOPPED); + } +} From b36a1058d4584adca399a591c4109866d4265e07 Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Fri, 24 Aug 2018 14:24:25 +0100 Subject: [PATCH 170/207] ARTEMIS-2056 Set write position on JDBCFile copy --- .../artemis/jdbc/store/file/JDBCSequentialFile.java | 6 ++++++ .../artemis/jdbc/file/JDBCSequentialFileFactoryTest.java | 3 +++ 2 files changed, 9 insertions(+) diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFile.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFile.java index 843be545d8b..fec8eaf7bff 100644 --- a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFile.java +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/file/JDBCSequentialFile.java @@ -330,6 +330,7 @@ public void renameTo(String newFileName) throws Exception { public SequentialFile cloneFile() { try { JDBCSequentialFile clone = new JDBCSequentialFile(fileFactory, filename, executor, dbDriver, writeLock); + clone.setWritePosition(this.writePosition); return clone; } catch (Exception e) { fileFactory.onIOError(e, "Error cloning JDBC file.", this); @@ -342,8 +343,13 @@ public void copyTo(SequentialFile cloneFile) throws Exception { JDBCSequentialFile clone = (JDBCSequentialFile) cloneFile; try { synchronized (writeLock) { + if (logger.isTraceEnabled()) { + logger.trace("JDBC Copying File. From: " + this + " To: " + cloneFile); + } + clone.open(); dbDriver.copyFileData(this, clone); + clone.setWritePosition(writePosition); } } catch (Exception e) { fileFactory.onIOError(e, "Error copying JDBC file.", this); diff --git a/artemis-jdbc-store/src/test/java/org/apache/activemq/artemis/jdbc/file/JDBCSequentialFileFactoryTest.java b/artemis-jdbc-store/src/test/java/org/apache/activemq/artemis/jdbc/file/JDBCSequentialFileFactoryTest.java index d567f84eb94..a45b9a81d39 100644 --- a/artemis-jdbc-store/src/test/java/org/apache/activemq/artemis/jdbc/file/JDBCSequentialFileFactoryTest.java +++ b/artemis-jdbc-store/src/test/java/org/apache/activemq/artemis/jdbc/file/JDBCSequentialFileFactoryTest.java @@ -221,6 +221,9 @@ public void testCopyFile() throws Exception { checkData(file, src); checkData(copy, src); + + assertEquals(bufferSize, copy.size()); + assertEquals(bufferSize, file.size()); } @Test From a8dad35031964ee0a0a6b80801f2ad3844011b90 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 28 Aug 2018 10:35:36 -0400 Subject: [PATCH 171/207] ARTEMIS-2055 fixing checkstyle --- .../tests/extras/byteman/LargeMessageOnShutdownTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java index ebbd7dee037..e9be73b8cb0 100644 --- a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java @@ -46,6 +46,7 @@ public class LargeMessageOnShutdownTest extends ActiveMQTestBase { private static ActiveMQServer server; @Before + @Override public void setUp() throws Exception { super.setUp(); @@ -55,6 +56,7 @@ public void setUp() throws Exception { } @After + @Override public void tearDown() throws Exception { super.tearDown(); stopServer(); @@ -98,8 +100,7 @@ public void testLargeMessageOnShutdown() throws Exception { producer1.send(message); } catch (Exception e) { // Expected due to shutdown. - } - finally { + } finally { csf1.close(); } From 41b094df2bf564631c53f5dcbfeb4c2b1e815747 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Wed, 22 Aug 2018 22:10:37 -0500 Subject: [PATCH 172/207] ARTEMIS-2051 add trace logging for JDBC Activate by enabling TRACE logging for: org.apache.activemq.artemis.jdbc.store.drivers.AbstractJDBCDriver This doesn't log *all* JDBC operations, just those that are used by the JDBC store. --- .../store/drivers/AbstractJDBCDriver.java | 23 +- .../jdbc/store/logging/LoggingConnection.java | 436 ++++++ .../logging/LoggingPreparedStatement.java | 406 +++++ .../jdbc/store/logging/LoggingResultSet.java | 1338 +++++++++++++++++ .../jdbc/store/logging/LoggingStatement.java | 400 +++++ .../jdbc/store/logging/LoggingUtil.java | 24 + 6 files changed, 2625 insertions(+), 2 deletions(-) create mode 100644 artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingConnection.java create mode 100644 artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingPreparedStatement.java create mode 100644 artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingResultSet.java create mode 100644 artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingStatement.java create mode 100644 artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingUtil.java diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/AbstractJDBCDriver.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/AbstractJDBCDriver.java index 262cf2e1a0d..ceb096779d3 100644 --- a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/AbstractJDBCDriver.java +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/AbstractJDBCDriver.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.activemq.artemis.jdbc.store.logging.LoggingConnection; import org.apache.activemq.artemis.jdbc.store.sql.SQLProvider; import org.apache.activemq.artemis.journal.ActiveMQJournalLogger; import org.jboss.logging.Logger; @@ -85,7 +86,11 @@ public void start() throws SQLException { } public AbstractJDBCDriver(Connection connection, SQLProvider sqlProvider) { - this.connection = connection; + if (logger.isTraceEnabled() && !(connection instanceof LoggingConnection)) { + this.connection = new LoggingConnection(connection, logger); + } else { + this.connection = connection; + } this.sqlProvider = sqlProvider; this.networkTimeoutExecutor = null; this.networkTimeoutMillis = -1; @@ -118,6 +123,11 @@ private void connect() throws SQLException { if (dataSource != null) { try { connection = dataSource.getConnection(); + + if (logger.isTraceEnabled() && !(connection instanceof LoggingConnection)) { + this.connection = new LoggingConnection(connection, logger); + } + } catch (SQLException e) { logger.error(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), e)); throw e; @@ -132,6 +142,11 @@ private void connect() throws SQLException { } final Driver dbDriver = getDriver(jdbcDriverClass); connection = dbDriver.connect(jdbcConnectionUrl, new Properties()); + + if (logger.isTraceEnabled() && !(connection instanceof LoggingConnection)) { + this.connection = new LoggingConnection(connection, logger); + } + if (connection == null) { throw new IllegalStateException("the driver: " + jdbcDriverClass + " isn't able to connect to the requested url: " + jdbcConnectionUrl); } @@ -293,7 +308,11 @@ public Connection getConnection() { public final void setConnection(Connection connection) { if (this.connection == null) { - this.connection = connection; + if (logger.isTraceEnabled() && !(connection instanceof LoggingConnection)) { + this.connection = new LoggingConnection(connection, logger); + } else { + this.connection = connection; + } } } diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingConnection.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingConnection.java new file mode 100644 index 00000000000..8a9284abebb --- /dev/null +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingConnection.java @@ -0,0 +1,436 @@ +/** + * 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.artemis.jdbc.store.logging; + +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.Arrays; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +import org.jboss.logging.Logger; + +public class LoggingConnection implements Connection { + + private final Connection connection; + + private final String connectionID; + + private Logger logger; + + private Logger.Level level = Logger.Level.TRACE; + + public LoggingConnection(Connection connection, Logger logger) { + this.connection = connection; + this.logger = logger; + this.connectionID = LoggingUtil.getID(connection); + } + + public Connection getConnection() { + return connection; + } + + public String getConnectionID() { + return connectionID; + } + + @Override + public Statement createStatement() throws SQLException { + LoggingStatement statement = new LoggingStatement(connection.createStatement(), logger); + logger.logf(level, "%s.createStatement() = %s", connectionID, statement.getStatementID()); + return statement; + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + LoggingPreparedStatement statement = new LoggingPreparedStatement(connection.prepareStatement(sql), logger); + logger.logf(level, "%s.prepareStatement(%s) = %s", connectionID, sql, statement.getStatementID()); + return statement; + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + CallableStatement statement = connection.prepareCall(sql); + logger.logf(level, "%s.prepareCall(%s) = %s", connectionID, sql, LoggingUtil.getID(statement)); + return statement; + } + + @Override + public String nativeSQL(String sql) throws SQLException { + String x = connection.nativeSQL(sql); + logger.logf(level, "%s.nativeSQL(%s) = %s", connectionID, sql, x); + return x; + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + logger.logf(level, "%s.setAutoCommit(%s)", connectionID, autoCommit); + connection.setAutoCommit(autoCommit); + } + + @Override + public boolean getAutoCommit() throws SQLException { + boolean x = connection.getAutoCommit(); + logger.logf(level, "%s.getAutoCommit() = %s", connectionID, x); + return x; + } + + @Override + public void commit() throws SQLException { + logger.logf(level, "%s.commit()", connectionID); + connection.commit(); + } + + @Override + public void rollback() throws SQLException { + logger.logf(level, "%s.rollback()", connectionID); + connection.rollback(); + } + + @Override + public void close() throws SQLException { + logger.logf(level, "%s.close()", connectionID); + connection.close(); + } + + @Override + public boolean isClosed() throws SQLException { + boolean x = connection.isClosed(); + logger.logf(level, "%s.isClosed() = %s", connectionID, x); + return x; + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + DatabaseMetaData x = connection.getMetaData(); + logger.logf(level, "%s.getMetaData() = %s", connectionID, x); + return x; + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + logger.logf(level, "%s.setReadOnly(%s)", connectionID, readOnly); + connection.setReadOnly(readOnly); + } + + @Override + public boolean isReadOnly() throws SQLException { + boolean x = connection.isReadOnly(); + logger.logf(level, "%s.isReadOnly() = %s", connectionID, x); + return x; + } + + @Override + public void setCatalog(String catalog) throws SQLException { + logger.logf(level, "%s.setCatalog(%s)", connectionID, catalog); + connection.setCatalog(catalog); + } + + @Override + public String getCatalog() throws SQLException { + String x = connection.getCatalog(); + logger.logf(level, "%s.getCatalog() = %s", connectionID, x); + return x; + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + logger.logf(this.level, "%s.setTransactionIsolation(%s)", connectionID, level); + connection.setTransactionIsolation(level); + } + + @Override + public int getTransactionIsolation() throws SQLException { + int x = connection.getTransactionIsolation(); + logger.logf(level, "%s.getTransactionIsolation() = %s", connectionID, x); + return x; + } + + @Override + public SQLWarning getWarnings() throws SQLException { + SQLWarning x = connection.getWarnings(); + logger.logf(level, "%s.getWarnings() = %s", connectionID, x); + return x; + } + + @Override + public void clearWarnings() throws SQLException { + logger.logf(level, "%s.clearWarnings()", connectionID); + connection.clearWarnings(); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + LoggingStatement statement = new LoggingStatement(connection.createStatement(resultSetType, resultSetConcurrency), logger); + logger.logf(level, "%s.createStatement(%s, %s) = %s", connectionID, resultSetType, resultSetConcurrency, statement.getStatementID()); + return statement; + } + + @Override + public PreparedStatement prepareStatement(String sql, + int resultSetType, + int resultSetConcurrency) throws SQLException { + LoggingPreparedStatement statement = new LoggingPreparedStatement(connection.prepareStatement(sql, resultSetType, resultSetConcurrency), logger); + logger.logf(level, "%s.prepareStatement(%s, %s, %s) = %s", connectionID, sql, resultSetType, resultSetConcurrency, statement.getStatementID()); + return statement; + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + CallableStatement statement = connection.prepareCall(sql, resultSetType, resultSetConcurrency); + logger.logf(level, "%s.createStatement(%s, %s) = %s", connectionID, sql, resultSetType, resultSetConcurrency, LoggingUtil.getID(statement)); + return statement; + } + + @Override + public Map> getTypeMap() throws SQLException { + Map> x = connection.getTypeMap(); + logger.logf(level, "%s.getTypeMap() = %s", connectionID, x); + return x; + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + logger.logf(level, "%s.setTypeMap(%s)", connectionID, map); + connection.setTypeMap(map); + } + + @Override + public void setHoldability(int holdability) throws SQLException { + logger.logf(level, "%s.setHoldability(%s)", connectionID, holdability); + connection.setHoldability(holdability); + } + + @Override + public int getHoldability() throws SQLException { + int x = connection.getHoldability(); + logger.logf(level, "%s.getHoldability() = %s", connectionID, x); + return x; + } + + @Override + public Savepoint setSavepoint() throws SQLException { + Savepoint x = connection.setSavepoint(); + logger.logf(level, "%s.setSavepoint() = %s", connectionID, x); + return x; + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + Savepoint x = connection.setSavepoint(name); + logger.logf(level, "%s.setSavepoint(%s) = %s", connectionID, name, x); + return x; + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + logger.logf(level, "%s.rollback(%s)", connectionID, savepoint); + connection.rollback(savepoint); + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + logger.logf(level, "%s.releaseSavepoint(%s)", connectionID, savepoint); + connection.releaseSavepoint(savepoint); + } + + @Override + public Statement createStatement(int resultSetType, + int resultSetConcurrency, + int resultSetHoldability) throws SQLException { + LoggingStatement statement = new LoggingStatement(connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability), logger); + logger.logf(level, "%s.createStatement(%s, %s, %s) = %s", connectionID, resultSetType, resultSetConcurrency, resultSetHoldability, statement.getStatementID()); + return statement; + } + + @Override + public PreparedStatement prepareStatement(String sql, + int resultSetType, + int resultSetConcurrency, + int resultSetHoldability) throws SQLException { + LoggingPreparedStatement statement = new LoggingPreparedStatement(connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability), logger); + logger.logf(level, "%s.prepareStatement(%s, %s, %s, %s) = %s", connectionID, sql, resultSetType, resultSetConcurrency, resultSetHoldability, statement.getStatementID()); + return statement; + } + + @Override + public CallableStatement prepareCall(String sql, + int resultSetType, + int resultSetConcurrency, + int resultSetHoldability) throws SQLException { + CallableStatement statement = connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + logger.logf(level, "%s.prepareCall(%s, %s, %s, %s) = %s", connectionID, sql, resultSetType, resultSetConcurrency, resultSetHoldability, LoggingUtil.getID(statement)); + return statement; + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + LoggingPreparedStatement preparedStatement = new LoggingPreparedStatement(connection.prepareStatement(sql, autoGeneratedKeys), logger); + logger.logf(level, "%s.prepareStatement(%s, %s) = %s", connectionID, sql, autoGeneratedKeys, preparedStatement.getStatementID()); + return preparedStatement; + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + LoggingPreparedStatement statement = new LoggingPreparedStatement(connection.prepareStatement(sql, columnIndexes), logger); + logger.logf(level, "%s.prepareStatement(%s, %s) = %s", connectionID, sql, Arrays.toString(columnIndexes), statement.getStatementID()); + return statement; + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + LoggingPreparedStatement statement = new LoggingPreparedStatement(connection.prepareStatement(sql, columnNames), logger); + logger.logf(level, "%s.prepareStatement(%s, %s) = %s", connectionID, sql, Arrays.toString(columnNames), statement.getStatementID()); + return statement; + } + + @Override + public Clob createClob() throws SQLException { + Clob x = connection.createClob(); + logger.logf(level, "%s.createClob() = %s", connectionID, x); + return x; + } + + @Override + public Blob createBlob() throws SQLException { + Blob x = connection.createBlob(); + logger.logf(level, "%s.createBlob() = %s", connectionID, x); + return x; + } + + @Override + public NClob createNClob() throws SQLException { + NClob x = connection.createNClob(); + logger.logf(level, "%s.createNClob() = %s", connectionID, x); + return x; + } + + @Override + public SQLXML createSQLXML() throws SQLException { + SQLXML x = connection.createSQLXML(); + logger.logf(level, "%s.createSQLXML() = %s", connectionID, x); + return x; + } + + @Override + public boolean isValid(int timeout) throws SQLException { + boolean x = connection.isValid(timeout); + logger.logf(level, "%s.isValid(%s) = %s", connectionID, timeout, x); + return x; + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + logger.logf(level, "%s.setClientInfo(%s, %s)", connectionID, name, value); + connection.setClientInfo(name, value); + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + logger.logf(level, "%s.setClientInfo(%s)", connectionID, properties); + connection.setClientInfo(properties); + } + + @Override + public String getClientInfo(String name) throws SQLException { + String x = connection.getClientInfo(name); + logger.logf(level, "%s.getClientInfo(%s) = %s", connectionID, name, x); + return x; + } + + @Override + public Properties getClientInfo() throws SQLException { + Properties x = connection.getClientInfo(); + logger.logf(level, "%s.getClientInfo() = %s", connectionID, x); + return x; + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + Array x = connection.createArrayOf(typeName, elements); + logger.logf(level, "%s.createArrayOf(%s, %s) = %s", connectionID, typeName, Arrays.toString(elements), x); + return x; + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + Struct x = connection.createStruct(typeName, attributes); + logger.logf(level, "%s.createStruct(%s, %s) = %s", connectionID, typeName, Arrays.toString(attributes), x); + return x; + } + + @Override + public void setSchema(String schema) throws SQLException { + logger.logf(level, "%s.setSchema(%s)", connectionID, schema); + connection.setSchema(schema); + } + + @Override + public String getSchema() throws SQLException { + String x = connection.getSchema(); + logger.logf(level, "%s.getSchema() = %s", connectionID, x); + return x; + } + + @Override + public void abort(Executor executor) throws SQLException { + logger.logf(level, "%s.abort(%s)", connectionID, executor); + connection.abort(executor); + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + logger.logf(level, "%s.setNetworkTimeout(%s, %d)", connectionID, executor, milliseconds); + connection.setNetworkTimeout(executor, milliseconds); + } + + @Override + public int getNetworkTimeout() throws SQLException { + int x = connection.getNetworkTimeout(); + logger.logf(level, "%s.getNetworkTimeout() = %s", connectionID, x); + return x; + } + + @Override + public T unwrap(Class iface) throws SQLException { + T x = connection.unwrap(iface); + logger.logf(level, "%s.unwrap(%s) = %s", connectionID, iface, x); + return x; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + boolean x = connection.isWrapperFor(iface); + logger.logf(level, "%s.isWrapperFor() = %s", connectionID, iface, x); + return x; + } +} diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingPreparedStatement.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingPreparedStatement.java new file mode 100644 index 00000000000..4cf270d4bbb --- /dev/null +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingPreparedStatement.java @@ -0,0 +1,406 @@ +/** + * 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.artemis.jdbc.store.logging; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; + +import org.jboss.logging.Logger; + +public class LoggingPreparedStatement extends LoggingStatement implements PreparedStatement { + + private final PreparedStatement preparedStatement; + + public LoggingPreparedStatement(PreparedStatement preparedStatement, Logger logger) { + super(preparedStatement, logger); + this.preparedStatement = preparedStatement; + } + + @Override + public ResultSet executeQuery() throws SQLException { + LoggingResultSet rs = new LoggingResultSet(preparedStatement.executeQuery(), logger); + logger.logf(level, "%s.executeQuery() = %s", statementID, rs.getResultSetID()); + return rs; + } + + @Override + public int executeUpdate() throws SQLException { + int i = preparedStatement.executeUpdate(); + logger.logf(level, "%s.executeUpdate() = %s", statementID, i); + return i; + } + + @Override + public void setNull(int parameterIndex, int sqlType) throws SQLException { + logger.logf(level, "%s.setNull(%d, %d)", statementID, parameterIndex, sqlType); + preparedStatement.setNull(parameterIndex, sqlType); + } + + @Override + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + logger.logf(level, "%s.setBoolean(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setBoolean(parameterIndex, x); + } + + @Override + public void setByte(int parameterIndex, byte x) throws SQLException { + logger.logf(level, "%s.setByte(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setByte(parameterIndex, x); + } + + @Override + public void setShort(int parameterIndex, short x) throws SQLException { + logger.logf(level, "%s.setShort(%d, %d)", statementID, parameterIndex, x); + preparedStatement.setShort(parameterIndex, x); + } + + @Override + public void setInt(int parameterIndex, int x) throws SQLException { + logger.logf(level, "%s.setInt(%d, %d)", statementID, parameterIndex, x); + preparedStatement.setInt(parameterIndex, x); + } + + @Override + public void setLong(int parameterIndex, long x) throws SQLException { + logger.logf(level, "%s.setLong(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setLong(parameterIndex, x); + } + + @Override + public void setFloat(int parameterIndex, float x) throws SQLException { + logger.logf(level, "%s.setFloat(%d, %f)", statementID, parameterIndex, x); + preparedStatement.setFloat(parameterIndex, x); + } + + @Override + public void setDouble(int parameterIndex, double x) throws SQLException { + logger.logf(level, "%s.setDouble(%d, %d)", statementID, parameterIndex, x); + preparedStatement.setDouble(parameterIndex, x); + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + logger.logf(level, "%s.setBigDecimal(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setBigDecimal(parameterIndex, x); + } + + @Override + public void setString(int parameterIndex, String x) throws SQLException { + logger.logf(level, "%s.setString(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setString(parameterIndex, x); + } + + @Override + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + logger.logf(level, "%s.setBytes(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setBytes(parameterIndex, x); + } + + @Override + public void setDate(int parameterIndex, Date x) throws SQLException { + logger.logf(level, "%s.setDate(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setDate(parameterIndex, x); + } + + @Override + public void setTime(int parameterIndex, Time x) throws SQLException { + logger.logf(level, "%s.setTime(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setTime(parameterIndex, x); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + logger.logf(level, "%s.setTimestamp(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setTimestamp(parameterIndex, x); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + logger.logf(level, "%s.setAsciiStream(%d, %s, %d)", statementID, parameterIndex, x, length); + preparedStatement.setAsciiStream(parameterIndex, x, length); + } + + @Override + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + logger.logf(level, "%s.setUnicodeStream(%d, %s, %d)", statementID, parameterIndex, x, length); + preparedStatement.setUnicodeStream(parameterIndex, x, length); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + logger.logf(level, "%s.setBinaryStream(%d, %s, %d)", statementID, parameterIndex, x, length); + preparedStatement.setBinaryStream(parameterIndex, x, length); + } + + @Override + public void clearParameters() throws SQLException { + logger.logf(level, "%s.clearParameters()", statementID); + preparedStatement.clearParameters(); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + logger.logf(level, "%s.setObject(%d, %s, %d)", statementID, parameterIndex, x, targetSqlType); + preparedStatement.setObject(parameterIndex, x, targetSqlType); + } + + @Override + public void setObject(int parameterIndex, Object x) throws SQLException { + logger.logf(level, "%s.setObject(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setObject(parameterIndex, x); + } + + @Override + public boolean execute() throws SQLException { + boolean b = preparedStatement.execute(); + logger.logf(level, "%s.execute() = %s", statementID, b); + return b; + } + + @Override + public void addBatch() throws SQLException { + logger.logf(level, "%s.addBatch()", statementID); + preparedStatement.addBatch(); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + logger.logf(level, "%s.setCharacterStream(%d, %s, %d)", statementID, parameterIndex, reader, length); + preparedStatement.setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setRef(int parameterIndex, Ref x) throws SQLException { + logger.logf(level, "%s.setRef(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setRef(parameterIndex, x); + } + + @Override + public void setBlob(int parameterIndex, Blob x) throws SQLException { + logger.logf(level, "%s.setBlob(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setBlob(parameterIndex, x); + } + + @Override + public void setClob(int parameterIndex, Clob x) throws SQLException { + logger.logf(level, "%s.setClob(%d, %x)", statementID, parameterIndex, x); + preparedStatement.setClob(parameterIndex, x); + } + + @Override + public void setArray(int parameterIndex, Array x) throws SQLException { + logger.logf(level, "%s.setArray(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setArray(parameterIndex, x); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + ResultSetMetaData resultSetMetaData = preparedStatement.getMetaData(); + logger.logf(level, "%s.getMetaData() = %s", statementID, LoggingUtil.getID(resultSetMetaData)); + return resultSetMetaData; + } + + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + logger.logf(level, "%s.setDate(%d, %s, %s)", statementID, parameterIndex, x, cal); + preparedStatement.setDate(parameterIndex, x, cal); + } + + @Override + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + logger.logf(level, "%s.setTime(%d, %s, %s)", statementID, parameterIndex, x, cal); + preparedStatement.setTime(parameterIndex, x, cal); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + logger.logf(level, "%s.setTimestamp(%d, %s, %s)", statementID, parameterIndex, x, cal); + preparedStatement.setTimestamp(parameterIndex, x, cal); + } + + @Override + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + logger.logf(level, "%s.setNull(%d, %d, %s)", statementID, parameterIndex, sqlType, typeName); + preparedStatement.setNull(parameterIndex, sqlType, typeName); + } + + @Override + public void setURL(int parameterIndex, URL x) throws SQLException { + logger.logf(level, "%s.setURL(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setURL(parameterIndex, x); + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + ParameterMetaData x = preparedStatement.getParameterMetaData(); + logger.logf(level, "%s.getParameterMetaData() = %s", statementID, x); + return x; + } + + @Override + public void setRowId(int parameterIndex, RowId x) throws SQLException { + logger.logf(level, "%s.setRowId(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setRowId(parameterIndex, x); + } + + @Override + public void setNString(int parameterIndex, String value) throws SQLException { + logger.logf(level, "%s.setNString(%d, %s)", statementID, parameterIndex, value); + preparedStatement.setNString(parameterIndex, value); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + logger.logf(level, "%s.setNCharacterStream(%d, %s, %d)", statementID, parameterIndex, value, length); + preparedStatement.setNCharacterStream(parameterIndex, value, length); + } + + @Override + public void setNClob(int parameterIndex, NClob value) throws SQLException { + logger.logf(level, "%s.setNClob(%d, %s)", statementID, parameterIndex, value); + preparedStatement.setNClob(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + logger.logf(level, "%s.setClob(%d, %s, %s)", statementID, parameterIndex, reader, length); + preparedStatement.setClob(parameterIndex, reader, length); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + logger.logf(level, "%s.setBlob(%d, %s, %d)", statementID, parameterIndex, inputStream, length); + preparedStatement.setBlob(parameterIndex, inputStream, length); + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + logger.logf(level, "%s.setNClob(%d, %s, %d)", statementID, parameterIndex, reader, length); + preparedStatement.setNClob(parameterIndex, reader, length); + } + + @Override + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + logger.logf(level, "%s.setSQLXML(%d, %s)", statementID, parameterIndex, xmlObject); + preparedStatement.setSQLXML(parameterIndex, xmlObject); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + logger.logf(level, "%s.setNull(%d, %d)", statementID, parameterIndex, x); + preparedStatement.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + logger.logf(level, "%s.setNull(%d, %d)", statementID, parameterIndex, x); + preparedStatement.setAsciiStream(parameterIndex, x, length); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + logger.logf(level, "%s.setNull(%d, %d)", statementID, parameterIndex, x); + preparedStatement.setBinaryStream(parameterIndex, x, length); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + logger.logf(level, "%s.setCharacterStream(%d, %s, %d)", statementID, parameterIndex, reader, length); + preparedStatement.setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + logger.logf(level, "%s.setAsciiStream(%d, %d)", statementID, parameterIndex, x); + preparedStatement.setAsciiStream(parameterIndex, x); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + logger.logf(level, "%s.setBinaryStream(%d, %s)", statementID, parameterIndex, x); + preparedStatement.setBinaryStream(parameterIndex, x); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + logger.logf(level, "%s.setCharacterStream(%d, %s)", statementID, parameterIndex, reader); + preparedStatement.setCharacterStream(parameterIndex, reader); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + logger.logf(level, "%s.setNCharacterStream(%d, %s)", statementID, parameterIndex, value); + preparedStatement.setNCharacterStream(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader) throws SQLException { + logger.logf(level, "%s.setClob(%d, %s)", statementID, parameterIndex, reader); + preparedStatement.setClob(parameterIndex, reader); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + logger.logf(level, "%s.setBlob(%d, %s)", statementID, parameterIndex, inputStream); + preparedStatement.setBlob(parameterIndex, inputStream); + } + + @Override + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + logger.logf(level, "%s.setNClob(%d, %s)", statementID, parameterIndex, reader); + preparedStatement.setNClob(parameterIndex, reader); + } + + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + logger.logf(level, "%s.setObject(%d, %s, %s, %d)", statementID, parameterIndex, x, targetSqlType, scaleOrLength); + preparedStatement.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + logger.logf(level, "%s.setObject(%d, %s, %d)", statementID, parameterIndex, x, targetSqlType); + preparedStatement.setObject(parameterIndex, x, targetSqlType); + } + + @Override + public long executeLargeUpdate() throws SQLException { + long l = preparedStatement.executeLargeUpdate(); + logger.logf(level, "%s.executeLargeUpdate() = %s", statementID, l); + return l; + } +} diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingResultSet.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingResultSet.java new file mode 100644 index 00000000000..74533606839 --- /dev/null +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingResultSet.java @@ -0,0 +1,1338 @@ +/** + * 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.artemis.jdbc.store.logging; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Map; + +import org.jboss.logging.Logger; + +public class LoggingResultSet implements ResultSet { + + private final ResultSet resultSet; + + private final String resultSetID; + + private final Logger logger; + + private static final Logger.Level level = Logger.Level.TRACE; + + public LoggingResultSet(ResultSet resultSet, Logger logger) { + this.resultSet = resultSet; + this.logger = logger; + this.resultSetID = LoggingUtil.getID(resultSet); + } + + public ResultSet getResultSet() { + return resultSet; + } + + public String getResultSetID() { + return resultSetID; + } + + @Override + public boolean next() throws SQLException { + boolean b = resultSet.next(); + logger.logf(level, "%s.next() = %s", resultSetID, b); + return b; + } + + @Override + public void close() throws SQLException { + logger.logf(level, "%s.close()", resultSetID); + resultSet.close(); + } + + @Override + public boolean wasNull() throws SQLException { + boolean b = resultSet.wasNull(); + logger.logf(level, "%s.wasNull() = %s", resultSetID, b); + return b; + } + + @Override + public String getString(int columnIndex) throws SQLException { + String x = resultSet.getString(columnIndex); + logger.logf(level, "%s.getString(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public boolean getBoolean(int columnIndex) throws SQLException { + boolean x = resultSet.getBoolean(columnIndex); + logger.logf(level, "%s.getBoolean(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public byte getByte(int columnIndex) throws SQLException { + byte x = resultSet.getByte(columnIndex); + logger.logf(level, "%s.getByte(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public short getShort(int columnIndex) throws SQLException { + short x = resultSet.getShort(columnIndex); + logger.logf(level, "%s.getShort(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public int getInt(int columnIndex) throws SQLException { + int x = resultSet.getInt(columnIndex); + logger.logf(level, "%s.getInt(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public long getLong(int columnIndex) throws SQLException { + long x = resultSet.getLong(columnIndex); + logger.logf(level, "%s.getLong(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public float getFloat(int columnIndex) throws SQLException { + float x = resultSet.getFloat(columnIndex); + logger.logf(level, "%s.getFloat(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public double getDouble(int columnIndex) throws SQLException { + double x = resultSet.getDouble(columnIndex); + logger.logf(level, "%s.getDouble(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { + BigDecimal x = resultSet.getBigDecimal(columnIndex); + logger.logf(level, "%s.getBigDecimal(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public byte[] getBytes(int columnIndex) throws SQLException { + byte[] x = resultSet.getBytes(columnIndex); + logger.logf(level, "%s.getBytes(%s) = %s", resultSetID, columnIndex, Arrays.toString(x)); + return x; + } + + @Override + public Date getDate(int columnIndex) throws SQLException { + Date x = resultSet.getDate(columnIndex); + logger.logf(level, "%s.getDate(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public Time getTime(int columnIndex) throws SQLException { + Time x = resultSet.getTime(columnIndex); + logger.logf(level, "%s.getTime(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public Timestamp getTimestamp(int columnIndex) throws SQLException { + Timestamp x = resultSet.getTimestamp(columnIndex); + logger.logf(level, "%s.getTimestamp(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public InputStream getAsciiStream(int columnIndex) throws SQLException { + InputStream x = resultSet.getAsciiStream(columnIndex); + logger.logf(level, "%s.getAsciiStream(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public InputStream getUnicodeStream(int columnIndex) throws SQLException { + InputStream x = resultSet.getUnicodeStream(columnIndex); + logger.logf(level, "%s.getUnicodeStream(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public InputStream getBinaryStream(int columnIndex) throws SQLException { + InputStream x = resultSet.getBinaryStream(columnIndex); + logger.logf(level, "%s.getBinaryStream(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public String getString(String columnLabel) throws SQLException { + String x = resultSet.getString(columnLabel); + logger.logf(level, "%s.getString(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public boolean getBoolean(String columnLabel) throws SQLException { + boolean x = resultSet.getBoolean(columnLabel); + logger.logf(level, "%s.getBoolean(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public byte getByte(String columnLabel) throws SQLException { + byte x = resultSet.getByte(columnLabel); + logger.logf(level, "%s.getByte(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public short getShort(String columnLabel) throws SQLException { + short x = resultSet.getShort(columnLabel); + logger.logf(level, "%s.getShort(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public int getInt(String columnLabel) throws SQLException { + int x = resultSet.getInt(columnLabel); + logger.logf(level, "%s.getInt(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public long getLong(String columnLabel) throws SQLException { + long x = resultSet.getLong(columnLabel); + logger.logf(level, "%s.getLong(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public float getFloat(String columnLabel) throws SQLException { + float x = resultSet.getFloat(columnLabel); + logger.logf(level, "%s.getFloat(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public double getDouble(String columnLabel) throws SQLException { + double x = resultSet.getDouble(columnLabel); + logger.logf(level, "%s.getDouble(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { + BigDecimal x = resultSet.getBigDecimal(columnLabel); + logger.logf(level, "%s.getBigDecimal(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public byte[] getBytes(String columnLabel) throws SQLException { + byte[] x = resultSet.getBytes(columnLabel); + logger.logf(level, "%s.getBytes(%s) = %s", resultSetID, columnLabel, Arrays.toString(x)); + return x; + } + + @Override + public Date getDate(String columnLabel) throws SQLException { + Date x = resultSet.getDate(columnLabel); + logger.logf(level, "%s.getDate(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public Time getTime(String columnLabel) throws SQLException { + Time x = resultSet.getTime(columnLabel); + logger.logf(level, "%s.getTime(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public Timestamp getTimestamp(String columnLabel) throws SQLException { + Timestamp x = resultSet.getTimestamp(columnLabel); + logger.logf(level, "%s.getTimestamp(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public InputStream getAsciiStream(String columnLabel) throws SQLException { + InputStream x = resultSet.getAsciiStream(columnLabel); + logger.logf(level, "%s.getAsciiStream(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public InputStream getUnicodeStream(String columnLabel) throws SQLException { + InputStream x = resultSet.getUnicodeStream(columnLabel); + logger.logf(level, "%s.getUnicodeStream(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public InputStream getBinaryStream(String columnLabel) throws SQLException { + InputStream x = resultSet.getBinaryStream(columnLabel); + logger.logf(level, "%s.getBinaryStream(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public SQLWarning getWarnings() throws SQLException { + SQLWarning x = resultSet.getWarnings(); + logger.logf(level, "%s.getWarnings) = %s", resultSetID, x); + return x; + } + + @Override + public void clearWarnings() throws SQLException { + logger.logf(level, "%s.clearWarnings()", resultSetID); + resultSet.clearWarnings(); + } + + @Override + public String getCursorName() throws SQLException { + String x = resultSet.getCursorName(); + logger.logf(level, "%s.getCursorName() = %s", resultSetID, x); + return x; + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + ResultSetMetaData x = resultSet.getMetaData(); + logger.logf(level, "%s.getMetaData() = %s", resultSetID, x); + return x; + } + + @Override + public Object getObject(int columnIndex) throws SQLException { + String x = resultSet.getString(columnIndex); + logger.logf(level, "%s.getString(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public Object getObject(String columnLabel) throws SQLException { + Object x = resultSet.getObject(columnLabel); + logger.logf(level, "%s.getObject(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public int findColumn(String columnLabel) throws SQLException { + int x = resultSet.findColumn(columnLabel); + logger.logf(level, "%s.findColumn(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public Reader getCharacterStream(int columnIndex) throws SQLException { + Reader x = resultSet.getCharacterStream(columnIndex); + logger.logf(level, "%s.getCharacterStream(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public Reader getCharacterStream(String columnLabel) throws SQLException { + Reader x = resultSet.getCharacterStream(columnLabel); + logger.logf(level, "%s.getCharacterStream(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + BigDecimal x = resultSet.getBigDecimal(columnIndex); + logger.logf(level, "%s.getBigDecimal(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public BigDecimal getBigDecimal(String columnLabel) throws SQLException { + BigDecimal x = resultSet.getBigDecimal(columnLabel); + logger.logf(level, "%s.getBigDecimal(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public boolean isBeforeFirst() throws SQLException { + boolean x = resultSet.isBeforeFirst(); + logger.logf(level, "%s.isBeforeFirst() = %s", resultSetID, x); + return x; + } + + @Override + public boolean isAfterLast() throws SQLException { + boolean x = resultSet.isAfterLast(); + logger.logf(level, "%s.isAfterLast() = %s", resultSetID, x); + return x; + } + + @Override + public boolean isFirst() throws SQLException { + boolean x = resultSet.isFirst(); + logger.logf(level, "%s.isFirst() = %s", resultSetID, x); + return x; + } + + @Override + public boolean isLast() throws SQLException { + boolean x = resultSet.isLast(); + logger.logf(level, "%s.isLast() = %s", resultSetID, x); + return x; + } + + @Override + public void beforeFirst() throws SQLException { + logger.logf(level, "%s.beforeFirst()", resultSetID); + resultSet.beforeFirst(); + } + + @Override + public void afterLast() throws SQLException { + logger.logf(level, "%s.afterLast()", resultSetID); + resultSet.afterLast(); + } + + @Override + public boolean first() throws SQLException { + boolean x = resultSet.first(); + logger.logf(level, "%s.first() = %s", resultSetID, x); + return x; + } + + @Override + public boolean last() throws SQLException { + boolean x = resultSet.last(); + logger.logf(level, "%s.last() = %s", resultSetID, x); + return x; + } + + @Override + public int getRow() throws SQLException { + int x = resultSet.getRow(); + logger.logf(level, "%s.getRow() = %s", resultSetID, x); + return x; + } + + @Override + public boolean absolute(int row) throws SQLException { + boolean x = resultSet.absolute(row); + logger.logf(level, "%s.absolute(%s) = %s", resultSetID, row, x); + return x; + } + + @Override + public boolean relative(int rows) throws SQLException { + boolean x = resultSet.relative(rows); + logger.logf(level, "%s.relative(%s) = %s", resultSetID, rows, x); + return x; + } + + @Override + public boolean previous() throws SQLException { + boolean x = resultSet.previous(); + logger.logf(level, "%s.previous() = %s", resultSetID, x); + return x; + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + logger.logf(level, "%s.setFetchDirection(%s)", resultSetID, direction); + resultSet.setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + int x = resultSet.getFetchDirection(); + logger.logf(level, "%s.getFetchDirection() = %s", resultSetID, x); + return x; + } + + @Override + public void setFetchSize(int rows) throws SQLException { + logger.logf(level, "%s.setFetchSize(%s)", resultSetID, rows); + resultSet.setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + int x = resultSet.getFetchSize(); + logger.logf(level, "%s.getFetchSize() = %s", resultSetID, x); + return x; + } + + @Override + public int getType() throws SQLException { + int x = resultSet.getType(); + logger.logf(level, "%s.getType() = %s", resultSetID, x); + return x; + } + + @Override + public int getConcurrency() throws SQLException { + int x = resultSet.getConcurrency(); + logger.logf(level, "%s.getConcurrency() = %s", resultSetID, x); + return x; + } + + @Override + public boolean rowUpdated() throws SQLException { + boolean x = resultSet.rowUpdated(); + logger.logf(level, "%s.rowUpdated() = %s", resultSetID, x); + return x; + } + + @Override + public boolean rowInserted() throws SQLException { + boolean x = resultSet.rowInserted(); + logger.logf(level, "%s.rowInserted() = %s", resultSetID, x); + return x; + } + + @Override + public boolean rowDeleted() throws SQLException { + boolean x = resultSet.rowDeleted(); + logger.logf(level, "%s.rowDeleted() = %s", resultSetID, x); + return x; + } + + @Override + public void updateNull(int columnIndex) throws SQLException { + logger.logf(level, "%s.updateNull(%s)", resultSetID, columnIndex); + resultSet.updateNull(columnIndex); + } + + @Override + public void updateBoolean(int columnIndex, boolean x) throws SQLException { + logger.logf(level, "%s.updateBoolean(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateBoolean(columnIndex, x); + } + + @Override + public void updateByte(int columnIndex, byte x) throws SQLException { + logger.logf(level, "%s.updateByte(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateByte(columnIndex, x); + } + + @Override + public void updateShort(int columnIndex, short x) throws SQLException { + logger.logf(level, "%s.updateShort(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateShort(columnIndex, x); + } + + @Override + public void updateInt(int columnIndex, int x) throws SQLException { + logger.logf(level, "%s.updateInt(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateInt(columnIndex, x); + } + + @Override + public void updateLong(int columnIndex, long x) throws SQLException { + logger.logf(level, "%s.updateLong(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateLong(columnIndex, x); + } + + @Override + public void updateFloat(int columnIndex, float x) throws SQLException { + logger.logf(level, "%s.updateFloat(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateFloat(columnIndex, x); + } + + @Override + public void updateDouble(int columnIndex, double x) throws SQLException { + logger.logf(level, "%s.updateDouble(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateDouble(columnIndex, x); + } + + @Override + public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + logger.logf(level, "%s.updateBigDecimal(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateBigDecimal(columnIndex, x); + } + + @Override + public void updateString(int columnIndex, String x) throws SQLException { + logger.logf(level, "%s.updateString(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateString(columnIndex, x); + } + + @Override + public void updateBytes(int columnIndex, byte[] x) throws SQLException { + logger.logf(level, "%s.updateBytes(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateBytes(columnIndex, x); + } + + @Override + public void updateDate(int columnIndex, Date x) throws SQLException { + logger.logf(level, "%s.updateDate(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateDate(columnIndex, x); + } + + @Override + public void updateTime(int columnIndex, Time x) throws SQLException { + logger.logf(level, "%s.updateTime(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateTime(columnIndex, x); + } + + @Override + public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { + logger.logf(level, "%s.updateTimestamp(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateTimestamp(columnIndex, x); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { + logger.logf(level, "%s.updateAsciiStream(%s, %s, %s)", resultSetID, columnIndex, x, length); + resultSet.updateAsciiStream(columnIndex, x, length); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { + logger.logf(level, "%s.updateBinaryStream(%s, %s, %s)", resultSetID, columnIndex, x, length); + resultSet.updateBinaryStream(columnIndex, x, length); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { + logger.logf(level, "%s.updateCharacterStream(%s, %s, %s)", resultSetID, columnIndex, x, length); + resultSet.updateCharacterStream(columnIndex, x, length); + } + + @Override + public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { + logger.logf(level, "%s.updateObject(%s, %s, %s)", resultSetID, columnIndex, x, scaleOrLength); + resultSet.updateObject(columnIndex, x, scaleOrLength); + } + + @Override + public void updateObject(int columnIndex, Object x) throws SQLException { + logger.logf(level, "%s.updateObject(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateObject(columnIndex, x); + } + + @Override + public void updateNull(String columnLabel) throws SQLException { + logger.logf(level, "%s.updateNull(%s)", resultSetID, columnLabel); + resultSet.updateNull(columnLabel); + } + + @Override + public void updateBoolean(String columnLabel, boolean x) throws SQLException { + logger.logf(level, "%s.updateBoolean(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateBoolean(columnLabel, x); + } + + @Override + public void updateByte(String columnLabel, byte x) throws SQLException { + logger.logf(level, "%s.updateByte(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateByte(columnLabel, x); + } + + @Override + public void updateShort(String columnLabel, short x) throws SQLException { + logger.logf(level, "%s.updateShort(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateShort(columnLabel, x); + } + + @Override + public void updateInt(String columnLabel, int x) throws SQLException { + logger.logf(level, "%s.updateInt(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateInt(columnLabel, x); + } + + @Override + public void updateLong(String columnLabel, long x) throws SQLException { + logger.logf(level, "%s.updateLong(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateLong(columnLabel, x); + } + + @Override + public void updateFloat(String columnLabel, float x) throws SQLException { + logger.logf(level, "%s.updateFloat(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateFloat(columnLabel, x); + } + + @Override + public void updateDouble(String columnLabel, double x) throws SQLException { + logger.logf(level, "%s.updateDouble(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateDouble(columnLabel, x); + } + + @Override + public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { + logger.logf(level, "%s.updateBigDecimal(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateBigDecimal(columnLabel, x); + } + + @Override + public void updateString(String columnLabel, String x) throws SQLException { + logger.logf(level, "%s.updateString(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateString(columnLabel, x); + } + + @Override + public void updateBytes(String columnLabel, byte[] x) throws SQLException { + logger.logf(level, "%s.updateBytes(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateBytes(columnLabel, x); + } + + @Override + public void updateDate(String columnLabel, Date x) throws SQLException { + logger.logf(level, "%s.updateDate(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateDate(columnLabel, x); + } + + @Override + public void updateTime(String columnLabel, Time x) throws SQLException { + logger.logf(level, "%s.updateTime(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateTime(columnLabel, x); + } + + @Override + public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { + logger.logf(level, "%s.updateTimestamp(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateTimestamp(columnLabel, x); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { + logger.logf(level, "%s.updateAsciiStream(%s, %s, %s)", resultSetID, columnLabel, x, length); + resultSet.updateAsciiStream(columnLabel, x, length); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { + logger.logf(level, "%s.updateBinaryStream(%s, %s, %s)", resultSetID, columnLabel, x, length); + resultSet.updateBinaryStream(columnLabel, x, length); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader x, int length) throws SQLException { + logger.logf(level, "%s.updateCharacterStream(%s, %s, %s)", resultSetID, columnLabel, x, length); + resultSet.updateCharacterStream(columnLabel, x, length); + } + + @Override + public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException { + logger.logf(level, "%s.updateObject(%s, %s, %s)", resultSetID, columnLabel, x, scaleOrLength); + resultSet.updateObject(columnLabel, x, scaleOrLength); + } + + @Override + public void updateObject(String columnLabel, Object x) throws SQLException { + logger.logf(level, "%s.updateObject(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateObject(columnLabel, x); + } + + @Override + public void insertRow() throws SQLException { + logger.logf(level, "%s.insertRow()", resultSetID); + resultSet.insertRow(); + } + + @Override + public void updateRow() throws SQLException { + logger.logf(level, "%s.updateRow()", resultSetID); + resultSet.updateRow(); + } + + @Override + public void deleteRow() throws SQLException { + logger.logf(level, "%s.deleteRow()", resultSetID); + resultSet.deleteRow(); + } + + @Override + public void refreshRow() throws SQLException { + logger.logf(level, "%s.refreshRow()", resultSetID); + resultSet.refreshRow(); + } + + @Override + public void cancelRowUpdates() throws SQLException { + logger.logf(level, "%s.cancelRowUpdates()", resultSetID); + resultSet.cancelRowUpdates(); + } + + @Override + public void moveToInsertRow() throws SQLException { + logger.logf(level, "%s.moveToInsertRow()", resultSetID); + resultSet.moveToInsertRow(); + } + + @Override + public void moveToCurrentRow() throws SQLException { + logger.logf(level, "%s.moveToCurrentRow()", resultSetID); + resultSet.moveToCurrentRow(); + } + + @Override + public Statement getStatement() throws SQLException { + Statement x = resultSet.getStatement(); + logger.logf(level, "%s.getStatement() = %s", resultSetID, x); + return x; + } + + @Override + public Object getObject(int columnIndex, Map> map) throws SQLException { + Object x = resultSet.getObject(columnIndex, map); + logger.logf(level, "%s.getObject(%s, %s) = %s", resultSetID, columnIndex, map, x); + return x; + } + + @Override + public Ref getRef(int columnIndex) throws SQLException { + Ref x = resultSet.getRef(columnIndex); + logger.logf(level, "%s.getRef(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public Blob getBlob(int columnIndex) throws SQLException { + Blob x = resultSet.getBlob(columnIndex); + logger.logf(level, "%s.getBlob(%s) = %s (length: %d)", resultSetID, columnIndex, x, x.length()); + return x; + } + + @Override + public Clob getClob(int columnIndex) throws SQLException { + Clob x = resultSet.getClob(columnIndex); + logger.logf(level, "%s.getClob(%s) = %s (length: %d)", resultSetID, columnIndex, x, x.length()); + return x; + } + + @Override + public Array getArray(int columnIndex) throws SQLException { + Array x = resultSet.getArray(columnIndex); + logger.logf(level, "%s.getArray(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public Object getObject(String columnLabel, Map> map) throws SQLException { + Object x = resultSet.getObject(columnLabel, map); + logger.logf(level, "%s.getObject(%s, %s) = %s", resultSetID, columnLabel, map, x); + return x; + } + + @Override + public Ref getRef(String columnLabel) throws SQLException { + Ref x = resultSet.getRef(columnLabel); + logger.logf(level, "%s.getRef(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public Blob getBlob(String columnLabel) throws SQLException { + Blob x = resultSet.getBlob(columnLabel); + logger.logf(level, "%s.getBlob(%s) = %s (length: %d)", resultSetID, columnLabel, x, x.length()); + return x; + } + + @Override + public Clob getClob(String columnLabel) throws SQLException { + Clob x = resultSet.getClob(columnLabel); + logger.logf(level, "%s.getClob(%s) = %s (length: %d)", resultSetID, columnLabel, x, x.length()); + return x; + } + + @Override + public Array getArray(String columnLabel) throws SQLException { + Array x = resultSet.getArray(columnLabel); + logger.logf(level, "%s.getArray(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public Date getDate(int columnLabel, Calendar cal) throws SQLException { + Date x = resultSet.getDate(columnLabel, cal); + logger.logf(level, "%s.getDate(%s) = %s", resultSetID, columnLabel, cal, x); + return x; + } + + @Override + public Date getDate(String columnLabel, Calendar cal) throws SQLException { + Date x = resultSet.getDate(columnLabel, cal); + logger.logf(level, "%s.getDate(%s) = %s", resultSetID, columnLabel, cal, x); + return x; + } + + @Override + public Time getTime(int columnLabel, Calendar cal) throws SQLException { + Time x = resultSet.getTime(columnLabel, cal); + logger.logf(level, "%s.getTime(%s) = %s", resultSetID, columnLabel, cal, x); + return x; + } + + @Override + public Time getTime(String columnLabel, Calendar cal) throws SQLException { + Time x = resultSet.getTime(columnLabel, cal); + logger.logf(level, "%s.getTime(%s) = %s", resultSetID, columnLabel, cal, x); + return x; + } + + @Override + public Timestamp getTimestamp(int columnLabel, Calendar cal) throws SQLException { + Timestamp x = resultSet.getTimestamp(columnLabel, cal); + logger.logf(level, "%s.getTimestamp(%s) = %s", resultSetID, columnLabel, cal, x); + return x; + } + + @Override + public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { + Timestamp x = resultSet.getTimestamp(columnLabel, cal); + logger.logf(level, "%s.getTimestamp(%s) = %s", resultSetID, columnLabel, cal, x); + return x; + } + + @Override + public URL getURL(int columnLabel) throws SQLException { + URL x = resultSet.getURL(columnLabel); + logger.logf(level, "%s.getURL(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public URL getURL(String columnLabel) throws SQLException { + URL x = resultSet.getURL(columnLabel); + logger.logf(level, "%s.getURL(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public void updateRef(int columnIndex, Ref x) throws SQLException { + logger.logf(level, "%s.updateRef(%s, %s) = %s", resultSetID, columnIndex, x); + resultSet.updateRef(columnIndex, x); + } + + @Override + public void updateRef(String columnLabel, Ref x) throws SQLException { + logger.logf(level, "%s.updateRef(%s, %s) = %s", resultSetID, columnLabel, x); + resultSet.updateRef(columnLabel, x); + } + + @Override + public void updateBlob(int columnIndex, Blob x) throws SQLException { + logger.logf(level, "%s.updateBlob(%s, %s) = %s", resultSetID, columnIndex, x); + resultSet.updateBlob(columnIndex, x); + } + + @Override + public void updateBlob(String columnLabel, Blob x) throws SQLException { + logger.logf(level, "%s.updateBlob(%s, %s) = %s", resultSetID, columnLabel, x); + resultSet.updateBlob(columnLabel, x); + } + + @Override + public void updateClob(int columnIndex, Clob x) throws SQLException { + logger.logf(level, "%s.updateClob(%s, %s) = %s", resultSetID, columnIndex, x); + resultSet.updateClob(columnIndex, x); + } + + @Override + public void updateClob(String columnLabel, Clob x) throws SQLException { + logger.logf(level, "%s.updateClob(%s, %s) = %s", resultSetID, columnLabel, x); + resultSet.updateClob(columnLabel, x); + } + + @Override + public void updateArray(int columnIndex, Array x) throws SQLException { + logger.logf(level, "%s.updateArray(%s, %s) = %s", resultSetID, columnIndex, x); + resultSet.updateArray(columnIndex, x); + } + + @Override + public void updateArray(String columnLabel, Array x) throws SQLException { + logger.logf(level, "%s.updateArray(%s, %s) = %s", resultSetID, columnLabel, x); + resultSet.updateArray(columnLabel, x); + } + + @Override + public RowId getRowId(int columnIndex) throws SQLException { + RowId x = resultSet.getRowId(columnIndex); + logger.logf(level, "%s.getRowId(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public RowId getRowId(String columnLabel) throws SQLException { + RowId x = resultSet.getRowId(columnLabel); + logger.logf(level, "%s.getRowId(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public void updateRowId(int columnIndex, RowId x) throws SQLException { + logger.logf(level, "%s.updateRowId(%s, %s) = %s", resultSetID, columnIndex, x); + resultSet.updateRowId(columnIndex, x); + } + + @Override + public void updateRowId(String columnLabel, RowId x) throws SQLException { + logger.logf(level, "%s.updateRowId(%s, %s) = %s", resultSetID, columnLabel, x); + resultSet.updateRowId(columnLabel, x); + } + + @Override + public int getHoldability() throws SQLException { + int x = resultSet.getHoldability(); + logger.logf(level, "%s.getHoldability() = %s", resultSetID, x); + return x; + } + + @Override + public boolean isClosed() throws SQLException { + boolean x = resultSet.isClosed(); + logger.logf(level, "%s.isClosed() = %s", resultSetID, x); + return x; + } + + @Override + public void updateNString(int columnIndex, String x) throws SQLException { + logger.logf(level, "%s.updateNString(%s, %s) = %s", resultSetID, columnIndex, x); + resultSet.updateNString(columnIndex, x); + } + + @Override + public void updateNString(String columnLabel, String x) throws SQLException { + logger.logf(level, "%s.updateNString(%s, %s) = %s", resultSetID, columnLabel, x); + resultSet.updateNString(columnLabel, x); + } + + @Override + public void updateNClob(int columnIndex, NClob x) throws SQLException { + logger.logf(level, "%s.updateNClob(%s, %s) = %s", resultSetID, columnIndex, x); + resultSet.updateNClob(columnIndex, x); + } + + @Override + public void updateNClob(String columnLabel, NClob x) throws SQLException { + logger.logf(level, "%s.updateNClob(%s, %s) = %s", resultSetID, columnLabel, x); + resultSet.updateNClob(columnLabel, x); + } + + @Override + public NClob getNClob(int columnIndex) throws SQLException { + NClob x = resultSet.getNClob(columnIndex); + logger.logf(level, "%s.getNClob(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public NClob getNClob(String columnLabel) throws SQLException { + NClob x = resultSet.getNClob(columnLabel); + logger.logf(level, "%s.getNClob(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public SQLXML getSQLXML(int columnIndex) throws SQLException { + SQLXML x = resultSet.getSQLXML(columnIndex); + logger.logf(level, "%s.getSQLXML(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public SQLXML getSQLXML(String columnLabel) throws SQLException { + SQLXML x = resultSet.getSQLXML(columnLabel); + logger.logf(level, "%s.getSQLXML(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public void updateSQLXML(int columnIndex, SQLXML x) throws SQLException { + logger.logf(level, "%s.updateSQLXML(%s, %s) = %s", resultSetID, columnIndex, x); + resultSet.updateSQLXML(columnIndex, x); + } + + @Override + public void updateSQLXML(String columnLabel, SQLXML x) throws SQLException { + logger.logf(level, "%s.updateSQLXML(%s, %s) = %s", resultSetID, columnLabel, x); + resultSet.updateSQLXML(columnLabel, x); + } + + @Override + public String getNString(int columnIndex) throws SQLException { + String x = resultSet.getNString(columnIndex); + logger.logf(level, "%s.getNString(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public String getNString(String columnLabel) throws SQLException { + String x = resultSet.getNString(columnLabel); + logger.logf(level, "%s.getNString(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public Reader getNCharacterStream(int columnIndex) throws SQLException { + Reader x = resultSet.getNCharacterStream(columnIndex); + logger.logf(level, "%s.getNCharacterStream(%s) = %s", resultSetID, columnIndex, x); + return x; + } + + @Override + public Reader getNCharacterStream(String columnLabel) throws SQLException { + Reader x = resultSet.getNCharacterStream(columnLabel); + logger.logf(level, "%s.getNCharacterStream(%s) = %s", resultSetID, columnLabel, x); + return x; + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + logger.logf(level, "%s.updateNCharacterStream(%s, %s, %s)", resultSetID, columnIndex, x, length); + resultSet.updateNCharacterStream(columnIndex, x, length); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + logger.logf(level, "%s.updateNCharacterStream(%s, %s, %s)", resultSetID, columnLabel, reader, length); + resultSet.updateNCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { + logger.logf(level, "%s.updateAsciiStream(%s, %s, %s)", resultSetID, columnIndex, x, length); + resultSet.updateAsciiStream(columnIndex, x, length); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { + logger.logf(level, "%s.updateBinaryStream(%s, %s, %s)", resultSetID, columnIndex, x, length); + resultSet.updateBinaryStream(columnIndex, x, length); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + logger.logf(level, "%s.updateCharacterStream(%s, %s, %s)", resultSetID, columnIndex, x, length); + resultSet.updateCharacterStream(columnIndex, x, length); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { + logger.logf(level, "%s.updateAsciiStream(%s, %s, %s)", resultSetID, columnLabel, x, length); + resultSet.updateAsciiStream(columnLabel, x, length); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { + logger.logf(level, "%s.updateBinaryStream(%s, %s, %s)", resultSetID, columnLabel, x, length); + resultSet.updateBinaryStream(columnLabel, x, length); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + logger.logf(level, "%s.updateCharacterStream(%s, %s, %s)", resultSetID, columnLabel, reader, length); + resultSet.updateCharacterStream(columnLabel, reader); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { + logger.logf(level, "%s.updateBlob(%s, %s, %s)", resultSetID, columnIndex, inputStream, length); + resultSet.updateBlob(columnIndex, inputStream, length); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { + logger.logf(level, "%s.updateBlob(%s, %s, %s)", resultSetID, columnLabel, inputStream, length); + resultSet.updateBlob(columnLabel, inputStream, length); + } + + @Override + public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { + logger.logf(level, "%s.updateClob(%s, %s, %s)", resultSetID, columnIndex, reader, length); + resultSet.updateClob(columnIndex, reader, length); + } + + @Override + public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { + logger.logf(level, "%s.updateClob(%s, %s, %s)", resultSetID, columnLabel, reader, length); + resultSet.updateClob(columnLabel, reader, length); + } + + @Override + public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { + logger.logf(level, "%s.updateNClob(%s, %s, %s)", resultSetID, columnIndex, reader, length); + resultSet.updateNClob(columnIndex, reader, length); + } + + @Override + public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { + logger.logf(level, "%s.updateNClob(%s, %s, %s)", resultSetID, columnLabel, reader, length); + resultSet.updateNClob(columnLabel, reader, length); + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { + logger.logf(level, "%s.updateNCharacterStream(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateNCharacterStream(columnIndex, x); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { + logger.logf(level, "%s.updateNCharacterStream(%s, %s)", resultSetID, columnLabel, reader); + resultSet.updateNCharacterStream(columnLabel, reader); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { + logger.logf(level, "%s.updateAsciiStream(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateAsciiStream(columnIndex, x); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { + logger.logf(level, "%s.updateBinaryStream(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateBinaryStream(columnIndex, x); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { + logger.logf(level, "%s.updateCharacterStream(%s, %s)", resultSetID, columnIndex, x); + resultSet.updateCharacterStream(columnIndex, x); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { + logger.logf(level, "%s.updateAsciiStream(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateAsciiStream(columnLabel, x); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { + logger.logf(level, "%s.updateBinaryStream(%s, %s)", resultSetID, columnLabel, x); + resultSet.updateBinaryStream(columnLabel, x); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { + logger.logf(level, "%s.updateCharacterStream(%s, %s)", resultSetID, columnLabel, reader); + resultSet.updateCharacterStream(columnLabel, reader); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { + logger.logf(level, "%s.updateBlob(%s, %s)", resultSetID, columnIndex, inputStream); + resultSet.updateBlob(columnIndex, inputStream); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { + logger.logf(level, "%s.updateBlob(%s, %s)", resultSetID, columnLabel, inputStream); + resultSet.updateBlob(columnLabel, inputStream); + } + + @Override + public void updateClob(int columnIndex, Reader reader) throws SQLException { + logger.logf(level, "%s.updateClob(%s, %s)", resultSetID, columnIndex, reader); + resultSet.updateClob(columnIndex, reader); + } + + @Override + public void updateClob(String columnLabel, Reader reader) throws SQLException { + logger.logf(level, "%s.updateClob(%s, %s)", resultSetID, columnLabel, reader); + resultSet.updateClob(columnLabel, reader); + } + + @Override + public void updateNClob(int columnIndex, Reader reader) throws SQLException { + logger.logf(level, "%s.updateNClob(%s, %s)", resultSetID, columnIndex, reader); + resultSet.updateNClob(columnIndex, reader); + } + + @Override + public void updateNClob(String columnLabel, Reader reader) throws SQLException { + logger.logf(level, "%s.updateNClob(%s, %s)", resultSetID, columnLabel, reader); + resultSet.updateNClob(columnLabel, reader); + } + + @Override + public T getObject(int columnIndex, Class type) throws SQLException { + T x = resultSet.getObject(columnIndex, type); + logger.logf(level, "%s.getObject(%s, %s) = %s", resultSetID, columnIndex, type, x); + return x; + } + + @Override + public T getObject(String columnLabel, Class type) throws SQLException { + T x = resultSet.getObject(columnLabel, type); + logger.logf(level, "%s.getObject(%s, %s) = %s", resultSetID, columnLabel, type, x); + return x; + } + + @Override + public void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + logger.logf(level, "%s.updateObject(%s, %s, %s, %s) = %s", resultSetID, columnIndex, x, targetSqlType, scaleOrLength); + resultSet.updateObject(columnIndex, x, targetSqlType); + } + + @Override + public void updateObject(String columnLabel, + Object x, + SQLType targetSqlType, + int scaleOrLength) throws SQLException { + logger.logf(level, "%s.updateObject(%s, %s, %s, %s) = %s", resultSetID, columnLabel, x, targetSqlType, scaleOrLength); + resultSet.updateObject(columnLabel, x, targetSqlType, scaleOrLength); + } + + @Override + public void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException { + logger.logf(level, "%s.updateObject(%s, %s, %s) = %s", resultSetID, columnIndex, x, targetSqlType); + resultSet.updateObject(columnIndex, x, targetSqlType); + } + + @Override + public void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException { + logger.logf(level, "%s.updateObject(%s, %s, %s) = %s", resultSetID, columnLabel, x, targetSqlType); + resultSet.updateObject(columnLabel, x, targetSqlType); + } + + @Override + public T unwrap(Class iface) throws SQLException { + T x = resultSet.unwrap(iface); + logger.logf(level, "%s.unwrap(%s) = %s", resultSetID, iface, x); + return x; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + boolean x = resultSet.isWrapperFor(iface); + logger.logf(level, "%s.isWrapperFor(%s) = %s", resultSetID, iface, x); + return x; + } +} diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingStatement.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingStatement.java new file mode 100644 index 00000000000..84186f95849 --- /dev/null +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingStatement.java @@ -0,0 +1,400 @@ +/** + * 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.artemis.jdbc.store.logging; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.util.Arrays; + +import org.jboss.logging.Logger; + +public class LoggingStatement implements Statement { + + private final Statement statement; + + protected final String statementID; + + protected final Logger logger; + + protected static Logger.Level level = Logger.Level.TRACE; + + public LoggingStatement(Statement statement, Logger logger) { + this.statement = statement; + this.logger = logger; + this.statementID = LoggingUtil.getID(statement); + } + + public Statement getStatement() { + return statement; + } + + public String getStatementID() { + return statementID; + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + LoggingResultSet rs = new LoggingResultSet(statement.executeQuery(sql), logger); + logger.logf(level, "%s.executeQuery(%s) = %s", statementID, sql, rs.getResultSetID()); + return rs; + } + + @Override + public int executeUpdate(String sql) throws SQLException { + int i = statement.executeUpdate(sql); + logger.logf(level, "%s.executeUpdate(%s) = %s", statementID, sql, i); + return i; + } + + @Override + public void close() throws SQLException { + logger.logf(level, "%s.close()", statementID); + statement.close(); + } + + @Override + public int getMaxFieldSize() throws SQLException { + int i = statement.getMaxFieldSize(); + logger.logf(level, "%s.getMaxFieldSize() = %s", statementID, i); + return i; + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + logger.logf(level, "%s.setMaxFieldSize(%d)", statementID, max); + statement.setMaxFieldSize(max); + } + + @Override + public int getMaxRows() throws SQLException { + int i = statement.getMaxRows(); + logger.logf(level, "%s.getMaxRows() = %s", statementID, i); + return i; + } + + @Override + public void setMaxRows(int max) throws SQLException { + logger.logf(level, "%s.setMaxRows()", statementID); + statement.setMaxRows(max); + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + logger.logf(level, "%s.setEscapeProcessing(%s)", statementID, enable); + statement.setEscapeProcessing(enable); + } + + @Override + public int getQueryTimeout() throws SQLException { + int i = statement.getQueryTimeout(); + logger.logf(level, "%s.getQueryTimeout() = %s", statementID, i); + return i; + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + logger.logf(level, "%s.setQueryTimeout(%d)", statementID, seconds); + statement.setQueryTimeout(seconds); + } + + @Override + public void cancel() throws SQLException { + logger.logf(level, "%s.cancel()", statementID); + statement.cancel(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + logger.logf(level, "%s.getWarnings()", statementID); + return statement.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + logger.logf(level, "%s.clearWarnings()", statementID); + statement.clearWarnings(); + } + + @Override + public void setCursorName(String name) throws SQLException { + logger.logf(level, "%s.setCursorName(%s)", statementID, name); + statement.setCursorName(name); + } + + @Override + public boolean execute(String sql) throws SQLException { + boolean b = statement.execute(sql); + logger.logf(level, "%s.execute(%s) = %s", statementID, sql, b); + return b; + } + + @Override + public ResultSet getResultSet() throws SQLException { + LoggingResultSet rs = new LoggingResultSet(statement.getResultSet(), logger); + logger.logf(level, "%s.executeQuery() = %s", statementID, rs.getResultSetID()); + return rs; + } + + @Override + public int getUpdateCount() throws SQLException { + int i = statement.getUpdateCount(); + logger.logf(level, "%s.getUpdateCount() = %s", statementID, i); + return i; + } + + @Override + public boolean getMoreResults() throws SQLException { + boolean b = statement.getMoreResults(); + logger.logf(level, "%s.getMoreResults() = %s", statementID, b); + return b; + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + logger.logf(level, "%s.setFetchDirection()", statementID); + statement.setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + int i = statement.getFetchDirection(); + logger.logf(level, "%s.getFetchDirection() = %s", statementID, i); + return i; + } + + @Override + public void setFetchSize(int rows) throws SQLException { + logger.logf(level, "%s.setFetchSize(%d)", statementID, rows); + statement.setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + int i = statement.getFetchSize(); + logger.logf(level, "%s.getFetchSize() = %s", statementID, i); + return i; + } + + @Override + public int getResultSetConcurrency() throws SQLException { + int i = statement.getResultSetConcurrency(); + logger.logf(level, "%s.getResultSetConcurrency() = %s", statementID, i); + return i; + } + + @Override + public int getResultSetType() throws SQLException { + int i = statement.getResultSetType(); + logger.logf(level, "%s.getResultSetType() = %s", statementID, i); + return i; + } + + @Override + public void addBatch(String sql) throws SQLException { + logger.logf(level, "%s.addBatch(%d, %s)", statementID); + statement.addBatch(sql); + } + + @Override + public void clearBatch() throws SQLException { + logger.logf(level, "%s.clearBatch()", statementID); + statement.clearBatch(); + } + + @Override + public int[] executeBatch() throws SQLException { + int[] i = statement.executeBatch(); + logger.logf(level, "%s.executeBatch() = %s", statementID, Arrays.toString(i)); + return i; + } + + @Override + public Connection getConnection() throws SQLException { + LoggingConnection connection = new LoggingConnection(statement.getConnection(), logger); + logger.logf(level, "%s.getConnection() = %s", statementID, connection.getConnectionID()); + return connection; + } + + @Override + public boolean getMoreResults(int current) throws SQLException { + boolean b = statement.getMoreResults(current); + logger.logf(level, "%s.getMoreResults(%s) = %s", statementID, current, b); + return b; + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + LoggingResultSet rs = new LoggingResultSet(statement.getGeneratedKeys(), logger); + logger.logf(level, "%s.getGeneratedKeys() = %s", statementID, rs.getResultSetID()); + return rs; + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + int i = statement.executeUpdate(sql, autoGeneratedKeys); + logger.logf(level, "%s.executeUpdate(%s, %d) = %s", statementID, sql, autoGeneratedKeys, i); + return i; + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + int i = statement.executeUpdate(sql, columnIndexes); + logger.logf(level, "%s.executeUpdate(%s, %s) = %s", statementID, sql, Arrays.toString(columnIndexes), i); + return i; + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + int i = statement.executeUpdate(sql, columnNames); + logger.logf(level, "%s.executeUpdate(%s, %s) = %s", statementID, sql, Arrays.toString(columnNames), i); + return i; + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + boolean b = statement.execute(sql, autoGeneratedKeys); + logger.logf(level, "%s.execute(%s, %s) = %s", statementID, sql, autoGeneratedKeys, b); + return b; + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + boolean b = statement.execute(sql, columnIndexes); + logger.logf(level, "%s.execute(%s, %s) = %s", statementID, sql, Arrays.toString(columnIndexes), b); + return b; + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + boolean b = statement.execute(sql, columnNames); + logger.logf(level, "%s.execute(%s, %s) = %s", statementID, sql, Arrays.toString(columnNames), b); + return b; + } + + @Override + public int getResultSetHoldability() throws SQLException { + int i = statement.getResultSetHoldability(); + logger.logf(level, "%s.getResultSetHoldability() = %s", statementID, i); + return i; + } + + @Override + public boolean isClosed() throws SQLException { + boolean b = statement.isClosed(); + logger.logf(level, "%s.isClosed() = %s", statementID, b); + return b; + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + logger.logf(level, "%s.setPoolable(%s)", statementID, poolable); + statement.setPoolable(poolable); + } + + @Override + public boolean isPoolable() throws SQLException { + boolean b = statement.isPoolable(); + logger.logf(level, "%s.isPoolable() = %s", statementID, b); + return b; + } + + @Override + public void closeOnCompletion() throws SQLException { + logger.logf(level, "%s.closeOnCompletion()", statementID); + statement.closeOnCompletion(); + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + boolean b = statement.isCloseOnCompletion(); + logger.logf(level, "%s.isCloseOnCompletion() = %s", statementID, b); + return b; + } + + @Override + public long getLargeUpdateCount() throws SQLException { + long x = statement.getLargeUpdateCount(); + logger.logf(level, "%s.getLargeUpdateCount() = %s", statementID, x); + return x; + } + + @Override + public void setLargeMaxRows(long max) throws SQLException { + logger.logf(level, "%s.setLargeMaxRows(%d)", statementID, max); + statement.setLargeMaxRows(max); + } + + @Override + public long getLargeMaxRows() throws SQLException { + long x = statement.getLargeMaxRows(); + logger.logf(level, "%s.getLargeMaxRows() = %s", statementID, x); + return x; + } + + @Override + public long[] executeLargeBatch() throws SQLException { + long[] x = statement.executeLargeBatch(); + logger.logf(level, "%s.executeLargeBatch() = %s", statementID, x); + return x; + } + + @Override + public long executeLargeUpdate(String sql) throws SQLException { + long x = statement.executeLargeUpdate(sql); + logger.logf(level, "%s.executeLargeUpdate(%s) = %s", statementID, sql, x); + return x; + } + + @Override + public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + long x = statement.executeLargeUpdate(sql, autoGeneratedKeys); + logger.logf(level, "%s.executeLargeUpdate() = %s", statementID, sql, autoGeneratedKeys, x); + return x; + } + + @Override + public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { + long x = statement.executeLargeUpdate(sql, columnIndexes); + logger.logf(level, "%s.executeLargeUpdate(%s, %s) = %s", statementID, sql, Arrays.toString(columnIndexes), x); + return x; + } + + @Override + public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { + long x = statement.executeLargeUpdate(sql, columnNames); + logger.logf(level, "%s.executeLargeUpdate(%s, %s) = %s", statementID, sql, Arrays.toString(columnNames), x); + return x; + } + + @Override + public T unwrap(Class iface) throws SQLException { + T x = statement.unwrap(iface); + logger.logf(level, "%s.unwrap(%s) = %s", statementID, iface, x); + return x; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + boolean b = statement.isWrapperFor(iface); + logger.logf(level, "%s.isWrapperFor(%s) = %s", statementID, iface, b); + return b; + } +} diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingUtil.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingUtil.java new file mode 100644 index 00000000000..27a94e0a66c --- /dev/null +++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/logging/LoggingUtil.java @@ -0,0 +1,24 @@ +/** + * 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.artemis.jdbc.store.logging; + +public class LoggingUtil { + public static String getID(Object object) { + return new StringBuilder().append(object.getClass().getName()).append("@").append(Integer.toHexString(System.identityHashCode(object))).toString(); + } +} From ad7c0ea12da627ea9f3a34460b0723cd4ad6faf0 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Tue, 28 Aug 2018 11:02:27 -0500 Subject: [PATCH 173/207] ARTEMIS-2060 OpenSSL tests broken after Netty upgrade --- examples/features/standard/netty-openssl/pom.xml | 5 ++--- tests/integration-tests/pom.xml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/features/standard/netty-openssl/pom.xml b/examples/features/standard/netty-openssl/pom.xml index b146d280081..ebd9c414c8f 100644 --- a/examples/features/standard/netty-openssl/pom.xml +++ b/examples/features/standard/netty-openssl/pom.xml @@ -33,7 +33,6 @@ under the License. ${project.basedir}/../../../.. - 2.0.7.Final @@ -45,7 +44,7 @@ under the License. io.netty netty-tcnative-boringssl-static - ${netty.tcnative.version} + ${netty-tcnative-version} @@ -63,7 +62,7 @@ under the License. ${noServer} - io.netty:netty-tcnative-boringssl-static:${netty.tcnative.version} + io.netty:netty-tcnative-boringssl-static:${netty-tcnative-version} diff --git a/tests/integration-tests/pom.xml b/tests/integration-tests/pom.xml index ad278643ec7..3ab96b0e3cd 100644 --- a/tests/integration-tests/pom.xml +++ b/tests/integration-tests/pom.xml @@ -401,7 +401,7 @@ io.netty netty-tcnative-boringssl-static - 2.0.7.Final + ${netty-tcnative-version} From 34254095c88b2fa41a65af5eb359f862e4c34496 Mon Sep 17 00:00:00 2001 From: Timothy Bish Date: Tue, 28 Aug 2018 12:15:01 -0400 Subject: [PATCH 174/207] ARTEMIS-2057 Fix runaway credit grants Ensure the broker looks at local receiver credit when checking for credit top off threshold and then do a proper top off back to the high water mark to sync with how client receivers manage their credit. --- .../amqp/broker/AMQPSessionCallback.java | 7 +- .../amqp/broker/AMQPSessionCallbackTest.java | 248 ++++++++++++++++++ .../integration/amqp/AmqpSenderTest.java | 2 +- 3 files changed, 254 insertions(+), 3 deletions(-) create mode 100644 artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallbackTest.java diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java index f940c5aa59a..b99a0538af8 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java @@ -581,8 +581,11 @@ public void offerProducerCredit(final SimpleString address, Runnable creditRunnable = () -> { connection.lock(); try { - if (receiver.getRemoteCredit() <= threshold) { - receiver.flow(credits); + if (receiver.getCredit() <= threshold) { + int topUp = credits - receiver.getCredit(); + if (topUp > 0) { + receiver.flow(topUp); + } } } finally { connection.unlock(); diff --git a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallbackTest.java b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallbackTest.java new file mode 100644 index 00000000000..e86e960209f --- /dev/null +++ b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallbackTest.java @@ -0,0 +1,248 @@ +/* + * 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.artemis.protocol.amqp.broker; + +import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.AMQP_CREDITS_DEFAULT; +import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.AMQP_LOW_CREDITS_DEFAULT; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; + +import java.util.concurrent.Executor; + +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.paging.PagingManager; +import org.apache.activemq.artemis.core.paging.PagingStore; +import org.apache.activemq.artemis.core.persistence.OperationContext; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.protocol.amqp.proton.AMQPConnectionContext; +import org.apache.activemq.artemis.spi.core.remoting.Connection; +import org.apache.qpid.proton.engine.Receiver; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.quality.Strictness; + +public class AMQPSessionCallbackTest { + + @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); + + @Mock private AMQPConnectionCallback protonSPI; + @Mock private ProtonProtocolManager manager; + @Mock private AMQPConnectionContext connection; + @Mock private Connection transportConnection; + @Mock private Executor executor; + @Mock private OperationContext operationContext; + @Mock private Receiver receiver; + @Mock private ActiveMQServer server; + @Mock private PagingManager pagingManager; + @Mock private PagingStore pagingStore; + + /** + * Test that the AMQPSessionCallback grants no credit when not at threshold + */ + @Test + public void testOfferProducerWithNoAddressDoesNotTopOffCreditAboveThreshold() { + // Mock returns to get at the runnable that grants credit. + Mockito.when(manager.getServer()).thenReturn(server); + Mockito.when(server.getPagingManager()).thenReturn(pagingManager); + + // Capture credit runnable and invoke to trigger credit top off + ArgumentCaptor argument = ArgumentCaptor.forClass(Runnable.class); + AMQPSessionCallback session = new AMQPSessionCallback( + protonSPI, manager, connection, transportConnection, executor, operationContext); + + // Credit is above threshold + Mockito.when(receiver.getCredit()).thenReturn(AMQP_LOW_CREDITS_DEFAULT + 1); + + session.offerProducerCredit(null, AMQP_CREDITS_DEFAULT, AMQP_LOW_CREDITS_DEFAULT, receiver); + + // Run the credit refill code. + Mockito.verify(pagingManager).checkMemory(argument.capture()); + assertNotNull(argument.getValue()); + argument.getValue().run(); + + // Ensure we aren't looking at remote credit as that gives us the wrong view of what credit is at the broker + Mockito.verify(receiver, never()).getRemoteCredit(); + + // Credit runnable should not top off credit to configured value + Mockito.verify(receiver, never()).flow(anyInt()); + } + + /** + * Test that when at threshold the manager tops off credit for anonymous sender + */ + @Test + public void testOfferProducerWithNoAddressTopsOffCreditAtThreshold() { + // Mock returns to get at the runnable that grants credit. + Mockito.when(manager.getServer()).thenReturn(server); + Mockito.when(server.getPagingManager()).thenReturn(pagingManager); + + // Capture credit runnable and invoke to trigger credit top off + ArgumentCaptor argument = ArgumentCaptor.forClass(Runnable.class); + AMQPSessionCallback session = new AMQPSessionCallback( + protonSPI, manager, connection, transportConnection, executor, operationContext); + + // Credit is at threshold + Mockito.when(receiver.getCredit()).thenReturn(AMQP_LOW_CREDITS_DEFAULT); + + session.offerProducerCredit(null, AMQP_CREDITS_DEFAULT, AMQP_LOW_CREDITS_DEFAULT, receiver); + + // Run the credit refill code. + Mockito.verify(pagingManager).checkMemory(argument.capture()); + assertNotNull(argument.getValue()); + argument.getValue().run(); + + // Ensure we aren't looking at remote credit as that gives us the wrong view of what credit is at the broker + Mockito.verify(receiver, never()).getRemoteCredit(); + + // Credit runnable should top off credit to configured value + Mockito.verify(receiver).flow(AMQP_CREDITS_DEFAULT - AMQP_LOW_CREDITS_DEFAULT); + } + + /** + * Test that the AMQPSessionCallback grants no credit when not at threshold + */ + @Test + public void testOfferProducerWithAddressDoesNotTopOffCreditAboveThreshold() throws Exception { + // Mock returns to get at the runnable that grants credit. + Mockito.when(manager.getServer()).thenReturn(server); + Mockito.when(server.getPagingManager()).thenReturn(pagingManager); + Mockito.when(pagingManager.getPageStore(any(SimpleString.class))).thenReturn(pagingStore); + + // Capture credit runnable and invoke to trigger credit top off + ArgumentCaptor argument = ArgumentCaptor.forClass(Runnable.class); + AMQPSessionCallback session = new AMQPSessionCallback( + protonSPI, manager, connection, transportConnection, executor, operationContext); + + // Credit is above threshold + Mockito.when(receiver.getCredit()).thenReturn(AMQP_LOW_CREDITS_DEFAULT + 1); + + session.offerProducerCredit(new SimpleString("test"), AMQP_CREDITS_DEFAULT, AMQP_LOW_CREDITS_DEFAULT, receiver); + + // Run the credit refill code. + Mockito.verify(pagingStore).checkMemory(argument.capture()); + assertNotNull(argument.getValue()); + argument.getValue().run(); + + // Ensure we aren't looking at remote credit as that gives us the wrong view of what credit is at the broker + Mockito.verify(receiver, never()).getRemoteCredit(); + + // Credit runnable should not top off credit to configured value + Mockito.verify(receiver, never()).flow(anyInt()); + } + + /** + * Test that when at threshold the manager tops off credit for sender + */ + @Test + public void testOfferProducerWithAddressTopsOffCreditAtThreshold() throws Exception { + // Mock returns to get at the runnable that grants credit. + Mockito.when(manager.getServer()).thenReturn(server); + Mockito.when(server.getPagingManager()).thenReturn(pagingManager); + Mockito.when(pagingManager.getPageStore(any(SimpleString.class))).thenReturn(pagingStore); + + // Capture credit runnable and invoke to trigger credit top off + ArgumentCaptor argument = ArgumentCaptor.forClass(Runnable.class); + AMQPSessionCallback session = new AMQPSessionCallback( + protonSPI, manager, connection, transportConnection, executor, operationContext); + + // Credit is at threshold + Mockito.when(receiver.getCredit()).thenReturn(AMQP_LOW_CREDITS_DEFAULT); + + session.offerProducerCredit(new SimpleString("test"), AMQP_CREDITS_DEFAULT, AMQP_LOW_CREDITS_DEFAULT, receiver); + + // Run the credit refill code. + Mockito.verify(pagingStore).checkMemory(argument.capture()); + assertNotNull(argument.getValue()); + argument.getValue().run(); + + // Ensure we aren't looking at remote credit as that gives us the wrong view of what credit is at the broker + Mockito.verify(receiver, never()).getRemoteCredit(); + + // Credit runnable should top off credit to configured value + Mockito.verify(receiver).flow(AMQP_CREDITS_DEFAULT - AMQP_LOW_CREDITS_DEFAULT); + } + + /** + * Test that the AMQPSessionCallback grants no credit when the computation results in negative credit + */ + @Test + public void testOfferProducerWithNoAddressDoesNotGrantNegativeCredit() { + // Mock returns to get at the runnable that grants credit. + Mockito.when(manager.getServer()).thenReturn(server); + Mockito.when(server.getPagingManager()).thenReturn(pagingManager); + + // Capture credit runnable and invoke to trigger credit top off + ArgumentCaptor argument = ArgumentCaptor.forClass(Runnable.class); + AMQPSessionCallback session = new AMQPSessionCallback( + protonSPI, manager, connection, transportConnection, executor, operationContext); + + // Credit is at threshold + Mockito.when(receiver.getCredit()).thenReturn(AMQP_LOW_CREDITS_DEFAULT); + + session.offerProducerCredit(null, 1, AMQP_LOW_CREDITS_DEFAULT, receiver); + + // Run the credit refill code. + Mockito.verify(pagingManager).checkMemory(argument.capture()); + assertNotNull(argument.getValue()); + argument.getValue().run(); + + // Ensure we aren't looking at remote credit as that gives us the wrong view of what credit is at the broker + Mockito.verify(receiver, never()).getRemoteCredit(); + + // Credit runnable should not grant what would be negative credit here + Mockito.verify(receiver, never()).flow(anyInt()); + } + + /** + * Test that the AMQPSessionCallback grants no credit when the computation results in negative credit + */ + @Test + public void testOfferProducerWithAddressDoesNotGrantNegativeCredit() throws Exception { + // Mock returns to get at the runnable that grants credit. + Mockito.when(manager.getServer()).thenReturn(server); + Mockito.when(server.getPagingManager()).thenReturn(pagingManager); + Mockito.when(pagingManager.getPageStore(any(SimpleString.class))).thenReturn(pagingStore); + + // Capture credit runnable and invoke to trigger credit top off + ArgumentCaptor argument = ArgumentCaptor.forClass(Runnable.class); + AMQPSessionCallback session = new AMQPSessionCallback( + protonSPI, manager, connection, transportConnection, executor, operationContext); + + // Credit is at threshold + Mockito.when(receiver.getCredit()).thenReturn(AMQP_LOW_CREDITS_DEFAULT); + + session.offerProducerCredit(new SimpleString("test"), 1, AMQP_LOW_CREDITS_DEFAULT, receiver); + + // Run the credit refill code. + Mockito.verify(pagingStore).checkMemory(argument.capture()); + assertNotNull(argument.getValue()); + argument.getValue().run(); + + // Ensure we aren't looking at remote credit as that gives us the wrong view of what credit is at the broker + Mockito.verify(receiver, never()).getRemoteCredit(); + + // Credit runnable should not grant what would be negative credit here + Mockito.verify(receiver, never()).flow(anyInt()); + } +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpSenderTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpSenderTest.java index e10bc7d2bfa..50d882825d5 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpSenderTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpSenderTest.java @@ -199,7 +199,7 @@ public void inspectCredit(Sender sender) { initialCredit.countDown(); break; case 2: - assertEquals("Unexpected replenished credit", AmqpSupport.AMQP_LOW_CREDITS_DEFAULT + AmqpSupport.AMQP_CREDITS_DEFAULT, sender.getCredit()); + assertEquals("Unexpected replenished credit", AmqpSupport.AMQP_CREDITS_DEFAULT, sender.getCredit()); refreshedCredit.countDown(); break; default: From 960833d2be6cfd632334e7a81b042ea7281d3fdc Mon Sep 17 00:00:00 2001 From: Timothy Bish Date: Tue, 28 Aug 2018 16:11:14 -0400 Subject: [PATCH 175/207] ARTEMIS-2062 Only attempt to refill credit when needed Avoid firing the offerProducerCredit code when we know that the credit isnt low enough that a refill is needed, which avoids lock contention and garbage creation as each inbound message is processed. --- .../protocol/amqp/proton/ProtonServerReceiverContext.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java index c3df1a73d02..e54a1a5ad6f 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java @@ -299,7 +299,9 @@ public void close(ErrorCondition condition) throws ActiveMQAMQPException { public void flow(int credits, int threshold) { // Use the SessionSPI to allocate producer credits, or default, always allocate credit. if (sessionSPI != null) { - sessionSPI.offerProducerCredit(address, credits, threshold, receiver); + if (receiver.getCredit() <= threshold) { + sessionSPI.offerProducerCredit(address, credits, threshold, receiver); + } } else { connection.lock(); try { From 34fa0dded6de2298e45013fd134d4c87b0e836b2 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 29 Aug 2018 10:59:51 -0400 Subject: [PATCH 176/207] ARTEMIS-2052 Fixing initial credit negotiation --- .../artemis/core/client/impl/ClientConsumerImpl.java | 10 ++++++++++ .../core/client/impl/ClientConsumerInternal.java | 2 ++ .../artemis/core/client/impl/ClientSessionImpl.java | 2 +- .../protocol/core/impl/ActiveMQSessionContext.java | 2 +- .../hornetq/client/HornetQClientSessionContext.java | 2 +- .../integration/client/ConsumerWindowSizeTest.java | 4 ++-- .../unit/core/client/impl/LargeMessageBufferTest.java | 5 +++++ 7 files changed, 22 insertions(+), 5 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerImpl.java index 5e3d95e07f9..4946efb9603 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerImpl.java @@ -86,6 +86,8 @@ public final class ClientConsumerImpl implements ClientConsumerInternal { // Number of pending calls on flow control private final ReusableLatch pendingFlowControl = new ReusableLatch(0); + private final int initialWindow; + private final int clientWindowSize; private final int ackBatchSize; @@ -140,6 +142,7 @@ public ClientConsumerImpl(final ClientSessionInternal session, final SimpleString queueName, final SimpleString filterString, final boolean browseOnly, + final int initialWindow, final int clientWindowSize, final int ackBatchSize, final TokenBucketLimiter rateLimiter, @@ -164,6 +167,8 @@ public ClientConsumerImpl(final ClientSessionInternal session, sessionExecutor = executor; + this.initialWindow = initialWindow; + this.clientWindowSize = clientWindowSize; this.ackBatchSize = ackBatchSize; @@ -743,6 +748,11 @@ private void resetLargeMessageController() { } } + @Override + public int getInitialWindowSize() { + return initialWindow; + } + @Override public int getClientWindowSize() { return clientWindowSize; diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerInternal.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerInternal.java index 986f397d25b..819082134bc 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerInternal.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientConsumerInternal.java @@ -55,6 +55,8 @@ public interface ClientConsumerInternal extends ClientConsumer { int getClientWindowSize(); + int getInitialWindowSize(); + int getBufferSize(); void cleanUp() throws ActiveMQException; diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java index f1ef526f586..2edf6292c60 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java @@ -1888,7 +1888,7 @@ private ClientConsumer internalCreateConsumer(final SimpleString queueName, // TODO: this could semantically change on other servers. I know for instance on stomp this is just an ignore if (consumer.getClientWindowSize() != 0) { - sessionContext.sendConsumerCredits(consumer, consumer.getClientWindowSize()); + sessionContext.sendConsumerCredits(consumer, consumer.getInitialWindowSize()); } return consumer; diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java index 4ee7ee9a79b..18227cbd545 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java @@ -329,7 +329,7 @@ public ClientConsumerInternal createConsumer(SimpleString queueName, // The value we send is just a hint final int consumerWindowSize = windowSize == ActiveMQClient.DEFAULT_CONSUMER_WINDOW_SIZE ? this.getDefaultConsumerWindowSize(queueInfo) : windowSize; - return new ClientConsumerImpl(session, consumerContext, queueName, filterString, browseOnly, calcWindowSize(consumerWindowSize), ackBatchSize, maxRate > 0 ? new TokenBucketLimiterImpl(maxRate, false) : null, executor, flowControlExecutor, this, queueInfo.toQueueQuery(), lookupTCCL()); + return new ClientConsumerImpl(session, consumerContext, queueName, filterString, browseOnly, consumerWindowSize, calcWindowSize(consumerWindowSize), ackBatchSize, maxRate > 0 ? new TokenBucketLimiterImpl(maxRate, false) : null, executor, flowControlExecutor, this, queueInfo.toQueueQuery(), lookupTCCL()); } @Override diff --git a/artemis-protocols/artemis-hqclient-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/hornetq/client/HornetQClientSessionContext.java b/artemis-protocols/artemis-hqclient-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/hornetq/client/HornetQClientSessionContext.java index ee3520fa1e9..f06772cb391 100644 --- a/artemis-protocols/artemis-hqclient-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/hornetq/client/HornetQClientSessionContext.java +++ b/artemis-protocols/artemis-hqclient-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/hornetq/client/HornetQClientSessionContext.java @@ -96,7 +96,7 @@ public ClientConsumerInternal createConsumer(SimpleString queueName, // could be overridden on the queue settings // The value we send is just a hint - return new ClientConsumerImpl(session, consumerContext, queueName, filterString, browseOnly, calcWindowSize(windowSize), ackBatchSize, maxRate > 0 ? new TokenBucketLimiterImpl(maxRate, false) : null, executor, flowControlExecutor, this, queueInfo.toQueueQuery(), lookupTCCL()); + return new ClientConsumerImpl(session, consumerContext, queueName, filterString, browseOnly, windowSize, calcWindowSize(windowSize), ackBatchSize, maxRate > 0 ? new TokenBucketLimiterImpl(maxRate, false) : null, executor, flowControlExecutor, this, queueInfo.toQueueQuery(), lookupTCCL()); } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java index e58f4bcaa13..2fb737c553f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/ConsumerWindowSizeTest.java @@ -1454,7 +1454,7 @@ public void testConsumerWindowSizeAddressSettingsDifferentAddressAndQueueName() ServerSession ss = messagingService.getSessionByID(((ClientSessionImpl)session).getName()); ServerConsumerImpl cons = (ServerConsumerImpl) ss.locateConsumer(consumer.getConsumerContext().getId()); - assertTrue(Wait.waitFor(() -> cons.getAvailableCredits().get() == consumer.getClientWindowSize(), 5000, 500)); + assertTrue(Wait.waitFor(() -> cons.getAvailableCredits().get() == consumer.getClientWindowSize() * 2, 5000, 50)); } @Test @@ -1465,7 +1465,7 @@ public void testConsumerWindowSizeAddressSettingsWildCard() throws Exception { final AddressSettings settings = new AddressSettings(); settings.setDefaultConsumerWindowSize(defaultConsumerWindowSize); messagingService.getConfiguration() - .getAddressesSettings().put("#", settings); + .getAddressesSettings().put("#", settings); messagingService.start(); messagingService.createQueue(queueA, RoutingType.ANYCAST, queueA, null, true, false); diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/client/impl/LargeMessageBufferTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/client/impl/LargeMessageBufferTest.java index c1790d0d609..a68382f79c9 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/client/impl/LargeMessageBufferTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/client/impl/LargeMessageBufferTest.java @@ -735,6 +735,11 @@ public int getClientWindowSize() { return 0; } + @Override + public int getInitialWindowSize() { + return 0; + } + @Override public SimpleString getFilterString() { From 9a855e18e1e73fb36486a06b53ee61b9fbf8f7db Mon Sep 17 00:00:00 2001 From: Martyn Taylor Date: Mon, 27 Aug 2018 15:05:20 +0100 Subject: [PATCH 177/207] ARTEMIS-2055 Lock LM on PacketHandler on clear --- .../core/ServerSessionPacketHandler.java | 47 +++++++++++-------- .../byteman/LargeMessageOnShutdownTest.java | 2 +- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java index 3b0433ef664..37564b51a29 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java @@ -160,6 +160,7 @@ public class ServerSessionPacketHandler implements ChannelHandler { private final boolean direct; + private final Object largeMessageLock = new Object(); public ServerSessionPacketHandler(final ActiveMQServer server, final CoreProtocolManager manager, @@ -196,11 +197,15 @@ public ServerSessionPacketHandler(final ActiveMQServer server, } private void clearLargeMessage() { - if (currentLargeMessage != null) { - try { - currentLargeMessage.deleteFile(); - } catch (Throwable error) { - ActiveMQServerLogger.LOGGER.errorDeletingLargeMessageFile(error); + synchronized (largeMessageLock) { + if (currentLargeMessage != null) { + try { + currentLargeMessage.deleteFile(); + } catch (Throwable error) { + ActiveMQServerLogger.LOGGER.errorDeletingLargeMessageFile(error); + } finally { + currentLargeMessage = null; + } } } } @@ -958,26 +963,28 @@ private void sendContinuations(final int packetSize, final long messageBodySize, final byte[] body, final boolean continues) throws Exception { - if (currentLargeMessage == null) { - throw ActiveMQMessageBundle.BUNDLE.largeMessageNotInitialised(); - } - // Immediately release the credits for the continuations- these don't contribute to the in-memory size - // of the message + synchronized (largeMessageLock) { + if (currentLargeMessage == null) { + throw ActiveMQMessageBundle.BUNDLE.largeMessageNotInitialised(); + } + + // Immediately release the credits for the continuations- these don't contribute to the in-memory size + // of the message - currentLargeMessage.addBytes(body); + currentLargeMessage.addBytes(body); - if (!continues) { - currentLargeMessage.releaseResources(); + if (!continues) { + currentLargeMessage.releaseResources(); - if (messageBodySize >= 0) { - currentLargeMessage.putLongProperty(Message.HDR_LARGE_BODY_SIZE, messageBodySize); - } + if (messageBodySize >= 0) { + currentLargeMessage.putLongProperty(Message.HDR_LARGE_BODY_SIZE, messageBodySize); + } - LargeServerMessage message = currentLargeMessage; - currentLargeMessage = null; - session.doSend(session.getCurrentTransaction(), message, null, false, false); + LargeServerMessage message = currentLargeMessage; + currentLargeMessage = null; + session.doSend(session.getCurrentTransaction(), message, null, false, false); + } } } - } diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java index e9be73b8cb0..9b01223424f 100644 --- a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/LargeMessageOnShutdownTest.java @@ -73,7 +73,7 @@ public void tearDown() throws Exception { condition = "!flagged(\"testLargeMessageOnShutdown\")", action = "org.apache.activemq.artemis.tests.extras.byteman.LargeMessageOnShutdownTest.stopServer();" + - "waitFor(\"testLargeMessageOnShutdown\");" + + "waitFor(\"testLargeMessageOnShutdown\", 5000);" + "flag(\"testLargeMessageOnShutdown\")" ), @BMRule( From 3af9ca057f31e4802312b557c5f80ab1bc920bb8 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Wed, 29 Aug 2018 14:29:10 -0500 Subject: [PATCH 178/207] ARTEMIS-2063 improve logging for address/queue deployment --- .../artemis/core/server/ActiveMQServerLogger.java | 8 ++++++-- .../artemis/core/server/impl/ActiveMQServerImpl.java | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java index b10d652cd31..6aa53f60e54 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java @@ -94,8 +94,8 @@ public interface ActiveMQServerLogger extends BasicLogger { void serverStopped(String version, SimpleString nodeId, String uptime); @LogMessage(level = Logger.Level.INFO) - @Message(id = 221003, value = "Deploying queue {0} on address {1}", format = Message.Format.MESSAGE_FORMAT) - void deployQueue(String queueName, String addressName); + @Message(id = 221003, value = "Deploying {2} queue {0} on address {1}", format = Message.Format.MESSAGE_FORMAT) + void deployQueue(String queueName, String addressName, String routingType); @LogMessage(level = Logger.Level.INFO) @Message(id = 221004, value = "{0}", format = Message.Format.MESSAGE_FORMAT) @@ -426,6 +426,10 @@ void slowConsumerDetected(String sessionID, @Message(id = 221079, value = "Ignoring prepare on xid as already called : {0}", format = Message.Format.MESSAGE_FORMAT) void ignoringPrepareOnXidAlreadyCalled(String xid); + @LogMessage(level = Logger.Level.INFO) + @Message(id = 221080, value = "Deploying address {0} supporting {1}", format = Message.Format.MESSAGE_FORMAT) + void deployAddress(String addressName, String routingTypes); + @LogMessage(level = Logger.Level.WARN) @Message(id = 222000, value = "ActiveMQServer is being finalized and has not been stopped. Please remember to stop the server before letting it go out of scope", format = Message.Format.MESSAGE_FORMAT) 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 054006b9f11..62d3c6e91c8 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 @@ -2744,6 +2744,7 @@ private void deployAddressesFromConfiguration(Configuration configuration) throw for (CoreAddressConfiguration config : configuration.getAddressConfigurations()) { AddressInfo info = new AddressInfo(SimpleString.toSimpleString(config.getName()), config.getRoutingTypes()); addOrUpdateAddressInfo(info); + ActiveMQServerLogger.LOGGER.deployAddress(config.getName(), config.getRoutingTypes().toString()); deployQueuesFromListCoreQueueConfiguration(config.getQueueConfigurations()); } } @@ -2751,7 +2752,7 @@ private void deployAddressesFromConfiguration(Configuration configuration) throw private void deployQueuesFromListCoreQueueConfiguration(List queues) throws Exception { for (CoreQueueConfiguration config : queues) { SimpleString queueName = SimpleString.toSimpleString(config.getName()); - ActiveMQServerLogger.LOGGER.deployQueue(config.getName(), config.getAddress()); + ActiveMQServerLogger.LOGGER.deployQueue(config.getName(), config.getAddress(), config.getRoutingType().toString()); AddressSettings as = addressSettingsRepository.getMatch(config.getAddress()); // determine if there is an address::queue match; update it if so int maxConsumers = config.getMaxConsumers() == null ? as.getDefaultMaxConsumers() : config.getMaxConsumers(); From 93cffedcba9d293c24b3497a1aff1de491bbf39c Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 28 Aug 2018 17:35:57 -0400 Subject: [PATCH 179/207] ARTEMIS-2023 Avoiding boolean on every message for 1x and tests --- .../jms/client/ActiveMQMapMessage.java | 2 +- .../artemis/jms/client/ActiveMQMessage.java | 57 ++----- .../jms/client/ActiveMQMessageConsumer.java | 6 +- .../jms/client/ActiveMQQueueBrowser.java | 7 +- .../artemis/jms/client/ActiveMQSession.java | 70 ++++++-- .../jms/client/ActiveMQStreamMessage.java | 2 +- .../jms/client/JMSMessageListenerWrapper.java | 13 +- .../ActiveMQBytesCompatibleMessage.java | 57 +++++++ .../ActiveMQCompatibleMessage.java | 159 ++++++++++++++++++ .../ActiveMQMapCompatibleMessage.java | 58 +++++++ .../ActiveMQObjectCompatibleMessage.java | 61 +++++++ .../ActiveMQStreamCompatibleMessage.java | 59 +++++++ .../ActiveMQTextCompabileMessage.java | 50 ++++++ .../artemis/ra/inflow/ActiveMQActivation.java | 5 + .../ra/inflow/ActiveMQMessageHandler.java | 13 +- .../tests/compatibility/GroovyRun.java | 6 + .../validateClient.groovy | 2 + .../ReplyToTest/replyToReceive.groovy | 87 ++++++++++ .../resources/ReplyToTest/replyToSend.groovy | 70 ++++++++ .../addressConfig/artemisServer.groovy | 2 +- .../addressConfig/receiveMessages.groovy | 2 +- .../addressConfig/sendMessagesAddress.groovy | 2 +- .../exportimport/artemisServer.groovy | 2 +- .../main/resources/exportimport/export.groovy | 1 + .../resources/exportimport/export1X.groovy | 1 + .../main/resources/exportimport/import.groovy | 1 + .../journalcompatibility/forcepaging.groovy | 2 + .../journalcompatibility/ispaging.groovy | 2 + .../oldAddressSpace/receiveMessages.groovy | 2 + .../prefixSendAckTest/artemisServer.groovy | 2 +- .../prefixSendAckTest/sendAckMessages.groovy | 2 +- .../sendAckTest/sendAckMessages.groovy | 2 +- .../src/main/resources/serial/cfserial.groovy | 2 +- .../main/resources/serial/jbmserial.groovy | 2 +- .../src/main/resources/serial/serial.groovy | 2 +- .../ActiveMQJMSClientCompatibilityTest.java | 3 +- .../compatibility/AddressConfigTest.java | 3 +- ...FactoryConfigurationSerializationTest.java | 3 +- .../tests/compatibility/ExportImportTest.java | 3 +- .../compatibility/HQClientTopologyTest.java | 3 +- .../tests/compatibility/HQFailoverTest.java | 3 +- .../JournalCompatibilityTest.java | 3 +- .../artemis/tests/compatibility/MeshTest.java | 11 +- .../compatibility/OldAddressSpaceTest.java | 3 +- .../compatibility/PrefixSendAckTest.java | 3 +- .../tests/compatibility/ReplyToTest.java | 134 +++++++++++++++ .../tests/compatibility/SendAckTest.java | 3 +- .../compatibility/SerializationTest.java | 3 +- .../ClasspathBase.java} | 24 ++- .../ServerBase.java} | 6 +- .../VersionedBase.java} | 26 ++- 51 files changed, 936 insertions(+), 111 deletions(-) create mode 100644 artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQBytesCompatibleMessage.java create mode 100644 artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQCompatibleMessage.java create mode 100644 artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQMapCompatibleMessage.java create mode 100644 artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQObjectCompatibleMessage.java create mode 100644 artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQStreamCompatibleMessage.java create mode 100644 artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQTextCompabileMessage.java create mode 100644 tests/compatibility-tests/src/main/resources/ReplyToTest/replyToReceive.groovy create mode 100644 tests/compatibility-tests/src/main/resources/ReplyToTest/replyToSend.groovy create mode 100644 tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ReplyToTest.java rename tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/{ClasspathBaseTest.java => base/ClasspathBase.java} (85%) rename tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/{ServerBaseTest.java => base/ServerBase.java} (86%) rename tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/{VersionedBaseTest.java => base/VersionedBase.java} (75%) diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMapMessage.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMapMessage.java index 9749328b1f8..557b0b8256a 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMapMessage.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMapMessage.java @@ -38,7 +38,7 @@ /** * ActiveMQ Artemis implementation of a JMS MapMessage. */ -public final class ActiveMQMapMessage extends ActiveMQMessage implements MapMessage { +public class ActiveMQMapMessage extends ActiveMQMessage implements MapMessage { // Constants ----------------------------------------------------- public static final byte TYPE = Message.MAP_TYPE; diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessage.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessage.java index ff7da00a316..a3360ef1350 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessage.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessage.java @@ -197,7 +197,7 @@ public static ActiveMQMessage createMessage(final ClientMessage message, private String msgID; // Cache it - private Destination replyTo; + protected Destination replyTo; // Cache it private String jmsCorrelationID; @@ -209,8 +209,6 @@ public static ActiveMQMessage createMessage(final ClientMessage message, private boolean clientAck; - private boolean enable1xPrefixes; - private long jmsDeliveryTime; // Constructors -------------------------------------------------- @@ -366,23 +364,11 @@ public String getJMSCorrelationID() throws JMSException { @Override public Destination getJMSReplyTo() throws JMSException { if (replyTo == null) { - SimpleString address = MessageUtil.getJMSReplyTo(message); - if (address != null) { - String name = address.toString(); - - // swap the old prefixes for the new ones so the proper destination type gets created - if (enable1xPrefixes) { - if (address.startsWith(OLD_QUEUE_QUALIFIED_PREFIX)) { - name = address.subSeq(OLD_QUEUE_QUALIFIED_PREFIX.length(), address.length()).toString(); - } else if (address.startsWith(OLD_TEMP_QUEUE_QUALIFED_PREFIX)) { - name = address.subSeq(OLD_TEMP_QUEUE_QUALIFED_PREFIX.length(), address.length()).toString(); - } else if (address.startsWith(OLD_TOPIC_QUALIFIED_PREFIX)) { - name = address.subSeq(OLD_TOPIC_QUALIFIED_PREFIX.length(), address.length()).toString(); - } else if (address.startsWith(OLD_TEMP_TOPIC_QUALIFED_PREFIX)) { - name = address.subSeq(OLD_TEMP_TOPIC_QUALIFED_PREFIX.length(), address.length()).toString(); - } - } - replyTo = ActiveMQDestination.fromPrefixedName(address.toString(), name); + + SimpleString repl = MessageUtil.getJMSReplyTo(message); + + if (repl != null) { + replyTo = ActiveMQDestination.fromPrefixedName(repl.toString()); } } return replyTo; @@ -417,23 +403,20 @@ public void setJMSReplyTo(final Destination dest) throws JMSException { } } + protected SimpleString checkPrefix(SimpleString address) { + return address; + } + + protected SimpleString checkPrefixStr(SimpleString address) { + return address; + } + + @Override public Destination getJMSDestination() throws JMSException { if (dest == null) { SimpleString address = message.getAddressSimpleString(); - SimpleString name = address; - - if (address != null & enable1xPrefixes) { - if (address.startsWith(PacketImpl.OLD_QUEUE_PREFIX)) { - name = address.subSeq(PacketImpl.OLD_QUEUE_PREFIX.length(), address.length()); - } else if (address.startsWith(PacketImpl.OLD_TEMP_QUEUE_PREFIX)) { - name = address.subSeq(PacketImpl.OLD_TEMP_QUEUE_PREFIX.length(), address.length()); - } else if (address.startsWith(PacketImpl.OLD_TOPIC_PREFIX)) { - name = address.subSeq(PacketImpl.OLD_TOPIC_PREFIX.length(), address.length()); - } else if (address.startsWith(PacketImpl.OLD_TEMP_TOPIC_PREFIX)) { - name = address.subSeq(PacketImpl.OLD_TEMP_TOPIC_PREFIX.length(), address.length()); - } - } + SimpleString changedAddress = checkPrefix(address); if (address == null) { dest = null; @@ -445,8 +428,8 @@ public Destination getJMSDestination() throws JMSException { dest = (ActiveMQDestination) ActiveMQDestination.fromPrefixedName(address.toString()); } - if (name != null) { - ((ActiveMQDestination) dest).setName(name.toString()); + if (changedAddress != null) { + ((ActiveMQDestination) dest).setName(changedAddress.toString()); } } @@ -903,10 +886,6 @@ public boolean waitCompletionOnStream(final long timeWait) throws JMSException { } } - public void setEnable1xPrefixes(boolean enable1xPrefixes) { - this.enable1xPrefixes = enable1xPrefixes; - } - @Override public String toString() { StringBuffer sb = new StringBuffer("ActiveMQMessage["); diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageConsumer.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageConsumer.java index 8fabe8b1fd3..dac8e57ca6e 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageConsumer.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageConsumer.java @@ -36,6 +36,7 @@ import org.apache.activemq.artemis.api.jms.ActiveMQJMSConstants; import org.apache.activemq.artemis.core.client.ActiveMQClientLogger; import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal; +import org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQCompatibleMessage; /** * ActiveMQ Artemis implementation of a JMS MessageConsumer. @@ -218,10 +219,11 @@ private ActiveMQMessage getMessage(final long timeout, final boolean noWait) thr boolean needSession = ackMode == Session.CLIENT_ACKNOWLEDGE || ackMode == ActiveMQJMSConstants.INDIVIDUAL_ACKNOWLEDGE || coreMessage.getType() == ActiveMQObjectMessage.TYPE; - jmsMsg = ActiveMQMessage.createMessage(coreMessage, needSession ? coreSession : null, options); if (session.isEnable1xPrefixes()) { - jmsMsg.setEnable1xPrefixes(true); + jmsMsg = ActiveMQCompatibleMessage.createMessage(coreMessage, needSession ? coreSession : null, options); + } else { + jmsMsg = ActiveMQMessage.createMessage(coreMessage, needSession ? coreSession : null, options); } try { diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueueBrowser.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueueBrowser.java index 716d044d45a..810166ce8f2 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueueBrowser.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQQueueBrowser.java @@ -27,6 +27,7 @@ import org.apache.activemq.artemis.api.core.client.ClientConsumer; import org.apache.activemq.artemis.api.core.client.ClientMessage; import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQCompatibleMessage; import org.apache.activemq.artemis.utils.SelectorTranslator; /** @@ -141,10 +142,10 @@ public ActiveMQMessage nextElement() { if (hasMoreElements()) { ClientMessage next = current; current = null; - msg = ActiveMQMessage.createMessage(next, session, options); - if (enable1xPrefixes) { - msg.setEnable1xPrefixes(true); + msg = ActiveMQCompatibleMessage.createMessage(next, session, options); + } else { + msg = ActiveMQMessage.createMessage(next, session, options); } try { diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java index 528310fa62e..95d3608d0e9 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java @@ -62,6 +62,12 @@ import org.apache.activemq.artemis.api.core.client.ClientSession.QueueQuery; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl; +import org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQBytesCompatibleMessage; +import org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQCompatibleMessage; +import org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQMapCompatibleMessage; +import org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQObjectCompatibleMessage; +import org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQStreamCompatibleMessage; +import org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQTextCompabileMessage; import org.apache.activemq.artemis.selector.filter.FilterException; import org.apache.activemq.artemis.selector.impl.SelectorParser; import org.apache.activemq.artemis.utils.SelectorTranslator; @@ -144,8 +150,12 @@ protected ActiveMQSession(final ConnectionFactoryOptions options, public BytesMessage createBytesMessage() throws JMSException { checkClosed(); - ActiveMQBytesMessage message = new ActiveMQBytesMessage(session); - message.setEnable1xPrefixes(enable1xPrefixes); + ActiveMQBytesMessage message; + if (enable1xPrefixes) { + message = new ActiveMQBytesCompatibleMessage(session); + } else { + message = new ActiveMQBytesMessage(session); + } return message; } @@ -153,8 +163,12 @@ public BytesMessage createBytesMessage() throws JMSException { public MapMessage createMapMessage() throws JMSException { checkClosed(); - ActiveMQMapMessage message = new ActiveMQMapMessage(session); - message.setEnable1xPrefixes(enable1xPrefixes); + ActiveMQMapMessage message; + if (enable1xPrefixes) { + message = new ActiveMQMapCompatibleMessage(session); + } else { + message = new ActiveMQMapMessage(session); + } return message; } @@ -162,8 +176,12 @@ public MapMessage createMapMessage() throws JMSException { public Message createMessage() throws JMSException { checkClosed(); - ActiveMQMessage message = new ActiveMQMessage(session); - message.setEnable1xPrefixes(enable1xPrefixes); + ActiveMQMessage message; + if (enable1xPrefixes) { + message = new ActiveMQCompatibleMessage(session); + } else { + message = new ActiveMQMessage(session); + } return message; } @@ -171,8 +189,12 @@ public Message createMessage() throws JMSException { public ObjectMessage createObjectMessage() throws JMSException { checkClosed(); - ActiveMQObjectMessage message = new ActiveMQObjectMessage(session, options); - message.setEnable1xPrefixes(enable1xPrefixes); + ActiveMQObjectMessage message; + if (enable1xPrefixes) { + message = new ActiveMQObjectCompatibleMessage(session, options); + } else { + message = new ActiveMQObjectMessage(session, options); + } return message; } @@ -180,9 +202,13 @@ public ObjectMessage createObjectMessage() throws JMSException { public ObjectMessage createObjectMessage(final Serializable object) throws JMSException { checkClosed(); - ActiveMQObjectMessage msg = new ActiveMQObjectMessage(session, options); + ActiveMQObjectMessage msg; + if (enable1xPrefixes) { + msg = new ActiveMQObjectCompatibleMessage(session, options); + } else { + msg = new ActiveMQObjectMessage(session, options); + } msg.setObject(object); - msg.setEnable1xPrefixes(enable1xPrefixes); return msg; } @@ -191,8 +217,12 @@ public ObjectMessage createObjectMessage(final Serializable object) throws JMSEx public StreamMessage createStreamMessage() throws JMSException { checkClosed(); - ActiveMQStreamMessage message = new ActiveMQStreamMessage(session); - message.setEnable1xPrefixes(enable1xPrefixes); + ActiveMQStreamMessage message; + if (enable1xPrefixes) { + message = new ActiveMQStreamMessage(session); + } else { + message = new ActiveMQStreamCompatibleMessage(session); + } return message; } @@ -200,9 +230,13 @@ public StreamMessage createStreamMessage() throws JMSException { public TextMessage createTextMessage() throws JMSException { checkClosed(); - ActiveMQTextMessage msg = new ActiveMQTextMessage(session); + ActiveMQTextMessage msg; + if (enable1xPrefixes) { + msg = new ActiveMQTextCompabileMessage(session); + } else { + msg = new ActiveMQTextMessage(session); + } msg.setText(null); - msg.setEnable1xPrefixes(enable1xPrefixes); return msg; } @@ -211,9 +245,13 @@ public TextMessage createTextMessage() throws JMSException { public TextMessage createTextMessage(final String text) throws JMSException { checkClosed(); - ActiveMQTextMessage msg = new ActiveMQTextMessage(session); + ActiveMQTextMessage msg; + if (enable1xPrefixes) { + msg = new ActiveMQTextCompabileMessage(session); + } else { + msg = new ActiveMQTextMessage(session); + } msg.setText(text); - msg.setEnable1xPrefixes(enable1xPrefixes); return msg; } diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQStreamMessage.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQStreamMessage.java index 1c70c5bde26..6904df40f1e 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQStreamMessage.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQStreamMessage.java @@ -44,7 +44,7 @@ /** * ActiveMQ Artemis implementation of a JMS StreamMessage. */ -public final class ActiveMQStreamMessage extends ActiveMQMessage implements StreamMessage { +public class ActiveMQStreamMessage extends ActiveMQMessage implements StreamMessage { public static final byte TYPE = Message.STREAM_TYPE; diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/JMSMessageListenerWrapper.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/JMSMessageListenerWrapper.java index 0d2420b68d7..f24e90db026 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/JMSMessageListenerWrapper.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/JMSMessageListenerWrapper.java @@ -25,6 +25,7 @@ import org.apache.activemq.artemis.api.core.client.MessageHandler; import org.apache.activemq.artemis.api.jms.ActiveMQJMSConstants; import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal; +import org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQCompatibleMessage; public class JMSMessageListenerWrapper implements MessageHandler { @@ -72,7 +73,13 @@ protected JMSMessageListenerWrapper(final ConnectionFactoryOptions options, */ @Override public void onMessage(final ClientMessage message) { - ActiveMQMessage msg = ActiveMQMessage.createMessage(message, session.getCoreSession(), options); + ActiveMQMessage msg; + + if (session.isEnable1xPrefixes()) { + msg = ActiveMQCompatibleMessage.createMessage(message, session.getCoreSession(), options); + } else { + msg = ActiveMQMessage.createMessage(message, session.getCoreSession(), options); + } if (individualACK) { msg.setIndividualAcknowledge(); @@ -82,10 +89,6 @@ public void onMessage(final ClientMessage message) { msg.setClientAcknowledge(); } - if (session.isEnable1xPrefixes()) { - msg.setEnable1xPrefixes(true); - } - try { msg.doBeforeReceive(); } catch (Exception e) { diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQBytesCompatibleMessage.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQBytesCompatibleMessage.java new file mode 100644 index 00000000000..626b5a5357a --- /dev/null +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQBytesCompatibleMessage.java @@ -0,0 +1,57 @@ +/* + * 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.artemis.jms.client.compatible1X; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.JMSException; + +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.jms.client.ActiveMQBytesMessage; + +public class ActiveMQBytesCompatibleMessage extends ActiveMQBytesMessage { + + @Override + protected SimpleString checkPrefix(SimpleString address) { + return ActiveMQCompatibleMessage.checkPrefix1X(address); + } + + + @Override + public Destination getJMSReplyTo() throws JMSException { + if (replyTo == null) { + replyTo = ActiveMQCompatibleMessage.findCompatibleReplyTo(message); + } + return replyTo; + } + + + public ActiveMQBytesCompatibleMessage(ClientSession session) { + super(session); + } + + protected ActiveMQBytesCompatibleMessage(ClientMessage message, ClientSession session) { + super(message, session); + } + + public ActiveMQBytesCompatibleMessage(BytesMessage foreign, ClientSession session) throws JMSException { + super(foreign, session); + } +} diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQCompatibleMessage.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQCompatibleMessage.java new file mode 100644 index 00000000000..1b21cbfa22a --- /dev/null +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQCompatibleMessage.java @@ -0,0 +1,159 @@ +/* + * 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.artemis.jms.client.compatible1X; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.JMSRuntimeException; +import javax.jms.Message; + +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl; +import org.apache.activemq.artemis.jms.client.ActiveMQBytesMessage; +import org.apache.activemq.artemis.jms.client.ActiveMQDestination; +import org.apache.activemq.artemis.jms.client.ActiveMQMapMessage; +import org.apache.activemq.artemis.jms.client.ActiveMQMessage; +import org.apache.activemq.artemis.jms.client.ActiveMQObjectMessage; +import org.apache.activemq.artemis.jms.client.ActiveMQStreamMessage; +import org.apache.activemq.artemis.jms.client.ActiveMQTextMessage; +import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions; +import org.apache.activemq.artemis.reader.MessageUtil; + +public class ActiveMQCompatibleMessage extends ActiveMQMessage { + + public ActiveMQCompatibleMessage(byte type, ClientSession session) { + super(type, session); + } + + public ActiveMQCompatibleMessage(ClientSession session) { + super(session); + } + + public ActiveMQCompatibleMessage(ClientMessage message, ClientSession session) { + super(message, session); + } + + public ActiveMQCompatibleMessage(Message foreign, ClientSession session) throws JMSException { + super(foreign, session); + } + + public ActiveMQCompatibleMessage() { + } + + public ActiveMQCompatibleMessage(Message foreign, byte type, ClientSession session) throws JMSException { + super(foreign, type, session); + } + + @Override + public Destination getJMSReplyTo() throws JMSException { + if (replyTo == null) { + replyTo = findCompatibleReplyTo(message); + } + return replyTo; + } + + public static Destination findCompatibleReplyTo(ClientMessage message) { + SimpleString address = MessageUtil.getJMSReplyTo(message); + if (address != null) { + String name = address.toString(); + + // swap the old prefixes for the new ones so the proper destination type gets created + if (address.startsWith(OLD_QUEUE_QUALIFIED_PREFIX)) { + name = address.subSeq(OLD_QUEUE_QUALIFIED_PREFIX.length(), address.length()).toString(); + } else if (address.startsWith(OLD_TEMP_QUEUE_QUALIFED_PREFIX)) { + name = address.subSeq(OLD_TEMP_QUEUE_QUALIFED_PREFIX.length(), address.length()).toString(); + } else if (address.startsWith(OLD_TOPIC_QUALIFIED_PREFIX)) { + name = address.subSeq(OLD_TOPIC_QUALIFIED_PREFIX.length(), address.length()).toString(); + } else if (address.startsWith(OLD_TEMP_TOPIC_QUALIFED_PREFIX)) { + name = address.subSeq(OLD_TEMP_TOPIC_QUALIFED_PREFIX.length(), address.length()).toString(); + } + return ActiveMQDestination.fromPrefixedName(address.toString(), name); + } + + return null; + } + + @Override + public SimpleString checkPrefix(SimpleString address) { + return checkPrefix1X(address); + } + + protected static SimpleString checkPrefix1X(SimpleString address) { + if (address != null) { + if (address.startsWith(PacketImpl.OLD_QUEUE_PREFIX)) { + return address.subSeq(PacketImpl.OLD_QUEUE_PREFIX.length(), address.length()); + } else if (address.startsWith(PacketImpl.OLD_TEMP_QUEUE_PREFIX)) { + return address.subSeq(PacketImpl.OLD_TEMP_QUEUE_PREFIX.length(), address.length()); + } else if (address.startsWith(PacketImpl.OLD_TOPIC_PREFIX)) { + return address.subSeq(PacketImpl.OLD_TOPIC_PREFIX.length(), address.length()); + } else if (address.startsWith(PacketImpl.OLD_TEMP_TOPIC_PREFIX)) { + return address.subSeq(PacketImpl.OLD_TEMP_TOPIC_PREFIX.length(), address.length()); + } + } + + return null; + } + + public static ActiveMQMessage createMessage(final ClientMessage message, + final ClientSession session, + final ConnectionFactoryOptions options) { + int type = message.getType(); + + ActiveMQMessage msg; + + switch (type) { + case ActiveMQMessage.TYPE: // 0 + { + msg = new ActiveMQCompatibleMessage(message, session); + break; + } + case ActiveMQBytesMessage.TYPE: // 4 + { + msg = new ActiveMQBytesCompatibleMessage(message, session); + break; + } + case ActiveMQMapMessage.TYPE: // 5 + { + msg = new ActiveMQMapCompatibleMessage(message, session); + break; + } + case ActiveMQObjectMessage.TYPE: { + msg = new ActiveMQObjectCompatibleMessage(message, session, options); + break; + } + case ActiveMQStreamMessage.TYPE: // 6 + { + msg = new ActiveMQStreamCompatibleMessage(message, session); + break; + } + case ActiveMQTextMessage.TYPE: // 3 + { + msg = new ActiveMQTextCompabileMessage(message, session); + break; + } + default: { + throw new JMSRuntimeException("Invalid message type " + type); + } + } + + return msg; + } + +} diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQMapCompatibleMessage.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQMapCompatibleMessage.java new file mode 100644 index 00000000000..2d6e5763320 --- /dev/null +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQMapCompatibleMessage.java @@ -0,0 +1,58 @@ +/* + * 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.artemis.jms.client.compatible1X; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MapMessage; + +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.jms.client.ActiveMQMapMessage; + +public class ActiveMQMapCompatibleMessage extends ActiveMQMapMessage { + + @Override + protected SimpleString checkPrefix(SimpleString address) { + return ActiveMQCompatibleMessage.checkPrefix1X(address); + } + + @Override + public Destination getJMSReplyTo() throws JMSException { + if (replyTo == null) { + replyTo = ActiveMQCompatibleMessage.findCompatibleReplyTo(message); + } + return replyTo; + } + + public ActiveMQMapCompatibleMessage(ClientSession session) { + super(session); + } + + public ActiveMQMapCompatibleMessage(ClientMessage message, ClientSession session) { + super(message, session); + } + + public ActiveMQMapCompatibleMessage() { + } + + public ActiveMQMapCompatibleMessage(MapMessage foreign, ClientSession session) throws JMSException { + super(foreign, session); + } +} diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQObjectCompatibleMessage.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQObjectCompatibleMessage.java new file mode 100644 index 00000000000..13a9d7dc7b3 --- /dev/null +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQObjectCompatibleMessage.java @@ -0,0 +1,61 @@ +/* + * 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.artemis.jms.client.compatible1X; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.ObjectMessage; + +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.jms.client.ActiveMQObjectMessage; +import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions; + +public class ActiveMQObjectCompatibleMessage extends ActiveMQObjectMessage { + + @Override + protected SimpleString checkPrefix(SimpleString address) { + return ActiveMQCompatibleMessage.checkPrefix1X(address); + } + + + @Override + public Destination getJMSReplyTo() throws JMSException { + if (replyTo == null) { + replyTo = ActiveMQCompatibleMessage.findCompatibleReplyTo(message); + } + return replyTo; + } + + public ActiveMQObjectCompatibleMessage(ClientSession session, ConnectionFactoryOptions options) { + super(session, options); + } + + public ActiveMQObjectCompatibleMessage(ClientMessage message, + ClientSession session, + ConnectionFactoryOptions options) { + super(message, session, options); + } + + public ActiveMQObjectCompatibleMessage(ObjectMessage foreign, + ClientSession session, + ConnectionFactoryOptions options) throws JMSException { + super(foreign, session, options); + } +} diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQStreamCompatibleMessage.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQStreamCompatibleMessage.java new file mode 100644 index 00000000000..bb2fda6d2bc --- /dev/null +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQStreamCompatibleMessage.java @@ -0,0 +1,59 @@ +/* + * 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.artemis.jms.client.compatible1X; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.StreamMessage; + +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.jms.client.ActiveMQStreamMessage; + +public class ActiveMQStreamCompatibleMessage extends ActiveMQStreamMessage { + + @Override + protected SimpleString checkPrefix(SimpleString address) { + return ActiveMQCompatibleMessage.checkPrefix1X(address); + } + + + @Override + public Destination getJMSReplyTo() throws JMSException { + if (replyTo == null) { + replyTo = ActiveMQCompatibleMessage.findCompatibleReplyTo(message); + } + return replyTo; + } + + public ActiveMQStreamCompatibleMessage(ClientSession session) { + super(session); + } + + public ActiveMQStreamCompatibleMessage(ClientMessage message, ClientSession session) { + super(message, session); + } + + public ActiveMQStreamCompatibleMessage(StreamMessage foreign, ClientSession session) throws JMSException { + super(foreign, session); + } + + public ActiveMQStreamCompatibleMessage() { + } +} diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQTextCompabileMessage.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQTextCompabileMessage.java new file mode 100644 index 00000000000..451c5828061 --- /dev/null +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/compatible1X/ActiveMQTextCompabileMessage.java @@ -0,0 +1,50 @@ +/* + * 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.artemis.jms.client.compatible1X; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.TextMessage; + +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.jms.client.ActiveMQTextMessage; + +public class ActiveMQTextCompabileMessage extends ActiveMQTextMessage { + + + @Override + public Destination getJMSReplyTo() throws JMSException { + if (replyTo == null) { + replyTo = ActiveMQCompatibleMessage.findCompatibleReplyTo(message); + } + return replyTo; + } + + public ActiveMQTextCompabileMessage(ClientSession session) { + super(session); + } + + public ActiveMQTextCompabileMessage(ClientMessage message, ClientSession session) { + super(message, session); + } + + public ActiveMQTextCompabileMessage(TextMessage foreign, ClientSession session) throws JMSException { + super(foreign, session); + } +} diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java index d6013e32b23..b0f0afff8e0 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java @@ -384,6 +384,11 @@ protected synchronized void teardown(boolean useInterrupt) { for (ActiveMQMessageHandler handler : handlersCopy) { Thread interruptThread = handler.getCurrentThread(); if (interruptThread != null) { + try { + logger.tracef("Interrupting thread %s", interruptThread.getName()); + } catch (Throwable justLog) { + logger.warn(justLog); + } try { interruptThread.interrupt(); } catch (Throwable e) { diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java index 33c64454de4..ef23d50c3b1 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQMessageHandler.java @@ -41,6 +41,7 @@ import org.apache.activemq.artemis.jms.client.ActiveMQDestination; import org.apache.activemq.artemis.jms.client.ActiveMQMessage; import org.apache.activemq.artemis.jms.client.ConnectionFactoryOptions; +import org.apache.activemq.artemis.jms.client.compatible1X.ActiveMQCompatibleMessage; import org.apache.activemq.artemis.ra.ActiveMQRALogger; import org.apache.activemq.artemis.ra.ActiveMQResourceAdapter; import org.apache.activemq.artemis.service.extensions.ServiceUtils; @@ -86,6 +87,8 @@ public class ActiveMQMessageHandler implements MessageHandler, FailoverEventList private volatile boolean connected; + private boolean enable1XPrefix; + public ActiveMQMessageHandler(final ConnectionFactoryOptions options, final ActiveMQActivation activation, final TransactionManager tm, @@ -105,6 +108,8 @@ public void setup() throws Exception { logger.trace("setup()"); } + this.enable1XPrefix = activation.getConnectionFactory().isEnable1xPrefixes(); + ActiveMQActivationSpec spec = activation.getActivationSpec(); String selector = spec.getMessageSelector(); @@ -281,8 +286,12 @@ public void onMessage(final ClientMessage message) { logger.trace("onMessage(" + message + ")"); } - ActiveMQMessage msg = ActiveMQMessage.createMessage(message, session, options); - msg.setEnable1xPrefixes(activation.getConnectionFactory().isEnable1xPrefixes()); + ActiveMQMessage msg; + if (enable1XPrefix) { + msg = ActiveMQCompatibleMessage.createMessage(message, session, options); + } else { + msg = ActiveMQMessage.createMessage(message, session, options); + } boolean beforeDelivery = false; diff --git a/tests/compatibility-tests/src/main/java/org/apache/activemq/artemis/tests/compatibility/GroovyRun.java b/tests/compatibility-tests/src/main/java/org/apache/activemq/artemis/tests/compatibility/GroovyRun.java index 5efa3d30aef..3be275b7c2e 100644 --- a/tests/compatibility-tests/src/main/java/org/apache/activemq/artemis/tests/compatibility/GroovyRun.java +++ b/tests/compatibility-tests/src/main/java/org/apache/activemq/artemis/tests/compatibility/GroovyRun.java @@ -39,6 +39,11 @@ public class GroovyRun { public static Binding binding = new Binding(); public static GroovyShell shell = new GroovyShell(binding); + public static void clear() { + binding = new Binding(); + shell = new GroovyShell(binding); + } + /** * This can be called from the scripts as well. * The scripts will use this method instead of its own groovy method. @@ -68,6 +73,7 @@ public static Object evaluate(String script, return shell.evaluate(scriptURI); } + public static void setVariable(String name, Object arg) { binding.setVariable(name, arg); } diff --git a/tests/compatibility-tests/src/main/resources/ActiveMQJMSClientCompatibilityTest/validateClient.groovy b/tests/compatibility-tests/src/main/resources/ActiveMQJMSClientCompatibilityTest/validateClient.groovy index 400a69eb824..d1c77f859d9 100644 --- a/tests/compatibility-tests/src/main/resources/ActiveMQJMSClientCompatibilityTest/validateClient.groovy +++ b/tests/compatibility-tests/src/main/resources/ActiveMQJMSClientCompatibilityTest/validateClient.groovy @@ -1,3 +1,5 @@ +package ActiveMQJMSClientCompatibilityTest + import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient import org.apache.activemq.artemis.jms.client.ActiveMQQueue import org.apache.activemq.artemis.jms.client.ActiveMQTopic diff --git a/tests/compatibility-tests/src/main/resources/ReplyToTest/replyToReceive.groovy b/tests/compatibility-tests/src/main/resources/ReplyToTest/replyToReceive.groovy new file mode 100644 index 00000000000..156cbdb0684 --- /dev/null +++ b/tests/compatibility-tests/src/main/resources/ReplyToTest/replyToReceive.groovy @@ -0,0 +1,87 @@ +package ReplyToTest + +import org.apache.activemq.artemis.api.core.client.ActiveMQClient +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient +import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory +import org.apache.activemq.artemis.jms.client.ActiveMQQueue +import org.apache.activemq.artemis.tests.compatibility.GroovyRun + +import javax.jms.* + +/* + * 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. + */ + +cf = new ActiveMQConnectionFactory("tcp://localhost:61616?confirmationWindowSize=1048576&blockOnDurableSend=false&ha=true&reconnectAttempts=-1&retryInterval=100"); +Connection connection = cf.createConnection(); +connection.start(); +Session session = connection.createSession(true, Session.SESSION_TRANSACTED); +Queue queue = session.createQueue("queue"); +QueueBrowser browser = session.createBrowser(queue); + +Enumeration messageEnumeration = browser.getEnumeration(); + +ArrayList messages = new ArrayList<>(); + +while (messageEnumeration.hasMoreElements()) { + messages.add(messageEnumeration.nextElement()); +} + +check(messages); + +MessageConsumer consumer = session.createConsumer(queue); +messages.clear(); + +while(true) { + Message message = consumer.receiveNoWait(); + if (message == null) { + break; + } + messages.add(message); +} + +check(messages); + +connection.close(); + +void check(List messages) { + Iterator iterator = messages.iterator(); + Message bareMessage = iterator.next(); + checkMessage(bareMessage); + + BytesMessage bytesMessage = iterator.next(); + checkMessage(bytesMessage); + + + MapMessage mapMessage = iterator.next(); + checkMessage(mapMessage); + + ObjectMessage objectMessage = iterator.next(); + checkMessage(objectMessage); + + StreamMessage streamMessage = iterator.next(); + checkMessage(streamMessage); + + TextMessage textMessage = iterator.next(); + checkMessage(objectMessage); +} + + +void checkMessage(Message message) { + ActiveMQQueue queue = message.getJMSReplyTo(); + GroovyRun.assertEquals("jms.queue.t1", queue.getAddress()); + GroovyRun.assertEquals("t1", queue.getName()); +} diff --git a/tests/compatibility-tests/src/main/resources/ReplyToTest/replyToSend.groovy b/tests/compatibility-tests/src/main/resources/ReplyToTest/replyToSend.groovy new file mode 100644 index 00000000000..478699e8e55 --- /dev/null +++ b/tests/compatibility-tests/src/main/resources/ReplyToTest/replyToSend.groovy @@ -0,0 +1,70 @@ +package ReplyToTest + +import org.apache.activemq.artemis.api.core.client.ActiveMQClient +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient +import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory +import org.apache.activemq.artemis.jms.client.ActiveMQQueue +import org.apache.activemq.artemis.jms.client.ActiveMQTopic +import org.apache.activemq.artemis.tests.compatibility.GroovyRun + +import javax.jms.* + +/* + * 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. + */ + +ActiveMQQueue queue = (ActiveMQQueue) ActiveMQJMSClient.createQueue("q1"); +GroovyRun.assertEquals("jms.queue.q1", queue.getAddress()); +GroovyRun.assertEquals("q1", queue.getQueueName()); +ActiveMQTopic topic = (ActiveMQTopic) ActiveMQJMSClient.createTopic("t1"); +GroovyRun.assertEquals("jms.topic.t1", topic.getAddress()); +GroovyRun.assertEquals("t1", topic.getTopicName()); + +cf = new ActiveMQConnectionFactory("tcp://localhost:61616?confirmationWindowSize=1048576&blockOnDurableSend=false&ha=true&reconnectAttempts=-1&retryInterval=100"); +Connection connection = cf.createConnection(); +Session session = connection.createSession(true, Session.SESSION_TRANSACTED); +queue = session.createQueue("queue"); +replyToQueue = ActiveMQJMSClient.createQueue("t1"); + +producer = session.createProducer(queue); +producer.setDeliveryMode(DeliveryMode.PERSISTENT); + +Message bareMessage = session.createMessage(); +send(bareMessage); + +BytesMessage bytesMessage = session.createBytesMessage(); +bytesMessage.writeBytes("hello".getBytes()); +send(bytesMessage); + + +MapMessage mapMessage = session.createMapMessage(); +send(mapMessage); + +ObjectMessage objectMessage = session.createObjectMessage("hello"); +send(objectMessage); + +send(session.createStreamMessage()); + +TextMessage textMessage = session.createTextMessage("May the force be with you"); +send(textMessage); + +session.commit(); + + +void send(Message message) { + message.setJMSReplyTo(replyToQueue); + producer.send(message); +} diff --git a/tests/compatibility-tests/src/main/resources/addressConfig/artemisServer.groovy b/tests/compatibility-tests/src/main/resources/addressConfig/artemisServer.groovy index c73994274f4..8221de75bb4 100644 --- a/tests/compatibility-tests/src/main/resources/addressConfig/artemisServer.groovy +++ b/tests/compatibility-tests/src/main/resources/addressConfig/artemisServer.groovy @@ -1,4 +1,4 @@ -package servers +package addressConfig /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with diff --git a/tests/compatibility-tests/src/main/resources/addressConfig/receiveMessages.groovy b/tests/compatibility-tests/src/main/resources/addressConfig/receiveMessages.groovy index 0dbf4bac8b5..441b8e58abf 100644 --- a/tests/compatibility-tests/src/main/resources/addressConfig/receiveMessages.groovy +++ b/tests/compatibility-tests/src/main/resources/addressConfig/receiveMessages.groovy @@ -1,4 +1,4 @@ -package meshTest +package addressConfig import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory import org.apache.activemq.artemis.tests.compatibility.GroovyRun diff --git a/tests/compatibility-tests/src/main/resources/addressConfig/sendMessagesAddress.groovy b/tests/compatibility-tests/src/main/resources/addressConfig/sendMessagesAddress.groovy index b75f8f5d094..7567b2c42c7 100644 --- a/tests/compatibility-tests/src/main/resources/addressConfig/sendMessagesAddress.groovy +++ b/tests/compatibility-tests/src/main/resources/addressConfig/sendMessagesAddress.groovy @@ -1,4 +1,4 @@ -package meshTest +package addressConfig import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory diff --git a/tests/compatibility-tests/src/main/resources/exportimport/artemisServer.groovy b/tests/compatibility-tests/src/main/resources/exportimport/artemisServer.groovy index 92d2a106169..0064493c5f7 100644 --- a/tests/compatibility-tests/src/main/resources/exportimport/artemisServer.groovy +++ b/tests/compatibility-tests/src/main/resources/exportimport/artemisServer.groovy @@ -1,4 +1,4 @@ -package servers +package exportimport /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with diff --git a/tests/compatibility-tests/src/main/resources/exportimport/export.groovy b/tests/compatibility-tests/src/main/resources/exportimport/export.groovy index bad99e7530c..2388fdd137a 100644 --- a/tests/compatibility-tests/src/main/resources/exportimport/export.groovy +++ b/tests/compatibility-tests/src/main/resources/exportimport/export.groovy @@ -1,3 +1,4 @@ +package exportimport /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with diff --git a/tests/compatibility-tests/src/main/resources/exportimport/export1X.groovy b/tests/compatibility-tests/src/main/resources/exportimport/export1X.groovy index 79b81c1285e..399f5c335b6 100644 --- a/tests/compatibility-tests/src/main/resources/exportimport/export1X.groovy +++ b/tests/compatibility-tests/src/main/resources/exportimport/export1X.groovy @@ -1,3 +1,4 @@ +package exportimport /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with diff --git a/tests/compatibility-tests/src/main/resources/exportimport/import.groovy b/tests/compatibility-tests/src/main/resources/exportimport/import.groovy index 39481d5f73f..b71871ceac5 100644 --- a/tests/compatibility-tests/src/main/resources/exportimport/import.groovy +++ b/tests/compatibility-tests/src/main/resources/exportimport/import.groovy @@ -1,3 +1,4 @@ +package exportimport /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with diff --git a/tests/compatibility-tests/src/main/resources/journalcompatibility/forcepaging.groovy b/tests/compatibility-tests/src/main/resources/journalcompatibility/forcepaging.groovy index 032bcc131ed..76e5f0dbff1 100644 --- a/tests/compatibility-tests/src/main/resources/journalcompatibility/forcepaging.groovy +++ b/tests/compatibility-tests/src/main/resources/journalcompatibility/forcepaging.groovy @@ -1,3 +1,5 @@ +package journalcompatibility + import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.core.server.Queue import org.apache.activemq.artemis.tests.compatibility.GroovyRun diff --git a/tests/compatibility-tests/src/main/resources/journalcompatibility/ispaging.groovy b/tests/compatibility-tests/src/main/resources/journalcompatibility/ispaging.groovy index a6dea7d9872..8bc8ed5a284 100644 --- a/tests/compatibility-tests/src/main/resources/journalcompatibility/ispaging.groovy +++ b/tests/compatibility-tests/src/main/resources/journalcompatibility/ispaging.groovy @@ -1,3 +1,5 @@ +package journalcompatibility + import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.core.server.Queue import org.apache.activemq.artemis.tests.compatibility.GroovyRun diff --git a/tests/compatibility-tests/src/main/resources/oldAddressSpace/receiveMessages.groovy b/tests/compatibility-tests/src/main/resources/oldAddressSpace/receiveMessages.groovy index 632993f27f5..0a3279875fe 100644 --- a/tests/compatibility-tests/src/main/resources/oldAddressSpace/receiveMessages.groovy +++ b/tests/compatibility-tests/src/main/resources/oldAddressSpace/receiveMessages.groovy @@ -65,6 +65,8 @@ for (int i = 0; i < 500; i++) { } session.commit(); +connection.close(); + // Defined on AddressConfigTest.java at the test with setVariable latch.countDown(); diff --git a/tests/compatibility-tests/src/main/resources/prefixSendAckTest/artemisServer.groovy b/tests/compatibility-tests/src/main/resources/prefixSendAckTest/artemisServer.groovy index b85cfcf96fb..5c456a83cd7 100644 --- a/tests/compatibility-tests/src/main/resources/prefixSendAckTest/artemisServer.groovy +++ b/tests/compatibility-tests/src/main/resources/prefixSendAckTest/artemisServer.groovy @@ -1,4 +1,4 @@ -package servers +package prefixSendAckTest /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with diff --git a/tests/compatibility-tests/src/main/resources/prefixSendAckTest/sendAckMessages.groovy b/tests/compatibility-tests/src/main/resources/prefixSendAckTest/sendAckMessages.groovy index a24ad8305f8..03d98b939cd 100644 --- a/tests/compatibility-tests/src/main/resources/prefixSendAckTest/sendAckMessages.groovy +++ b/tests/compatibility-tests/src/main/resources/prefixSendAckTest/sendAckMessages.groovy @@ -1,4 +1,4 @@ -package meshTest +package prefixSendAckTest import org.apache.activemq.artemis.tests.compatibility.GroovyRun diff --git a/tests/compatibility-tests/src/main/resources/sendAckTest/sendAckMessages.groovy b/tests/compatibility-tests/src/main/resources/sendAckTest/sendAckMessages.groovy index b0814cedd76..89a0dc18485 100644 --- a/tests/compatibility-tests/src/main/resources/sendAckTest/sendAckMessages.groovy +++ b/tests/compatibility-tests/src/main/resources/sendAckTest/sendAckMessages.groovy @@ -1,4 +1,4 @@ -package meshTest +package sendAckTest import org.apache.activemq.artemis.tests.compatibility.GroovyRun diff --git a/tests/compatibility-tests/src/main/resources/serial/cfserial.groovy b/tests/compatibility-tests/src/main/resources/serial/cfserial.groovy index 26085cd8d5a..a1a82da4b79 100644 --- a/tests/compatibility-tests/src/main/resources/serial/cfserial.groovy +++ b/tests/compatibility-tests/src/main/resources/serial/cfserial.groovy @@ -1,4 +1,4 @@ -package clients +package serial import io.netty.buffer.Unpooled import org.apache.activemq.artemis.api.core.ActiveMQBuffer diff --git a/tests/compatibility-tests/src/main/resources/serial/jbmserial.groovy b/tests/compatibility-tests/src/main/resources/serial/jbmserial.groovy index 02ee468af6b..93cceeb450f 100644 --- a/tests/compatibility-tests/src/main/resources/serial/jbmserial.groovy +++ b/tests/compatibility-tests/src/main/resources/serial/jbmserial.groovy @@ -1,4 +1,4 @@ -package clients +package serial /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with diff --git a/tests/compatibility-tests/src/main/resources/serial/serial.groovy b/tests/compatibility-tests/src/main/resources/serial/serial.groovy index 7caa3326033..d112c6c831f 100644 --- a/tests/compatibility-tests/src/main/resources/serial/serial.groovy +++ b/tests/compatibility-tests/src/main/resources/serial/serial.groovy @@ -1,4 +1,4 @@ -package clients +package serial /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ActiveMQJMSClientCompatibilityTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ActiveMQJMSClientCompatibilityTest.java index 91e0e2277bb..da606237117 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ActiveMQJMSClientCompatibilityTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ActiveMQJMSClientCompatibilityTest.java @@ -23,13 +23,14 @@ import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.jms.client.ActiveMQQueue; +import org.apache.activemq.artemis.tests.compatibility.base.ClasspathBase; import org.junit.Assert; import org.junit.Assume; import org.junit.Test; import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.SNAPSHOT; import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.ONE_FIVE; -public class ActiveMQJMSClientCompatibilityTest extends ClasspathBaseTest { +public class ActiveMQJMSClientCompatibilityTest extends ClasspathBase { @Test public void testActiveMQJMSCompatibility_1XPrefix_SNAPSHOT() throws Exception { diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/AddressConfigTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/AddressConfigTest.java index cba29afa452..2419e29d3cd 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/AddressConfigTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/AddressConfigTest.java @@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.activemq.artemis.tests.compatibility.base.VersionedBase; import org.apache.activemq.artemis.utils.FileUtil; import org.junit.After; import org.junit.Assert; @@ -36,7 +37,7 @@ import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.SNAPSHOT; @RunWith(Parameterized.class) -public class AddressConfigTest extends VersionedBaseTest { +public class AddressConfigTest extends VersionedBase { // this will ensure that all tests in this class are run twice, // once with "true" passed to the class' constructor and once with "false" diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java index 93894525fdf..18bcd7bfd7f 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ConnectionFactoryConfigurationSerializationTest.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.List; +import org.apache.activemq.artemis.tests.compatibility.base.VersionedBase; import org.apache.activemq.artemis.utils.FileUtil; import org.junit.After; import org.junit.Before; @@ -47,7 +48,7 @@ * Run->Edit Configuration->Add ArtemisMeshTest and add your properties. */ @RunWith(Parameterized.class) -public class ConnectionFactoryConfigurationSerializationTest extends VersionedBaseTest { +public class ConnectionFactoryConfigurationSerializationTest extends VersionedBase { // this will ensure that all tests in this class are run twice, // once with "true" passed to the class' constructor and once with "false" diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ExportImportTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ExportImportTest.java index 854ef1e0966..903165ff2ca 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ExportImportTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ExportImportTest.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.List; +import org.apache.activemq.artemis.tests.compatibility.base.VersionedBase; import org.apache.activemq.artemis.utils.FileUtil; import org.junit.After; import org.junit.Before; @@ -45,7 +46,7 @@ * Run->Edit Configuration->Add ArtemisMeshTest and add your properties. */ @RunWith(Parameterized.class) -public class ExportImportTest extends VersionedBaseTest { +public class ExportImportTest extends VersionedBase { private String serverScriptToUse; // this will ensure that all tests in this class are run twice, diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/HQClientTopologyTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/HQClientTopologyTest.java index 7a9ab9d4eea..87299d44ee0 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/HQClientTopologyTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/HQClientTopologyTest.java @@ -17,6 +17,7 @@ package org.apache.activemq.artemis.tests.compatibility; +import org.apache.activemq.artemis.tests.compatibility.base.VersionedBase; import org.apache.activemq.artemis.utils.FileUtil; import org.junit.After; import org.junit.Before; @@ -37,7 +38,7 @@ * correct connector parameters (keys must be dash-delimited instead of camelCase). */ @RunWith(Parameterized.class) -public class HQClientTopologyTest extends VersionedBaseTest { +public class HQClientTopologyTest extends VersionedBase { @Parameterized.Parameters(name = "server={0}, producer={1}, consumer={2}") public static Collection getParameters() { diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/HQFailoverTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/HQFailoverTest.java index d3bdaf1fb8f..8b3494609b0 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/HQFailoverTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/HQFailoverTest.java @@ -30,6 +30,7 @@ import org.apache.activemq.artemis.api.core.client.FailoverEventType; import org.apache.activemq.artemis.jms.client.ActiveMQConnection; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; +import org.apache.activemq.artemis.tests.compatibility.base.VersionedBase; import org.apache.activemq.artemis.utils.FileUtil; import org.junit.After; import org.junit.Before; @@ -46,7 +47,7 @@ * and it will make sure that failover happens without any problems. */ @RunWith(Parameterized.class) -public class HQFailoverTest extends VersionedBaseTest { +public class HQFailoverTest extends VersionedBase { @Parameterized.Parameters(name = "server={0}, producer={1}, consumer={2}") public static Collection getParameters() { diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java index 27ebdd02c3d..ae94083f679 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/JournalCompatibilityTest.java @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.List; +import org.apache.activemq.artemis.tests.compatibility.base.VersionedBase; import org.apache.activemq.artemis.utils.FileUtil; import org.junit.After; import org.junit.Before; @@ -47,7 +48,7 @@ * Run->Edit Configuration->Add ArtemisMeshTest and add your properties. */ @RunWith(Parameterized.class) -public class JournalCompatibilityTest extends VersionedBaseTest { +public class JournalCompatibilityTest extends VersionedBase { // this will ensure that all tests in this class are run twice, // once with "true" passed to the class' constructor and once with "false" diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/MeshTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/MeshTest.java index 37219792a7d..d37f8e5e9b5 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/MeshTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/MeshTest.java @@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.activemq.artemis.tests.compatibility.base.ServerBase; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -50,7 +51,7 @@ * Run->Edit Configuration->Add ArtemisMeshTest and add your properties. */ @RunWith(Parameterized.class) -public class MeshTest extends ServerBaseTest { +public class MeshTest extends ServerBase { // this will ensure that all tests in this class are run twice, // once with "true" passed to the class' constructor and once with "false" @@ -67,10 +68,10 @@ public static Collection getParameters() { // combinations.add(new Object[]{SNAPSHOT, ONE_FIVE, ONE_FIVE}); // combinations.add(new Object[]{ONE_FIVE, ONE_FIVE, ONE_FIVE}); - combinations.addAll(combinatory(new Object[]{SNAPSHOT}, new Object[]{ONE_FIVE, TWO_FOUR, SNAPSHOT, HORNETQ_235}, new Object[]{ONE_FIVE, TWO_FOUR, SNAPSHOT, HORNETQ_235})); - combinations.addAll(combinatory(new Object[]{ONE_FIVE}, new Object[]{ONE_FIVE, SNAPSHOT}, new Object[]{ONE_FIVE, SNAPSHOT})); - combinations.addAll(combinatory(new Object[]{HORNETQ_235}, new Object[]{ONE_FIVE, SNAPSHOT, HORNETQ_235}, new Object[]{ONE_FIVE, SNAPSHOT, HORNETQ_235})); - combinations.addAll(combinatory(new Object[]{HORNETQ_247}, new Object[]{SNAPSHOT, HORNETQ_247}, new Object[]{SNAPSHOT, HORNETQ_247})); + combinations.addAll(combinatory(SNAPSHOT, new Object[]{SNAPSHOT}, new Object[]{ONE_FIVE, TWO_FOUR, SNAPSHOT, HORNETQ_235}, new Object[]{ONE_FIVE, TWO_FOUR, SNAPSHOT, HORNETQ_235})); + combinations.addAll(combinatory(SNAPSHOT, new Object[]{ONE_FIVE}, new Object[]{ONE_FIVE, SNAPSHOT}, new Object[]{ONE_FIVE, SNAPSHOT})); + combinations.addAll(combinatory(SNAPSHOT, new Object[]{HORNETQ_235}, new Object[]{ONE_FIVE, SNAPSHOT, HORNETQ_235}, new Object[]{ONE_FIVE, SNAPSHOT, HORNETQ_235})); + combinations.addAll(combinatory(SNAPSHOT, new Object[]{HORNETQ_247}, new Object[]{SNAPSHOT, HORNETQ_247}, new Object[]{SNAPSHOT, HORNETQ_247})); combinations.add(new Object[]{SNAPSHOT, ONE_FOUR, ONE_FOUR}); return combinations; } diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/OldAddressSpaceTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/OldAddressSpaceTest.java index a0803b8cd0c..da55daf385b 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/OldAddressSpaceTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/OldAddressSpaceTest.java @@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.activemq.artemis.tests.compatibility.base.VersionedBase; import org.apache.activemq.artemis.utils.FileUtil; import org.junit.After; import org.junit.Assert; @@ -35,7 +36,7 @@ import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.SNAPSHOT; @RunWith(Parameterized.class) -public class OldAddressSpaceTest extends VersionedBaseTest { +public class OldAddressSpaceTest extends VersionedBase { @Parameterized.Parameters(name = "server={0}, producer={1}, consumer={2}") public static Collection getParameters() { diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/PrefixSendAckTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/PrefixSendAckTest.java index 3bdb23a6779..c4b0d87b38d 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/PrefixSendAckTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/PrefixSendAckTest.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.List; +import org.apache.activemq.artemis.tests.compatibility.base.ServerBase; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,7 +32,7 @@ import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.TWO_FOUR; @RunWith(Parameterized.class) -public class PrefixSendAckTest extends ServerBaseTest { +public class PrefixSendAckTest extends ServerBase { @Parameterized.Parameters(name = "server={0}, producer={1}, consumer={2}") public static Collection getParameters() { List combinations = new ArrayList<>(); diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ReplyToTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ReplyToTest.java new file mode 100644 index 00000000000..fb7846cb194 --- /dev/null +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ReplyToTest.java @@ -0,0 +1,134 @@ +/* + * 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.artemis.tests.compatibility; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; +import org.apache.activemq.artemis.tests.compatibility.base.ServerBase; +import org.apache.activemq.artemis.utils.FileUtil; +import org.junit.After; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.ONE_FIVE; +import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.SNAPSHOT; + +/** + * To run this test on the IDE and debug it, run the compatibility-tests through a command line once: + * + * cd /compatibility-tests + * mvn install -Ptests | tee output.log + * + * on the output.log you will see the output generated by {@link #getClasspath(String)} + * + * On your IDE, edit the Run Configuration to your test and add those -D as parameters to your test. + * On Idea you would do the following: + * + * Run->Edit Configuration->Add ArtemisMeshTest and add your properties. + */ +@RunWith(Parameterized.class) +public class ReplyToTest extends ServerBase { + + @Before + @Override + public void setUp() throws Throwable { + + FileUtil.deleteDirectory(serverFolder.getRoot()); + serverFolder.getRoot().mkdirs(); + + File file = serverFolder.newFile(ActiveMQJMSClient.class.getName() + ".properties"); + FileOutputStream fileOutputStream = new FileOutputStream(file); + PrintStream stream = new PrintStream(fileOutputStream); + stream.println("enable1xPrefixes=true"); + stream.close(); + + setVariable(serverClassloader, "persistent", Boolean.FALSE); + startServer(serverFolder.getRoot(), serverClassloader, "live"); + } + + @After + @Override + public void tearDown() throws Throwable { + super.tearDown(); + } + + @Override + public ClassLoader getClasspath(String name) throws Exception { + if (name.equals(SNAPSHOT)) { + + String snapshotPath = System.getProperty(SNAPSHOT); + Assume.assumeNotNull(snapshotPath); + + String path = serverFolder.getRoot().getAbsolutePath() + File.pathSeparator + snapshotPath; + + ClassLoader loader = defineClassLoader(path); + + clearGroovy(loader); + + return loader; + } else { + return super.getClasspath(name); + } + } + + // this will ensure that all tests in this class are run twice, + // once with "true" passed to the class' constructor and once with "false" + @Parameterized.Parameters(name = "server={0}, producer={1}, consumer={2}") + public static Collection getParameters() { + // we don't need every single version ever released.. + // if we keep testing current one against 2.4 and 1.4.. we are sure the wire and API won't change over time + List combinations = new ArrayList<>(); + + /* + // during development sometimes is useful to comment out the combinations + // and add the ones you are interested.. example: + */ + // combinations.add(new Object[]{SNAPSHOT, ONE_FIVE, ONE_FIVE}); + // combinations.add(new Object[]{ONE_FIVE, ONE_FIVE, ONE_FIVE}); + + combinations.add(new Object[]{SNAPSHOT, ONE_FIVE, ONE_FIVE}); + combinations.add(new Object[]{ONE_FIVE, SNAPSHOT, SNAPSHOT}); + + // TODO: It's not currently possible to mix reply to between 1.x and SNAPSHOT. Both sides need to be on the same version! + // combinations.addAll(combinatory(SNAPSHOT, new Object[]{SNAPSHOT, ONE_FIVE}, new Object[]{SNAPSHOT, ONE_FIVE}, new Object[]{SNAPSHOT, ONE_FIVE})); + return combinations; + } + + public ReplyToTest(String server, String sender, String receiver) throws Exception { + super(server, sender, receiver); + } + + @Test + public void testSendReceive() throws Throwable { + + setVariable(receiverClassloader, "latch", null); + evaluate(senderClassloader, "ReplyToTest/replyToSend.groovy"); + evaluate(receiverClassloader, "ReplyToTest/replyToReceive.groovy"); + } + +} + diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SendAckTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SendAckTest.java index 168a56f6956..9df38e339a0 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SendAckTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SendAckTest.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.List; +import org.apache.activemq.artemis.tests.compatibility.base.ServerBase; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -44,7 +45,7 @@ * Run->Edit Configuration->Add ArtemisMeshTest and add your properties. */ @RunWith(Parameterized.class) -public class SendAckTest extends ServerBaseTest { +public class SendAckTest extends ServerBase { // this will ensure that all tests in this class are run twice, // once with "true" passed to the class' constructor and once with "false" diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SerializationTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SerializationTest.java index bb238164f79..f7c61926841 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SerializationTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/SerializationTest.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.List; +import org.apache.activemq.artemis.tests.compatibility.base.VersionedBase; import org.apache.activemq.artemis.utils.FileUtil; import org.junit.After; import org.junit.Before; @@ -46,7 +47,7 @@ * Run->Edit Configuration->Add ArtemisMeshTest and add your properties. */ @RunWith(Parameterized.class) -public class SerializationTest extends VersionedBaseTest { +public class SerializationTest extends VersionedBase { // this will ensure that all tests in this class are run twice, // once with "true" passed to the class' constructor and once with "false" diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ClasspathBaseTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/base/ClasspathBase.java similarity index 85% rename from tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ClasspathBaseTest.java rename to tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/base/ClasspathBase.java index d2a4b50e38b..0f821df6abd 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ClasspathBaseTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/base/ClasspathBase.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.activemq.artemis.tests.compatibility; +package org.apache.activemq.artemis.tests.compatibility.base; import java.io.File; import java.lang.reflect.Method; @@ -26,13 +26,14 @@ import java.util.HashSet; import java.util.Map; +import org.apache.activemq.artemis.tests.compatibility.GroovyRun; import org.junit.Assume; import org.junit.ClassRule; import org.junit.rules.TemporaryFolder; import static org.apache.activemq.artemis.tests.compatibility.GroovyRun.SNAPSHOT; -public class ClasspathBaseTest { +public class ClasspathBase { @ClassRule @@ -48,7 +49,7 @@ public class ClasspathBaseTest { private static HashSet printed = new HashSet<>(); - protected static ClassLoader defineClassLoader(String classPath) throws MalformedURLException { + protected ClassLoader defineClassLoader(String classPath) throws MalformedURLException { String[] classPathArray = classPath.split(File.pathSeparator); URL[] elements = new URL[classPathArray.length]; for (int i = 0; i < classPathArray.length; i++) { @@ -58,19 +59,21 @@ protected static ClassLoader defineClassLoader(String classPath) throws Malforme return new URLClassLoader(elements, null); } - public static ClassLoader getClasspath(String name) throws Exception { + protected ClassLoader getClasspath(String name) throws Exception { return getClasspath(name, false); } - public static ClassLoader getClasspath(String name, boolean forceNew) throws Exception { + protected ClassLoader getClasspath(String name, boolean forceNew) throws Exception { if (!forceNew) { if (name.equals(SNAPSHOT)) { - return VersionedBaseTest.class.getClassLoader(); + GroovyRun.clear(); + return VersionedBase.class.getClassLoader(); } ClassLoader loader = loaderMap.get(name); if (loader != null && !forceNew) { + clearGroovy(loader); return loader; } } @@ -117,6 +120,15 @@ protected static void setVariable(ClassLoader loader, String name, Object object }); } + protected static void clearGroovy(ClassLoader loader) throws Exception { + tclCall(loader, () -> { + Class clazz = loader.loadClass(GroovyRun.class.getName()); + Method method = clazz.getMethod("clear"); + method.invoke(null); + return null; + }); + } + protected static Object setVariable(ClassLoader loader, String name) throws Exception { return tclCall(loader, () -> { Class clazz = loader.loadClass(GroovyRun.class.getName()); diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ServerBaseTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/base/ServerBase.java similarity index 86% rename from tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ServerBaseTest.java rename to tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/base/ServerBase.java index f519eab745a..8c73c6376b3 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/ServerBaseTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/base/ServerBase.java @@ -15,15 +15,15 @@ * limitations under the License. */ -package org.apache.activemq.artemis.tests.compatibility; +package org.apache.activemq.artemis.tests.compatibility.base; import org.apache.activemq.artemis.utils.FileUtil; import org.junit.After; import org.junit.Before; -public class ServerBaseTest extends VersionedBaseTest { +public class ServerBase extends VersionedBase { - public ServerBaseTest(String server, String sender, String receiver) throws Exception { + public ServerBase(String server, String sender, String receiver) throws Exception { super(server, sender, receiver); } diff --git a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/base/VersionedBase.java similarity index 75% rename from tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java rename to tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/base/VersionedBase.java index ab5331d5d29..1c5b2442ee0 100644 --- a/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/VersionedBaseTest.java +++ b/tests/compatibility-tests/src/test/java/org/apache/activemq/artemis/tests/compatibility/base/VersionedBase.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.activemq.artemis.tests.compatibility; +package org.apache.activemq.artemis.tests.compatibility.base; import java.io.File; import java.util.LinkedList; @@ -23,17 +23,17 @@ import org.junit.AfterClass; -public abstract class VersionedBaseTest extends ClasspathBaseTest { +public abstract class VersionedBase extends ClasspathBase { protected final String server; protected final String sender; protected final String receiver; - protected ClassLoader serverClassloader; - protected ClassLoader senderClassloader; - protected ClassLoader receiverClassloader; + protected final ClassLoader serverClassloader; + protected final ClassLoader senderClassloader; + protected final ClassLoader receiverClassloader; - public VersionedBaseTest(String server, String sender, String receiver) throws Exception { + public VersionedBase(String server, String sender, String receiver) throws Exception { if (server == null) { server = sender; } @@ -43,6 +43,9 @@ public VersionedBaseTest(String server, String sender, String receiver) throws E this.serverClassloader = getClasspath(server); this.senderClassloader = getClasspath(sender); this.receiverClassloader = getClasspath(receiver); + clearGroovy(senderClassloader); + clearGroovy(receiverClassloader); + clearGroovy(serverClassloader); } @AfterClass @@ -51,12 +54,21 @@ public static void cleanup() { } protected static List combinatory(Object[] rootSide, Object[] sideLeft, Object[] sideRight) { + return combinatory(null, rootSide, sideLeft, sideRight); + } + + protected static List combinatory(Object required, + Object[] rootSide, + Object[] sideLeft, + Object[] sideRight) { LinkedList combinations = new LinkedList<>(); for (Object root : rootSide) { for (Object left : sideLeft) { for (Object right : sideRight) { - combinations.add(new Object[]{root, left, right}); + if (required == null || root.equals(required) || left.equals(required) || right.equals(required)) { + combinations.add(new Object[]{root, left, right}); + } } } } From e7e5112d51c0b25f9fbfd194ed2ddbf3062a127a Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Wed, 29 Aug 2018 21:20:13 -0500 Subject: [PATCH 180/207] ARTEMIS-2066 LegacyJMSConfiguration parser may deploy non-jms queues --- .../core/config/impl/LegacyJMSConfiguration.java | 11 +++++------ .../artemis/tests/integration/jms/RedeployTest.java | 3 +++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java index bc4a741c4da..a79402c9b54 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java @@ -17,9 +17,6 @@ package org.apache.activemq.artemis.core.config.impl; import javax.management.MBeanServer; -import javax.xml.XMLConstants; -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; @@ -110,9 +107,11 @@ public void parseConfiguration(final InputStream input) throws Exception { String xml = XMLUtil.readerToString(reader); xml = XMLUtil.replaceSystemProps(xml); Element e = XMLUtil.stringToElement(xml); - SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - Schema schema = schemaFactory.newSchema(XMLUtil.findResource(CONFIGURATION_SCHEMA_URL)); - parseConfiguration(e); + // only parse elements from + NodeList children = e.getElementsByTagName(CONFIGURATION_SCHEMA_ROOT_ELEMENT); + if (children.getLength() > 0) { + parseConfiguration(children.item(0)); + } } /** diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java index bb3060665a4..e6855680fc0 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java @@ -260,6 +260,9 @@ public void run() { Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_queue_change").contains("config_test_queue_change_queue")); Assert.assertEquals(1, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").getMaxConsumers()); Assert.assertEquals(true, getQueue(embeddedActiveMQ, "config_test_queue_change_queue").isPurgeOnNoConsumers()); + + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_change_queue")); + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_removal_queue_1")); } finally { embeddedActiveMQ.stop(); } From b0d30d4da5708e2f46f9cb747e0b380d05f94526 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Wed, 29 Aug 2018 21:03:00 -0500 Subject: [PATCH 181/207] ARTEMIS-2064 make address & queue deployment more robust Any failure to deploy an address or queue will short-circuit the broker initialization process preventing any other addresses or queues from being deployed as well as other critical resources like acceptors, etc. --- .../core/config/CoreAddressConfiguration.java | 10 ++++ .../core/config/CoreQueueConfiguration.java | 24 ++++++++ .../config/impl/LegacyJMSConfiguration.java | 30 +++++----- .../impl/FileConfigurationParser.java | 3 +- .../core/server/ActiveMQServerLogger.java | 10 ++++ .../core/server/impl/ActiveMQServerImpl.java | 58 ++++++++++--------- .../byteman/AddressDeploymentFailedTest.java | 45 ++++++++++++++ .../byteman/QueueDeploymentFailedTest.java | 47 +++++++++++++++ 8 files changed, 182 insertions(+), 45 deletions(-) create mode 100644 tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/AddressDeploymentFailedTest.java create mode 100644 tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/QueueDeploymentFailedTest.java diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreAddressConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreAddressConfiguration.java index 290d48342bf..069222a3c6d 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreAddressConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreAddressConfiguration.java @@ -65,4 +65,14 @@ public CoreAddressConfiguration addQueueConfiguration(CoreQueueConfiguration que public List getQueueConfigurations() { return queueConfigurations; } + + @Override + public String toString() { + return "CoreAddressConfiguration[" + + "name=" + name + + ", routingTypes=" + routingTypes + + ", queueConfigurations=" + queueConfigurations + + "]"; + } + } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java index 2ccae2df0a5..87e938edc28 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java @@ -201,6 +201,7 @@ public int hashCode() { result = prime * result + ((lastValue == null) ? 0 : lastValue.hashCode()); result = prime * result + ((consumersBeforeDispatch == null) ? 0 : consumersBeforeDispatch.hashCode()); result = prime * result + ((delayBeforeDispatch == null) ? 0 : delayBeforeDispatch.hashCode()); + result = prime * result + ((routingType == null) ? 0 : routingType.hashCode()); return result; } @@ -265,6 +266,29 @@ public boolean equals(Object obj) { } else if (!delayBeforeDispatch.equals(other.delayBeforeDispatch)) { return false; } + if (routingType == null) { + if (other.routingType != null) + return false; + } else if (!routingType.equals(other.routingType)) { + return false; + } return true; } + + @Override + public String toString() { + return "CoreQueueConfiguration[" + + "name=" + name + + ", address=" + address + + ", routingType=" + routingType + + ", durable=" + durable + + ", filterString=" + filterString + + ", maxConsumers=" + maxConsumers + + ", purgeOnNoConsumers=" + purgeOnNoConsumers + + ", exclusive=" + exclusive + + ", lastValue=" + lastValue + + ", consumersBeforeDispatch=" + consumersBeforeDispatch + + ", delayBeforeDispatch=" + delayBeforeDispatch + + "]"; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java index a79402c9b54..dc50917bd6d 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/LegacyJMSConfiguration.java @@ -21,7 +21,6 @@ import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; -import java.util.List; import java.util.Map; import org.apache.activemq.artemis.api.core.RoutingType; @@ -150,10 +149,9 @@ public void parseConfiguration(final Node rootnode) throws Exception { */ public void parseTopicConfiguration(final Node node) throws Exception { String topicName = node.getAttributes().getNamedItem(NAME_ATTR).getNodeValue(); - List coreAddressConfigurations = configuration.getAddressConfigurations(); - coreAddressConfigurations.add(new CoreAddressConfiguration() - .setName(topicName) - .addRoutingType(RoutingType.MULTICAST)); + configuration.addAddressConfiguration(new CoreAddressConfiguration() + .setName(topicName) + .addRoutingType(RoutingType.MULTICAST)); } /** @@ -173,22 +171,22 @@ public void parseQueueConfiguration(final Node node) throws Exception { for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); - if (QUEUE_SELECTOR_NODE_NAME.equals(children.item(i).getNodeName())) { - Node selectorNode = children.item(i); + if (QUEUE_SELECTOR_NODE_NAME.equals(child.getNodeName())) { + Node selectorNode = child; Node attNode = selectorNode.getAttributes().getNamedItem("string"); selectorString = attNode.getNodeValue(); } } - List coreAddressConfigurations = configuration.getAddressConfigurations(); - coreAddressConfigurations.add(new CoreAddressConfiguration() - .setName(queueName) - .addRoutingType(RoutingType.ANYCAST) - .addQueueConfiguration(new CoreQueueConfiguration() - .setAddress(queueName) - .setName(queueName) - .setFilterString(selectorString) - .setRoutingType(RoutingType.ANYCAST))); + configuration.addAddressConfiguration(new CoreAddressConfiguration() + .setName(queueName) + .addRoutingType(RoutingType.ANYCAST) + .addQueueConfiguration(new CoreQueueConfiguration() + .setAddress(queueName) + .setName(queueName) + .setFilterString(selectorString) + .setDurable(durable) + .setRoutingType(RoutingType.ANYCAST))); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java index dc5ba16320f..b1b2c0ec084 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java @@ -770,8 +770,7 @@ private void parseAddresses(final Element e, final Configuration config) { Element node = (Element) elements.item(0); NodeList list = node.getElementsByTagName("address"); for (int i = 0; i < list.getLength(); i++) { - CoreAddressConfiguration addrConfig = parseAddressConfiguration(list.item(i)); - config.getAddressConfigurations().add(addrConfig); + config.addAddressConfiguration(parseAddressConfiguration(list.item(i))); } } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java index 6aa53f60e54..e6b7b48b3e9 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java @@ -1593,6 +1593,16 @@ void slowConsumerDetected(String sessionID, @Message(id = 22273, value = "Address \"{0}\" is full. Bridge {1} will disconnect", format = Message.Format.MESSAGE_FORMAT) void bridgeAddressFull(String addressName, String bridgeName); + @LogMessage(level = Logger.Level.WARN) + @Message(id = 222274, value = "Failed to deploy address {0}: {1}", + format = Message.Format.MESSAGE_FORMAT) + void problemDeployingAddress(String addressName, String message); + + @LogMessage(level = Logger.Level.WARN) + @Message(id = 222275, value = "Failed to deploy queue {0}: {1}", + format = Message.Format.MESSAGE_FORMAT) + void problemDeployingQueue(String queueName, String message); + @LogMessage(level = Logger.Level.ERROR) @Message(id = 224000, value = "Failure in initialisation", format = Message.Format.MESSAGE_FORMAT) void initializationError(@Cause Throwable e); 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 62d3c6e91c8..6cb0515dec3 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 @@ -2742,39 +2742,43 @@ private void deployAddressesFromConfiguration() throws Exception { private void deployAddressesFromConfiguration(Configuration configuration) throws Exception { for (CoreAddressConfiguration config : configuration.getAddressConfigurations()) { - AddressInfo info = new AddressInfo(SimpleString.toSimpleString(config.getName()), config.getRoutingTypes()); - addOrUpdateAddressInfo(info); - ActiveMQServerLogger.LOGGER.deployAddress(config.getName(), config.getRoutingTypes().toString()); - deployQueuesFromListCoreQueueConfiguration(config.getQueueConfigurations()); + try { + ActiveMQServerLogger.LOGGER.deployAddress(config.getName(), config.getRoutingTypes().toString()); + AddressInfo info = new AddressInfo(SimpleString.toSimpleString(config.getName()), config.getRoutingTypes()); + addOrUpdateAddressInfo(info); + deployQueuesFromListCoreQueueConfiguration(config.getQueueConfigurations()); + } catch (Exception e) { + ActiveMQServerLogger.LOGGER.problemDeployingAddress(config.getName(), e.getMessage()); + } } } private void deployQueuesFromListCoreQueueConfiguration(List queues) throws Exception { for (CoreQueueConfiguration config : queues) { - SimpleString queueName = SimpleString.toSimpleString(config.getName()); - ActiveMQServerLogger.LOGGER.deployQueue(config.getName(), config.getAddress(), config.getRoutingType().toString()); - AddressSettings as = addressSettingsRepository.getMatch(config.getAddress()); - // determine if there is an address::queue match; update it if so - int maxConsumers = config.getMaxConsumers() == null ? as.getDefaultMaxConsumers() : config.getMaxConsumers(); - boolean isExclusive = config.isExclusive() == null ? as.isDefaultExclusiveQueue() : config.isExclusive(); - boolean isLastValue = config.isLastValue() == null ? as.isDefaultLastValueQueue() : config.isLastValue(); - int consumersBeforeDispatch = config.getConsumersBeforeDispatch() == null ? as.getDefaultConsumersBeforeDispatch() : config.getConsumersBeforeDispatch(); - long delayBeforeDispatch = config.getDelayBeforeDispatch() == null ? as.getDefaultDelayBeforeDispatch() : config.getDelayBeforeDispatch(); - - if (locateQueue(queueName) != null && locateQueue(queueName).getAddress().toString().equals(config.getAddress())) { - updateQueue(config.getName(), config.getRoutingType(), maxConsumers, config.getPurgeOnNoConsumers(), - isExclusive, consumersBeforeDispatch, delayBeforeDispatch, config.getUser()); - } else { - // if the address::queue doesn't exist then create it - try { - createQueue(SimpleString.toSimpleString(config.getAddress()), config.getRoutingType(), - queueName, SimpleString.toSimpleString(config.getFilterString()), SimpleString.toSimpleString(config.getUser()), - config.isDurable(),false,false,false,false, maxConsumers, config.getPurgeOnNoConsumers(), - isExclusive, isLastValue, consumersBeforeDispatch, delayBeforeDispatch, true); - } catch (ActiveMQQueueExistsException e) { - // the queue may exist on a *different* address - ActiveMQServerLogger.LOGGER.warn(e.getMessage()); + try { + SimpleString queueName = SimpleString.toSimpleString(config.getName()); + ActiveMQServerLogger.LOGGER.deployQueue(config.getName(), config.getAddress(), config.getRoutingType().toString()); + AddressSettings as = addressSettingsRepository.getMatch(config.getAddress()); + // determine if there is an address::queue match; update it if so + int maxConsumers = config.getMaxConsumers() == null ? as.getDefaultMaxConsumers() : config.getMaxConsumers(); + boolean isExclusive = config.isExclusive() == null ? as.isDefaultExclusiveQueue() : config.isExclusive(); + boolean isLastValue = config.isLastValue() == null ? as.isDefaultLastValueQueue() : config.isLastValue(); + int consumersBeforeDispatch = config.getConsumersBeforeDispatch() == null ? as.getDefaultConsumersBeforeDispatch() : config.getConsumersBeforeDispatch(); + long delayBeforeDispatch = config.getDelayBeforeDispatch() == null ? as.getDefaultDelayBeforeDispatch() : config.getDelayBeforeDispatch(); + + if (locateQueue(queueName) != null && locateQueue(queueName).getAddress().toString().equals(config.getAddress())) { + updateQueue(config.getName(), config.getRoutingType(), maxConsumers, config.getPurgeOnNoConsumers(), isExclusive, consumersBeforeDispatch, delayBeforeDispatch, config.getUser()); + } else { + // if the address::queue doesn't exist then create it + try { + createQueue(SimpleString.toSimpleString(config.getAddress()), config.getRoutingType(), queueName, SimpleString.toSimpleString(config.getFilterString()), SimpleString.toSimpleString(config.getUser()), config.isDurable(), false, false, false, false, maxConsumers, config.getPurgeOnNoConsumers(), isExclusive, isLastValue, consumersBeforeDispatch, delayBeforeDispatch, true); + } catch (ActiveMQQueueExistsException e) { + // the queue may exist on a *different* address + ActiveMQServerLogger.LOGGER.warn(e.getMessage()); + } } + } catch (Exception e) { + ActiveMQServerLogger.LOGGER.problemDeployingQueue(config.getName(), e.getMessage()); } } } diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/AddressDeploymentFailedTest.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/AddressDeploymentFailedTest.java new file mode 100644 index 00000000000..0d19158df6a --- /dev/null +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/AddressDeploymentFailedTest.java @@ -0,0 +1,45 @@ +/* + * 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.artemis.tests.extras.byteman; + +import java.util.UUID; + +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.core.config.CoreAddressConfiguration; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.jboss.byteman.contrib.bmunit.BMRule; +import org.jboss.byteman.contrib.bmunit.BMUnitRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(BMUnitRunner.class) +public class AddressDeploymentFailedTest extends ActiveMQTestBase { + + @Test + @BMRule(name = "blow up address deployment", + targetClass = "org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl", + targetMethod = "addOrUpdateAddressInfo(AddressInfo)", + targetLocation = "EXIT", + action = "throw new IllegalStateException(\"test exception\")") + public void testAddressDeploymentFailure() throws Exception { + ActiveMQServer server = createServer(false, createDefaultNettyConfig()); + server.getConfiguration().addAddressConfiguration(new CoreAddressConfiguration().setName(UUID.randomUUID().toString()).addRoutingType(RoutingType.ANYCAST)); + server.start(); + assertTrue(server.getRemotingService().isStarted()); + } +} diff --git a/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/QueueDeploymentFailedTest.java b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/QueueDeploymentFailedTest.java new file mode 100644 index 00000000000..d01215a563e --- /dev/null +++ b/tests/extra-tests/src/test/java/org/apache/activemq/artemis/tests/extras/byteman/QueueDeploymentFailedTest.java @@ -0,0 +1,47 @@ +/* + * 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.artemis.tests.extras.byteman; + +import java.util.UUID; + +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.core.config.CoreAddressConfiguration; +import org.apache.activemq.artemis.core.config.CoreQueueConfiguration; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.jboss.byteman.contrib.bmunit.BMRule; +import org.jboss.byteman.contrib.bmunit.BMUnitRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(BMUnitRunner.class) +public class QueueDeploymentFailedTest extends ActiveMQTestBase { + + @Test + @BMRule(name = "blow up queue deployment", + targetClass = "org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl", + targetMethod = "createQueue(SimpleString,RoutingType,SimpleString,SimpleString,SimpleString,boolean,boolean,boolean,boolean,boolean,int,boolean,boolean,boolean,int,long,boolean", + targetLocation = "EXIT", + action = "throw new IllegalStateException(\"test exception\")") + public void testQueueDeploymentFailure() throws Exception { + ActiveMQServer server = createServer(false, createDefaultNettyConfig()); + String address = UUID.randomUUID().toString(); + server.getConfiguration().addAddressConfiguration(new CoreAddressConfiguration().setName(address).addRoutingType(RoutingType.ANYCAST).addQueueConfiguration(new CoreQueueConfiguration().setName(UUID.randomUUID().toString()).setRoutingType(RoutingType.ANYCAST).setAddress(address))); + server.start(); + assertTrue(server.getRemotingService().isStarted()); + } +} From 3827c54c058e7fef062d6d5e7e97e3f6466361fe Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 30 Aug 2018 13:21:07 -0500 Subject: [PATCH 182/207] ARTEMIS-2065 Can't change queue routing-type between restarts --- .../core/server/ActiveMQServerLogger.java | 7 +- .../core/server/impl/ActiveMQServerImpl.java | 25 +++--- .../tests/integration/jms/RedeployTest.java | 44 +++++++++- .../persistence/ConfigChangeTest.java | 86 +++++++++++++++++++ .../reload-queue-routingtype-updated.xml | 40 +++++++++ .../resources/reload-queue-routingtype.xml | 40 +++++++++ 6 files changed, 225 insertions(+), 17 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java create mode 100644 tests/integration-tests/src/test/resources/reload-queue-routingtype-updated.xml create mode 100644 tests/integration-tests/src/test/resources/reload-queue-routingtype.xml diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java index e6b7b48b3e9..2cc00d39931 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java @@ -44,6 +44,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; import org.apache.activemq.artemis.api.core.Pair; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.client.impl.ServerLocatorInternal; import org.apache.activemq.artemis.core.config.Configuration; @@ -1898,12 +1899,12 @@ void slowConsumerDetected(String sessionID, void criticalSystemLog(Object component); @LogMessage(level = Logger.Level.INFO) - @Message(id = 224076, value = "UnDeploying address {0}", format = Message.Format.MESSAGE_FORMAT) + @Message(id = 224076, value = "Undeploying address {0}", format = Message.Format.MESSAGE_FORMAT) void undeployAddress(SimpleString addressName); @LogMessage(level = Logger.Level.INFO) - @Message(id = 224077, value = "UnDeploying queue {0}", format = Message.Format.MESSAGE_FORMAT) - void undeployQueue(SimpleString queueName); + @Message(id = 224077, value = "Undeploying {0} queue {1}", format = Message.Format.MESSAGE_FORMAT) + void undeployQueue(RoutingType routingType, SimpleString queueName); @LogMessage(level = Logger.Level.WARN) @Message(id = 224078, value = "The size of duplicate cache detection () appears to be too large {0}. It should be no greater than the number of messages that can be squeezed into conformation buffer () {1}.", format = Message.Format.MESSAGE_FORMAT) 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 6cb0515dec3..715f984ad24 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 @@ -2601,6 +2601,9 @@ public void run() { }, 0, dumpInfoInterval, TimeUnit.MILLISECONDS); } + // Undeploy any addresses and queues not in config + undeployAddressesAndQueueNotInConfiguration(); + // Deploy the rest of the stuff // Deploy predefined addresses @@ -2609,9 +2612,6 @@ public void run() { // Deploy any predefined queues deployQueuesFromConfiguration(); - // Undeploy any addresses and queues not in config - undeployAddressesAndQueueNotInConfiguration(); - // We need to call this here, this gives any dependent server a chance to deploy its own addresses // this needs to be done before clustering is fully activated callActivateCallbacks(); @@ -2698,25 +2698,28 @@ private void undeployAddressesAndQueueNotInConfiguration(Configuration configura .map(CoreAddressConfiguration::getName) .collect(Collectors.toSet()); - Set queuesInConfig = configuration.getAddressConfigurations().stream() - .map(CoreAddressConfiguration::getQueueConfigurations) - .flatMap(List::stream).map(CoreQueueConfiguration::getName) - .collect(Collectors.toSet()); + Set queuesInConfig = new HashSet<>(); + for (CoreAddressConfiguration cac : configuration.getAddressConfigurations()) { + for (CoreQueueConfiguration cqc : cac.getQueueConfigurations()) { + // combine the routing-type and queue name as the unique identifier as it's possible to change the routing-type without changing the name + queuesInConfig.add(cqc.getRoutingType().toString() + cqc.getName()); + } + } for (SimpleString addressName : listAddressNames()) { AddressSettings addressSettings = getAddressSettingsRepository().getMatch(addressName.toString()); if (!addressesInConfig.contains(addressName.toString()) && addressSettings.getConfigDeleteAddresses() == DeletionPolicy.FORCE) { for (Queue queue : listQueues(addressName)) { - ActiveMQServerLogger.LOGGER.undeployQueue(queue.getName()); + ActiveMQServerLogger.LOGGER.undeployQueue(queue.getRoutingType(), queue.getName()); queue.deleteQueue(true); } ActiveMQServerLogger.LOGGER.undeployAddress(addressName); removeAddressInfo(addressName, null); } else if (addressSettings.getConfigDeleteQueues() == DeletionPolicy.FORCE) { for (Queue queue : listConfiguredQueues(addressName)) { - if (!queuesInConfig.contains(queue.getName().toString())) { - ActiveMQServerLogger.LOGGER.undeployQueue(queue.getName()); + if (!queuesInConfig.contains(queue.getRoutingType().toString() + queue.getName().toString())) { + ActiveMQServerLogger.LOGGER.undeployQueue(queue.getRoutingType(), queue.getName()); queue.deleteQueue(true); } } @@ -3441,8 +3444,8 @@ public void reload(URL uri) throws Exception { } ActiveMQServerLogger.LOGGER.reloadingConfiguration("addresses"); - deployAddressesFromConfiguration(config); undeployAddressesAndQueueNotInConfiguration(config); + deployAddressesFromConfiguration(config); configuration.setAddressConfigurations(config.getAddressConfigurations()); configuration.setQueueConfigurations(config.getQueueConfigurations()); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java index e6855680fc0..1d8e7b93f3a 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java @@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.postoffice.QueueBinding; import org.apache.activemq.artemis.core.security.Role; @@ -196,9 +197,6 @@ private boolean tryConsume() throws JMSException { } - - - @Test public void testRedeployAddressQueue() throws Exception { Path brokerXML = getTestDirfile().toPath().resolve("broker.xml"); @@ -268,6 +266,46 @@ public void run() { } } + @Test + public void testRedeployChangeQueueRoutingType() throws Exception { + Path brokerXML = getTestDirfile().toPath().resolve("broker.xml"); + URL url1 = RedeployTest.class.getClassLoader().getResource("reload-queue-routingtype.xml"); + URL url2 = RedeployTest.class.getClassLoader().getResource("reload-queue-routingtype-updated.xml"); + Files.copy(url1.openStream(), brokerXML); + + EmbeddedActiveMQ embeddedActiveMQ = new EmbeddedActiveMQ(); + embeddedActiveMQ.setConfigResourcePath(brokerXML.toUri().toString()); + embeddedActiveMQ.start(); + + final ReusableLatch latch = new ReusableLatch(1); + + Runnable tick = new Runnable() { + @Override + public void run() { + latch.countDown(); + } + }; + + embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); + + try { + latch.await(10, TimeUnit.SECONDS); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "myAddress")); + Assert.assertEquals(RoutingType.MULTICAST, getQueue(embeddedActiveMQ, "myQueue").getRoutingType()); + + Files.copy(url2.openStream(), brokerXML, StandardCopyOption.REPLACE_EXISTING); + brokerXML.toFile().setLastModified(System.currentTimeMillis() + 1000); + latch.setCount(1); + embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); + latch.await(10, TimeUnit.SECONDS); + + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "myAddress")); + Assert.assertEquals(RoutingType.ANYCAST, getQueue(embeddedActiveMQ, "myQueue").getRoutingType()); + } finally { + embeddedActiveMQ.stop(); + } + } + /** diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java new file mode 100644 index 00000000000..3a2264f1278 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java @@ -0,0 +1,86 @@ +/** + * 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.artemis.tests.integration.persistence; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.config.Configuration; +import org.apache.activemq.artemis.core.config.CoreAddressConfiguration; +import org.apache.activemq.artemis.core.config.CoreQueueConfiguration; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.core.settings.impl.AddressSettings; +import org.apache.activemq.artemis.core.settings.impl.DeletionPolicy; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.junit.Test; + +public class ConfigChangeTest extends ActiveMQTestBase { + + private ActiveMQServer server; + + @Test + public void testChangeQueueRoutingTypeOnRestart() throws Exception { + internalTestChangeQueueRoutingTypeOnRestart(false); + } + + @Test + public void testChangeQueueRoutingTypeOnRestartNegative() throws Exception { + internalTestChangeQueueRoutingTypeOnRestart(true); + } + + public void internalTestChangeQueueRoutingTypeOnRestart(boolean negative) throws Exception { + // if negative == true then the queue's routing type should *not* change + + Configuration configuration = createDefaultInVMConfig(); + configuration.addAddressesSetting("#", new AddressSettings() + .setConfigDeleteQueues(negative ? DeletionPolicy.OFF : DeletionPolicy.FORCE) + .setConfigDeleteAddresses(negative ? DeletionPolicy.OFF : DeletionPolicy.FORCE)); + + List addressConfigurations = new ArrayList(); + CoreAddressConfiguration addressConfiguration = new CoreAddressConfiguration() + .setName("myAddress") + .addRoutingType(RoutingType.ANYCAST) + .addQueueConfiguration(new CoreQueueConfiguration() + .setName("myQueue") + .setAddress("myAddress") + .setRoutingType(RoutingType.ANYCAST)); + addressConfigurations.add(addressConfiguration); + configuration.setAddressConfigurations(addressConfigurations); + server = createServer(true, configuration); + server.start(); + server.stop(); + + addressConfiguration = new CoreAddressConfiguration() + .setName("myAddress") + .addRoutingType(RoutingType.MULTICAST) + .addQueueConfiguration(new CoreQueueConfiguration() + .setName("myQueue") + .setAddress("myAddress") + .setRoutingType(RoutingType.MULTICAST)); + addressConfigurations.clear(); + addressConfigurations.add(addressConfiguration); + configuration.setAddressConfigurations(addressConfigurations); + + server.start(); + assertEquals(negative ? RoutingType.ANYCAST : RoutingType.MULTICAST, server.getAddressInfo(SimpleString.toSimpleString("myAddress")).getRoutingType()); + assertEquals(negative ? RoutingType.ANYCAST : RoutingType.MULTICAST, server.locateQueue(SimpleString.toSimpleString("myQueue")).getRoutingType()); + server.stop(); + } +} diff --git a/tests/integration-tests/src/test/resources/reload-queue-routingtype-updated.xml b/tests/integration-tests/src/test/resources/reload-queue-routingtype-updated.xml new file mode 100644 index 00000000000..e5bbe4fe969 --- /dev/null +++ b/tests/integration-tests/src/test/resources/reload-queue-routingtype-updated.xml @@ -0,0 +1,40 @@ + + + + + + + + + FORCE + + + + +

+ + + +
+ + + diff --git a/tests/integration-tests/src/test/resources/reload-queue-routingtype.xml b/tests/integration-tests/src/test/resources/reload-queue-routingtype.xml new file mode 100644 index 00000000000..61ae86a9c8d --- /dev/null +++ b/tests/integration-tests/src/test/resources/reload-queue-routingtype.xml @@ -0,0 +1,40 @@ + + + + + + + + + FORCE + + + + +
+ + + +
+
+
+
From 2b633ee5c5213dffebf83391a6c7c21bc0bff5ed Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 4 Sep 2018 12:47:26 -0400 Subject: [PATCH 183/207] NO-JIRA Using less resources from public clouds on tests LibaioTest was failing because of a test that was allocating 500 positions on libaio. Which may not be available on Travis every time. --- .../org/apache/activemq/artemis/jlibaio/test/LibaioTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artemis-native/src/test/java/org/apache/activemq/artemis/jlibaio/test/LibaioTest.java b/artemis-native/src/test/java/org/apache/activemq/artemis/jlibaio/test/LibaioTest.java index 922df372f51..e663e1919b4 100644 --- a/artemis-native/src/test/java/org/apache/activemq/artemis/jlibaio/test/LibaioTest.java +++ b/artemis-native/src/test/java/org/apache/activemq/artemis/jlibaio/test/LibaioTest.java @@ -630,7 +630,7 @@ public void testIOExceptionConditions() throws Exception { @Test public void testBlockedCallback() throws Exception { - final LibaioContext blockedContext = new LibaioContext(500, true, true); + final LibaioContext blockedContext = new LibaioContext(LIBAIO_QUEUE_SIZE, true, true); Thread t = new Thread() { @Override public void run() { @@ -640,7 +640,7 @@ public void run() { t.start(); - int NUMBER_OF_BLOCKS = 5000; + int NUMBER_OF_BLOCKS = LIBAIO_QUEUE_SIZE * 10; final CountDownLatch latch = new CountDownLatch(NUMBER_OF_BLOCKS); From f4b77a2cb4a23241d8d0285a882cdc3cfb55601e Mon Sep 17 00:00:00 2001 From: andytaylor Date: Tue, 4 Sep 2018 11:16:35 +0100 Subject: [PATCH 184/207] ARTEMIS-2073 - make sure connection gets set for interceptors https://issues.apache.org/jira/browse/ARTEMIS-2073 --- .../ActiveMQProtonRemotingConnection.java | 1 + .../amqp/AmqpSendReceiveInterceptorTest.java | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/ActiveMQProtonRemotingConnection.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/ActiveMQProtonRemotingConnection.java index a37b7b74184..41f6e788c8a 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/ActiveMQProtonRemotingConnection.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/ActiveMQProtonRemotingConnection.java @@ -50,6 +50,7 @@ public ActiveMQProtonRemotingConnection(ProtonProtocolManager manager, super(transportConnection, executor); this.manager = manager; this.amqpConnection = amqpConnection; + transportConnection.setProtocolConnection(this); } public Executor getExecutor() { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpSendReceiveInterceptorTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpSendReceiveInterceptorTest.java index 20be85d2363..8dcb2bfe2fb 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpSendReceiveInterceptorTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpSendReceiveInterceptorTest.java @@ -176,4 +176,50 @@ public boolean intercept(AMQPMessage packet, RemotingConnection connection) thro receiver.close(); connection.close(); } + + @Test(timeout = 60000) + public void testCheckRemotingConnection() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final boolean[] passed = {false}; + server.getRemotingService().addIncomingInterceptor(new AmqpInterceptor() { + @Override + public boolean intercept(AMQPMessage message, RemotingConnection connection) throws ActiveMQException { + passed[0] = connection != null; + latch.countDown(); + return true; + } + }); + + AmqpClient client = createAmqpClient(); + AmqpConnection connection = addConnection(client.connect()); + AmqpSession session = connection.createSession(); + + AmqpSender sender = session.createSender(getTestName()); + AmqpMessage message = new AmqpMessage(); + message.setMessageId("msg" + 1); + message.setText("Test-Message"); + sender.send(message); + + assertTrue(latch.await(2, TimeUnit.SECONDS)); + assertTrue("connection not set", passed[0]); + + final CountDownLatch latch2 = new CountDownLatch(1); + server.getRemotingService().addOutgoingInterceptor(new AmqpInterceptor() { + @Override + public boolean intercept(AMQPMessage packet, RemotingConnection connection) throws ActiveMQException { + passed[0] = connection != null; + latch2.countDown(); + return true; + } + }); + AmqpReceiver receiver = session.createReceiver(getTestName()); + receiver.flow(2); + AmqpMessage amqpMessage = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull(amqpMessage); + assertEquals(latch2.getCount(), 0); + assertTrue("connection not set", passed[0]); + sender.close(); + receiver.close(); + connection.close(); + } } From fab96c4b1c65e2cf552d3bf89d24b7b414a6a980 Mon Sep 17 00:00:00 2001 From: Carsten Lohmann Date: Tue, 28 Aug 2018 15:38:40 +0200 Subject: [PATCH 185/207] ARTEMIS-2058 Support any kind of extraProperties in AmqpCoreConverter --- .../amqp/converter/AmqpCoreConverter.java | 2 +- .../amqp/converter/TestConversions.java | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java index e199d1f6692..45ba9317a9b 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/AmqpCoreConverter.java @@ -199,7 +199,7 @@ public static ICoreMessage toCore(AMQPMessage message, CoreMessageObjectPools co if (str.equals(AMQPMessage.ADDRESS_PROPERTY)) { continue; } - result.getInnerMessage().putBytesProperty(str, properties.getBytesProperty(str)); + result.getInnerMessage().putObjectProperty(str, properties.getProperty(str)); } } diff --git a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/TestConversions.java b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/TestConversions.java index 8ced3481e92..94df3a59454 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/TestConversions.java +++ b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/converter/TestConversions.java @@ -22,12 +22,14 @@ import java.util.Map; import org.apache.activemq.artemis.api.core.ICoreMessage; +import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.protocol.amqp.broker.AMQPMessage; import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSBytesMessage; import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMapMessage; import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSMessage; import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSStreamMessage; import org.apache.activemq.artemis.protocol.amqp.converter.jms.ServerJMSTextMessage; +import org.apache.activemq.artemis.utils.collections.TypedProperties; import org.apache.qpid.proton.amqp.Binary; import org.apache.qpid.proton.amqp.messaging.AmqpSequence; import org.apache.qpid.proton.amqp.messaging.AmqpValue; @@ -112,6 +114,14 @@ private Map createPropertiesMap() { return mapprop; } + private TypedProperties createTypedPropertiesMap() { + TypedProperties typedProperties = new TypedProperties(); + typedProperties.putBooleanProperty(new SimpleString("true"), Boolean.TRUE); + typedProperties.putBooleanProperty(new SimpleString("false"), Boolean.FALSE); + typedProperties.putSimpleStringProperty(new SimpleString("foo"), new SimpleString("bar")); + return typedProperties; + } + @Test public void testSimpleConversionMap() throws Exception { Map mapprop = createPropertiesMap(); @@ -192,4 +202,28 @@ public void testSimpleConversionText() throws Exception { } + @Test + public void testSimpleConversionWithExtraProperties() throws Exception { + MessageImpl message = (MessageImpl) Message.Factory.create(); + + String text = "someText"; + message.setBody(new AmqpValue(text)); + + AMQPMessage encodedMessage = new AMQPMessage(message); + TypedProperties extraProperties = createTypedPropertiesMap(); + extraProperties.putBytesProperty(new SimpleString("bytesProp"), "value".getBytes()); + encodedMessage.setExtraProperties(extraProperties); + + ICoreMessage serverMessage = encodedMessage.toCore(); + + ServerJMSTextMessage textMessage = (ServerJMSTextMessage) ServerJMSMessage.wrapCoreMessage(serverMessage); + textMessage.decode(); + + verifyProperties(textMessage); + assertEquals("value", new String(((byte[]) textMessage.getObjectProperty("bytesProp")))); + + Assert.assertEquals(text, textMessage.getText()); + + } + } From 71a34ff6042e9b616d49d0c1e876842e0f9b0c11 Mon Sep 17 00:00:00 2001 From: BiNZGi Date: Wed, 22 Aug 2018 15:55:18 +0200 Subject: [PATCH 186/207] NO-JIRA Add version update steps for windows services on doc --- docs/user-manual/en/upgrading.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/user-manual/en/upgrading.md b/docs/user-manual/en/upgrading.md index 61609b8c9b3..4eddda3c768 100644 --- a/docs/user-manual/en/upgrading.md +++ b/docs/user-manual/en/upgrading.md @@ -33,7 +33,16 @@ but the general process is as follows: ``` ARTEMIS_HOME='/path/to/apache-artemis-version' ``` - + +If you run Artemis as a service on windows you have to do the following additional steps: + +1. Navigate to the `bin` folder of the broker instance that's being upgraded +1. Open `artemis-service.xml`. It contains a property which is relevant for the upgrade: + + ``` + + ``` + The `ARTEMIS_HOME` property is used to link the instance with the home. _In most cases_ the instance can be upgraded to a newer version simply by changing the value of this property to the location of the new broker home. Please refer From d91da412c36c590bd6f0e18f660b45c28e2ad346 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Tue, 4 Sep 2018 10:34:03 -0500 Subject: [PATCH 187/207] ARTEMIS-2072 eliminate unnecessary binding queries --- .../amqp/broker/AMQPSessionCallback.java | 38 +++++++++---------- .../proton/ProtonServerReceiverContext.java | 2 +- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java index b99a0538af8..b32a4c74b41 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java @@ -38,7 +38,6 @@ import org.apache.activemq.artemis.core.security.CheckType; import org.apache.activemq.artemis.core.security.SecurityAuth; import org.apache.activemq.artemis.core.server.AddressQueryResult; -import org.apache.activemq.artemis.core.server.BindingQueryResult; import org.apache.activemq.artemis.core.server.MessageReference; import org.apache.activemq.artemis.core.server.QueueQueryResult; import org.apache.activemq.artemis.core.server.ServerConsumer; @@ -109,8 +108,6 @@ public class AMQPSessionCallback implements SessionCallback { private final AddressQueryCache addressQueryCache = new AddressQueryCache<>(); - private final AddressQueryCache bindingQueryCache = new AddressQueryCache<>(); - public AMQPSessionCallback(AMQPConnectionCallback protonSPI, ProtonProtocolManager manager, AMQPConnectionContext connection, @@ -308,35 +305,34 @@ public QueueQueryResult queueQuery(SimpleString queueName, RoutingType routingTy - public boolean bindingQuery(SimpleString address, RoutingType routingType) throws Exception { - BindingQueryResult bindingQueryResult = bindingQueryCache.getResult(address); + public boolean checkAddressAndAutocreateIfPossible(SimpleString address, RoutingType routingType) throws Exception { + AddressInfo addressInfo = manager.getServer().getAddressInfo(address); - if (bindingQueryResult != null) { - return bindingQueryResult.isExists(); + // if the address exists go ahead and return + if (addressInfo != null) { + return true; } - bindingQueryResult = serverSession.executeBindingQuery(address); - if (routingType == RoutingType.MULTICAST && !bindingQueryResult.isExists() && bindingQueryResult.isAutoCreateAddresses()) { - try { - serverSession.createAddress(address, routingType, true); - } catch (ActiveMQAddressExistsException e) { - // The address may have been created by another thread in the mean time. Catch and do nothing. + // if the address and/or queue don't exist then create them if possible + if (routingType == RoutingType.MULTICAST && addressInfo == null) { + if (manager.getServer().getAddressSettingsRepository().getMatch(address.toString()).isAutoCreateAddresses()) { + try { + serverSession.createAddress(address, routingType, true); + } catch (ActiveMQAddressExistsException e) { + // The address may have been created by another thread in the mean time. Catch and do nothing. + } } - bindingQueryResult = serverSession.executeBindingQuery(address); - } else if (routingType == RoutingType.ANYCAST && bindingQueryResult.isAutoCreateQueues()) { - QueueQueryResult queueBinding = serverSession.executeQueueQuery(address); - if (!queueBinding.isExists()) { + } else if (routingType == RoutingType.ANYCAST && manager.getServer().locateQueue(address) == null) { + if (manager.getServer().getAddressSettingsRepository().getMatch(address.toString()).isAutoCreateQueues()) { try { serverSession.createQueue(address, address, routingType, null, false, true, true); } catch (ActiveMQQueueExistsException e) { // The queue may have been created by another thread in the mean time. Catch and do nothing. } } - bindingQueryResult = serverSession.executeBindingQuery(address); } - bindingQueryCache.setResult(address, bindingQueryResult); - return bindingQueryResult.isExists(); + return manager.getServer().getAddressInfo(address) != null; } @@ -475,7 +471,7 @@ public void serverSend(final ProtonServerReceiverContext context, //here check queue-autocreation RoutingType routingType = context.getRoutingType(receiver, address); - if (!bindingQuery(address, routingType)) { + if (!checkAddressAndAutocreateIfPossible(address, routingType)) { throw ActiveMQAMQPProtocolMessageBundle.BUNDLE.addressDoesntExist(); } diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java index e54a1a5ad6f..30dd10aeeac 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java @@ -127,7 +127,7 @@ public void initialise() throws Exception { if (address != null && !address.isEmpty()) { defRoutingType = getRoutingType(target.getCapabilities(), address); try { - if (!sessionSPI.bindingQuery(address, defRoutingType)) { + if (!sessionSPI.checkAddressAndAutocreateIfPossible(address, defRoutingType)) { throw ActiveMQAMQPProtocolMessageBundle.BUNDLE.addressDoesntExist(); } } catch (ActiveMQAMQPNotFoundException e) { From 05ce7c6ecd1c70fc571764af9027767f04538ccd Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 5 Sep 2018 18:09:18 -0400 Subject: [PATCH 188/207] NO-JIRA Adding a test playing with network disconnects and failover --- .../artemis/tests/util/ActiveMQTestBase.java | 5 +- .../failover/NetworkFailureFailoverTest.java | 688 ++++++++++++++++++ .../artemis/tests/util/network/NetUtil.java | 226 ++++++ .../tests/util/network/NetUtilResource.java | 29 + 4 files changed, 946 insertions(+), 2 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NetworkFailureFailoverTest.java create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/network/NetUtil.java create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/network/NetUtilResource.java diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java b/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java index 530c399560c..2cd5d564cbe 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java @@ -1219,7 +1219,7 @@ protected static final Map generateParams(final int node, final return params; } - protected static final TransportConfiguration getNettyAcceptorTransportConfiguration(final boolean live) { + protected TransportConfiguration getNettyAcceptorTransportConfiguration(final boolean live) { if (live) { return new TransportConfiguration(NETTY_ACCEPTOR_FACTORY); } @@ -1231,7 +1231,7 @@ protected static final TransportConfiguration getNettyAcceptorTransportConfigura return new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, server1Params); } - protected static final TransportConfiguration getNettyConnectorTransportConfiguration(final boolean live) { + protected TransportConfiguration getNettyConnectorTransportConfiguration(final boolean live) { if (live) { return new TransportConfiguration(NETTY_CONNECTOR_FACTORY); } @@ -1239,6 +1239,7 @@ protected static final TransportConfiguration getNettyConnectorTransportConfigur Map server1Params = new HashMap<>(); server1Params.put(org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants.PORT_PROP_NAME, org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants.DEFAULT_PORT + 1); + server1Params.put(org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); return new TransportConfiguration(NETTY_CONNECTOR_FACTORY, server1Params); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NetworkFailureFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NetworkFailureFailoverTest.java new file mode 100644 index 00000000000..10115024dc8 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NetworkFailureFailoverTest.java @@ -0,0 +1,688 @@ +/* + * Copyright 2009 Red Hat, Inc. + * Red Hat 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.artemis.tests.integration.cluster.failover; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.Interceptor; +import org.apache.activemq.artemis.api.core.TransportConfiguration; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; +import org.apache.activemq.artemis.api.core.client.ClientConsumer; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientProducer; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; +import org.apache.activemq.artemis.api.core.client.ServerLocator; +import org.apache.activemq.artemis.api.core.client.SessionFailureListener; +import org.apache.activemq.artemis.api.core.client.TopologyMember; +import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal; +import org.apache.activemq.artemis.core.client.impl.Topology; +import org.apache.activemq.artemis.core.protocol.core.Packet; +import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.CreateSessionMessage; +import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionSendMessage; +import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; +import org.apache.activemq.artemis.junit.Wait; +import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; +import org.apache.activemq.artemis.tests.util.network.NetUtil; +import org.apache.activemq.artemis.tests.util.network.NetUtilResource; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; + +public class NetworkFailureFailoverTest extends FailoverTestBase { + + @Rule + public NetUtilResource netUtilResource = new NetUtilResource(); + + @BeforeClass + public static void start() { + NetUtil.assumeSudo(); + } + + // 192.0.2.0 is reserved for documentation, so I'm pretty sure this won't exist on any system. (It shouldn't at least) + private static final String LIVE_IP = "192.0.2.0"; + + private int beforeTime; + + @Override + public void setUp() throws Exception { + // beforeTime = NettyConnection.getLockTimeout(); + // NettyConnection.setLockTimeout(1000); + NetUtil.netUp(LIVE_IP); + super.setUp(); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + // NettyConnection.setLockTimeout(beforeTime); + } + + @Override + protected TransportConfiguration getAcceptorTransportConfiguration(final boolean live) { + return getNettyAcceptorTransportConfiguration(live); + } + + @Override + protected TransportConfiguration getConnectorTransportConfiguration(final boolean live) { + return getNettyConnectorTransportConfiguration(live); + } + + protected ClientSession createSession(ClientSessionFactory sf1, + boolean autoCommitSends, + boolean autoCommitAcks, + int ackBatchSize) throws Exception { + return addClientSession(sf1.createSession(autoCommitSends, autoCommitAcks, ackBatchSize)); + } + + protected ClientSession createSession(ClientSessionFactory sf1, + boolean autoCommitSends, + boolean autoCommitAcks) throws Exception { + return addClientSession(sf1.createSession(autoCommitSends, autoCommitAcks)); + } + + protected ClientSession createSession(ClientSessionFactory sf1) throws Exception { + return addClientSession(sf1.createSession()); + } + + protected ClientSession createSession(ClientSessionFactory sf1, + boolean xa, + boolean autoCommitSends, + boolean autoCommitAcks) throws Exception { + return addClientSession(sf1.createSession(xa, autoCommitSends, autoCommitAcks)); + } + + @Override + protected TransportConfiguration getNettyAcceptorTransportConfiguration(final boolean live) { + Map server1Params = new HashMap<>(); + + if (live) { + server1Params.put(TransportConstants.PORT_PROP_NAME, TransportConstants.DEFAULT_PORT); + server1Params.put(TransportConstants.HOST_PROP_NAME, LIVE_IP); + } else { + server1Params.put(TransportConstants.PORT_PROP_NAME, TransportConstants.DEFAULT_PORT); + server1Params.put(TransportConstants.HOST_PROP_NAME, "localhost"); + } + + return new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, server1Params); + } + + @Override + protected TransportConfiguration getNettyConnectorTransportConfiguration(final boolean live) { + Map server1Params = new HashMap<>(); + + if (live) { + server1Params.put(TransportConstants.PORT_PROP_NAME, TransportConstants.DEFAULT_PORT); + server1Params.put(TransportConstants.HOST_PROP_NAME, LIVE_IP); + } else { + server1Params.put(TransportConstants.PORT_PROP_NAME, TransportConstants.DEFAULT_PORT); + server1Params.put(TransportConstants.HOST_PROP_NAME, "localhost"); + } + + server1Params.put(TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); + + return new TransportConfiguration(NETTY_CONNECTOR_FACTORY, server1Params); + } + + @Test + public void testFailoverAfterNetFailure() throws Exception { + final AtomicInteger sentMessages = new AtomicInteger(0); + final AtomicInteger blockedAt = new AtomicInteger(0); + + Assert.assertTrue(NetUtil.checkIP(LIVE_IP)); + Map params = new HashMap<>(); + params.put(TransportConstants.HOST_PROP_NAME, LIVE_IP); + params.put(TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); + TransportConfiguration tc = createTransportConfiguration(true, false, params); + + final AtomicInteger countSent = new AtomicInteger(0); + + liveServer.addInterceptor(new Interceptor() { + @Override + public boolean intercept(Packet packet, RemotingConnection connection) throws ActiveMQException { + //System.out.println("Received " + packet); + if (packet instanceof SessionSendMessage) { + + if (countSent.incrementAndGet() == 500) { + try { + NetUtil.netDown(LIVE_IP); + System.out.println("Blocking traffic"); + // Thread.sleep(3000); // this is important to let stuff to block + liveServer.crash(true, false); + } catch (Exception e) { + e.printStackTrace(); + } + new Thread() { + @Override + public void run() { + try { + System.err.println("Stopping server"); + } catch (Exception e) { + e.printStackTrace(); + } + } + }.start(); + } + } + return true; + } + }); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithHA(tc)); + + locator.setBlockOnNonDurableSend(false); + locator.setBlockOnDurableSend(true); + locator.setBlockOnAcknowledge(false); + locator.setReconnectAttempts(-1); + locator.setConfirmationWindowSize(-1); + locator.setProducerWindowSize(-1); + locator.setClientFailureCheckPeriod(100); + locator.setConnectionTTL(1000); + ClientSessionFactoryInternal sfProducer = createSessionFactoryAndWaitForTopology(locator, 2); + sfProducer.addFailureListener(new SessionFailureListener() { + @Override + public void beforeReconnect(ActiveMQException exception) { + } + + @Override + public void connectionFailed(ActiveMQException exception, boolean failedOver, String scaleDownTargetNodeID) { + + } + + @Override + public void connectionFailed(ActiveMQException exception, boolean failedOver) { + + } + }); + + ClientSession sessionProducer = createSession(sfProducer, true, true, 0); + + sessionProducer.createQueue(FailoverTestBase.ADDRESS, FailoverTestBase.ADDRESS, null, true); + + ClientProducer producer = sessionProducer.createProducer(FailoverTestBase.ADDRESS); + + final int numMessages = 2001; + final CountDownLatch latchReceived = new CountDownLatch(numMessages); + + ClientSessionFactoryInternal sfConsumer = createSessionFactoryAndWaitForTopology(locator, 2); + + final ClientSession sessionConsumer = createSession(sfConsumer, true, true, 0); + final ClientConsumer consumer = sessionConsumer.createConsumer(FailoverTestBase.ADDRESS); + + sessionConsumer.start(); + + final AtomicBoolean running = new AtomicBoolean(true); + + final Thread t = new Thread() { + @Override + public void run() { + int received = 0; + int errors = 0; + while (running.get() && received < numMessages) { + try { + ClientMessage msgReceived = consumer.receive(500); + if (msgReceived != null) { + latchReceived.countDown(); + msgReceived.acknowledge(); + if (received++ % 100 == 0) { + System.out.println("Received " + received); + sessionConsumer.commit(); + } + } else { + System.out.println("Null"); + } + } catch (Throwable e) { + errors++; + if (errors > 10) { + break; + } + e.printStackTrace(); + } + } + } + }; + + t.start(); + + for (sentMessages.set(0); sentMessages.get() < numMessages; sentMessages.incrementAndGet()) { + do { + try { + if (sentMessages.get() % 100 == 0) { + System.out.println("Sent " + sentMessages.get()); + } + producer.send(createMessage(sessionProducer, sentMessages.get(), true)); + break; + } catch (Exception e) { + sentMessages.decrementAndGet(); + new Exception("Exception on ending", e).printStackTrace(); + } + } + while (true); + } + + // these may never be received. doing the count down where we blocked. + for (int i = 0; i < blockedAt.get(); i++) { + latchReceived.countDown(); + } + + Assert.assertTrue(latchReceived.await(1, TimeUnit.MINUTES)); + + running.set(false); + + t.join(); + } + + + private int countTopologyMembers(Topology topology) { + int count = 0; + for (TopologyMember m : topology.getMembers()) { + count++; + if (m.getBackup() != null) { + count++; + } + } + + return count; + } + + @Test + public void testNetFailureConsume() throws Exception { + final AtomicInteger sentMessages = new AtomicInteger(0); + final AtomicInteger blockedAt = new AtomicInteger(0); + + Assert.assertTrue(NetUtil.checkIP(LIVE_IP)); + Map params = new HashMap<>(); + params.put(TransportConstants.HOST_PROP_NAME, LIVE_IP); + params.put(TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); + TransportConfiguration tc = createTransportConfiguration(true, false, params); + + final AtomicInteger countSent = new AtomicInteger(0); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithHA(tc)); + + locator.setBlockOnNonDurableSend(false); + locator.setBlockOnDurableSend(false); + locator.setBlockOnAcknowledge(false); + locator.setReconnectAttempts(-1); + locator.setConfirmationWindowSize(-1); + locator.setProducerWindowSize(-1); + locator.setClientFailureCheckPeriod(100); + locator.setConnectionTTL(1000); + ClientSessionFactoryInternal sfProducer = createSessionFactoryAndWaitForTopology(locator, 2); + + Wait.assertEquals(2, () -> countTopologyMembers(locator.getTopology())); + + sfProducer.addFailureListener(new SessionFailureListener() { + @Override + public void beforeReconnect(ActiveMQException exception) { + } + + @Override + public void connectionFailed(ActiveMQException exception, boolean failedOver, String scaleDownTargetNodeID) { + + } + + @Override + public void connectionFailed(ActiveMQException exception, boolean failedOver) { + + } + }); + + ClientSession sessionProducer = createSession(sfProducer, true, true, 0); + + sessionProducer.createQueue(FailoverTestBase.ADDRESS, FailoverTestBase.ADDRESS, null, true); + + ClientProducer producer = sessionProducer.createProducer(FailoverTestBase.ADDRESS); + + final int numMessages = 2001; + final CountDownLatch latchReceived = new CountDownLatch(numMessages); + + ClientSessionFactoryInternal sfConsumer = createSessionFactoryAndWaitForTopology(locator, 2); + + final ClientSession sessionConsumer = createSession(sfConsumer, true, true, 0); + final ClientConsumer consumer = sessionConsumer.createConsumer(FailoverTestBase.ADDRESS); + + sessionConsumer.start(); + + final AtomicBoolean running = new AtomicBoolean(true); + + final Thread t = new Thread() { + @Override + public void run() { + int received = 0; + int errors = 0; + while (running.get() && received < numMessages) { + try { + ClientMessage msgReceived = consumer.receive(500); + if (msgReceived != null) { + latchReceived.countDown(); + msgReceived.acknowledge(); + if (++received % 100 == 0) { + + if (received == 300) { + System.out.println("Shutting down IP"); + NetUtil.netDown(LIVE_IP); + liveServer.crash(true, false); + } + System.out.println("Received " + received); + sessionConsumer.commit(); + } + } else { + System.out.println("Null"); + } + } catch (Throwable e) { + errors++; + if (errors > 10) { + break; + } + e.printStackTrace(); + } + } + } + }; + + + for (sentMessages.set(0); sentMessages.get() < numMessages; sentMessages.incrementAndGet()) { + do { + try { + if (sentMessages.get() % 100 == 0) { + System.out.println("Sent " + sentMessages.get()); + } + producer.send(createMessage(sessionProducer, sentMessages.get(), true)); + break; + } catch (Exception e) { + sentMessages.decrementAndGet(); + new Exception("Exception on ending", e).printStackTrace(); + } + } + while (true); + } + + sessionProducer.close(); + + + t.start(); + + // these may never be received. doing the count down where we blocked. + for (int i = 0; i < blockedAt.get(); i++) { + latchReceived.countDown(); + } + + Assert.assertTrue(latchReceived.await(1, TimeUnit.MINUTES)); + + running.set(false); + + t.join(); + } + + @Test + public void testFailoverCreateSessionOnFailure() throws Exception { + final AtomicInteger sentMessages = new AtomicInteger(0); + final AtomicInteger blockedAt = new AtomicInteger(0); + + Assert.assertTrue(NetUtil.checkIP(LIVE_IP)); + Map params = new HashMap<>(); + params.put(TransportConstants.HOST_PROP_NAME, LIVE_IP); + params.put(TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); + TransportConfiguration tc = createTransportConfiguration(true, false, params); + + final AtomicInteger countSent = new AtomicInteger(0); + + final CountDownLatch latchDown = new CountDownLatch(1); + + liveServer.addInterceptor(new Interceptor() { + @Override + public boolean intercept(Packet packet, RemotingConnection connection) throws ActiveMQException { + //System.out.println("Received " + packet); + if (packet instanceof CreateSessionMessage) { + + if (countSent.incrementAndGet() == 50) { + try { + NetUtil.netDown(LIVE_IP); + System.out.println("Blocking traffic"); + Thread.sleep(3000); // this is important to let stuff to block + blockedAt.set(sentMessages.get()); + latchDown.countDown(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return true; + } + }); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithHA(tc)); + //locator.setDebugReconnects("CF_retry"); + + locator.setBlockOnNonDurableSend(false); + locator.setBlockOnDurableSend(false); + locator.setBlockOnAcknowledge(false); + locator.setReconnectAttempts(-1); + locator.setConfirmationWindowSize(-1); + locator.setProducerWindowSize(-1); + locator.setClientFailureCheckPeriod(100); + locator.setConnectionTTL(1000); + final ClientSessionFactoryInternal sessionFactory = createSessionFactoryAndWaitForTopology(locator, 2); + final AtomicInteger failed = new AtomicInteger(0); + sessionFactory.addFailureListener(new SessionFailureListener() { + @Override + public void beforeReconnect(ActiveMQException exception) { + if (failed.incrementAndGet() == 1) { + Thread.currentThread().interrupt(); + } + new Exception("producer before reconnect", exception).printStackTrace(); + } + + @Override + public void connectionFailed(ActiveMQException exception, boolean failedOver) { + + } + + @Override + public void connectionFailed(ActiveMQException exception, boolean failedOver, String scaleDownTargetNodeID) { + + } + }); + final int numSessions = 100; + final CountDownLatch latchCreated = new CountDownLatch(numSessions); + + final AtomicBoolean running = new AtomicBoolean(true); + + final Thread t = new Thread("session-creator") { + @Override + public void run() { + int received = 0; + int errors = 0; + while (running.get() && received < numSessions) { + try { + ClientSession session = sessionFactory.createSession(); + System.out.println("Creating session, currentLatch = " + latchCreated.getCount()); + session.close(); + latchCreated.countDown(); + } catch (Throwable e) { + e.printStackTrace(); + errors++; + } + } + } + }; + + t.start(); + + Assert.assertTrue(latchDown.await(1, TimeUnit.MINUTES)); + + Thread.sleep(1000); + + System.out.println("Server crashed now!!!"); + + liveServer.crash(true, false); + + try { + Assert.assertTrue(latchCreated.await(5, TimeUnit.MINUTES)); + + } finally { + running.set(false); + + t.join(TimeUnit.SECONDS.toMillis(30)); + } + } + + @Test + public void testInterruptFailingThread() throws Exception { + final AtomicInteger sentMessages = new AtomicInteger(0); + final AtomicInteger blockedAt = new AtomicInteger(0); + + Assert.assertTrue(NetUtil.checkIP(LIVE_IP)); + Map params = new HashMap<>(); + params.put(TransportConstants.HOST_PROP_NAME, LIVE_IP); + params.put(TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); + TransportConfiguration tc = createTransportConfiguration(true, false, params); + + final AtomicInteger countSent = new AtomicInteger(0); + + final CountDownLatch latchBlocked = new CountDownLatch(1); + + liveServer.addInterceptor(new Interceptor() { + @Override + public boolean intercept(Packet packet, RemotingConnection connection) throws ActiveMQException { + //System.out.println("Received " + packet); + if (packet instanceof SessionSendMessage) { + + if (countSent.incrementAndGet() == 50) { + try { + NetUtil.netDown(LIVE_IP); + System.out.println("Blocking traffic"); + Thread.sleep(3000); // this is important to let stuff to block + blockedAt.set(sentMessages.get()); + latchBlocked.countDown(); + } catch (Exception e) { + e.printStackTrace(); + } + // new Thread() + // { + // public void run() + // { + // try + // { + // System.err.println("Stopping server"); + // // liveServer.stop(); + // liveServer.crash(true, false); + // } + // catch (Exception e) + // { + // e.printStackTrace(); + // } + // } + // }.start(); + } + } + return true; + } + }); + + final CountDownLatch failing = new CountDownLatch(1); + final HashSet setThread = new HashSet<>(); + + ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithHA(tc)); + + locator.setBlockOnNonDurableSend(false); + locator.setBlockOnDurableSend(false); + locator.setBlockOnAcknowledge(false); + locator.setReconnectAttempts(-1); + locator.setConfirmationWindowSize(-1); + locator.setProducerWindowSize(-1); + locator.setClientFailureCheckPeriod(100); + locator.setConnectionTTL(1000); + ClientSessionFactoryInternal sfProducer = createSessionFactoryAndWaitForTopology(locator, 2); + sfProducer.addFailureListener(new SessionFailureListener() { + @Override + public void beforeReconnect(ActiveMQException exception) { + setThread.add(Thread.currentThread()); + failing.countDown(); + } + + @Override + public void connectionFailed(ActiveMQException exception, boolean failedOver) { + + } + + @Override + public void connectionFailed(ActiveMQException exception, boolean failedOver, String scaleDownTargetNodeID) { + + } + }); + + final ClientSession sessionProducer = createSession(sfProducer, true, true, 0); + + sessionProducer.createQueue(FailoverTestBase.ADDRESS, FailoverTestBase.ADDRESS, null, true); + + final ClientProducer producer = sessionProducer.createProducer(FailoverTestBase.ADDRESS); + + final int numMessages = 10000; + + final AtomicBoolean running = new AtomicBoolean(true); + final CountDownLatch messagesSentlatch = new CountDownLatch(numMessages); + + Thread t = new Thread("sendingThread") { + @Override + public void run() { + + while (sentMessages.get() < numMessages && running.get()) { + try { + if (sentMessages.get() % 10 == 0) { + System.out.println("Sent " + sentMessages.get()); + } + producer.send(createMessage(sessionProducer, sentMessages.get(), true)); + sentMessages.incrementAndGet(); + messagesSentlatch.countDown(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + } + }; + + t.start(); + + Assert.assertTrue(latchBlocked.await(1, TimeUnit.MINUTES)); + + Assert.assertTrue(failing.await(1, TimeUnit.MINUTES)); + + for (int i = 0; i < 5; i++) { + for (Thread tint : setThread) { + tint.interrupt(); + } + Thread.sleep(500); + } + + liveServer.crash(true, false); + + Assert.assertTrue(messagesSentlatch.await(3, TimeUnit.MINUTES)); + + running.set(false); + + t.join(); + } + +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/network/NetUtil.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/network/NetUtil.java new file mode 100644 index 00000000000..1d29b3857f6 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/network/NetUtil.java @@ -0,0 +1,226 @@ +/* + * 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.artemis.tests.util.network; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.activemq.artemis.core.server.NetworkHealthCheck; +import org.jboss.logging.Logger; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; + +public class NetUtil { + + public static boolean checkIP(String ip) throws Exception { + InetAddress ipAddress = null; + try { + ipAddress = InetAddress.getByName(ip); + } catch (Exception e) { + e.printStackTrace(); // not supposed to happen + return false; + } + NetworkHealthCheck healthCheck = new NetworkHealthCheck(null, 100, 100); + return healthCheck.check(ipAddress); + } + + public static AtomicInteger nextDevice = new AtomicInteger(0); + + // IP / device (device being only valid on linux) + public static Map networks = new ConcurrentHashMap<>(); + + private enum OS { + MAC, LINUX, NON_SUPORTED; + } + + static final OS osUsed; + static final String user = System.getProperty("user.name"); + + static { + OS osTmp; + + String propOS = System.getProperty("os.name").toUpperCase(); + + if (propOS.contains("MAC")) { + osTmp = OS.MAC; + } else if (propOS.contains("LINUX")) { + osTmp = OS.LINUX; + } else { + osTmp = OS.NON_SUPORTED; + } + + osUsed = osTmp; + } + + public static void assumeSudo() { + Assume.assumeTrue("non supported OS", osUsed != OS.NON_SUPORTED); + if (!canSudo()) { + System.out.println("Add the following at the end of your /etc/sudoers (use the visudo command)"); + System.out.println("# ------------------------------------------------------- "); + System.out.println(user + " ALL = NOPASSWD: /sbin/ifconfig"); + System.out.println("# ------------------------------------------------------- "); + Assume.assumeFalse(true); + } + } + + public static void cleanup() { + nextDevice.set(0); + + Set entrySet = networks.entrySet(); + Iterator iter = entrySet.iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry) iter.next(); + try { + netDown(entry.getKey(), entry.getValue()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public static void netUp(String ip) throws Exception { + String deviceID = "lo:" + nextDevice.incrementAndGet(); + if (osUsed == OS.MAC) { + if (runCommand("sudo", "-n", "ifconfig", "lo0", "alias", ip) != 0) { + Assert.fail("Cannot sudo ifconfig for ip " + ip); + } + networks.put(ip, "lo0"); + } else if (osUsed == OS.LINUX) { + if (runCommand("sudo", "-n", "ifconfig", deviceID, ip, "netmask", "255.0.0.0") != 0) { + Assert.fail("Cannot sudo ifconfig for ip " + ip); + } + networks.put(ip, deviceID); + } else { + Assert.fail("OS not supported"); + } + } + + public static void netDown(String ip) throws Exception { + String device = networks.remove(ip); + Assert.assertNotNull("ip " + ip + "wasn't set up before", device); + netDown(ip, device); + + } + + private static void netDown(String ip, String device) throws Exception { + if (osUsed == OS.MAC) { + if (runCommand("sudo", "-n", "ifconfig", "lo0", "-alias", ip) != 0) { + Assert.fail("Cannot sudo ifconfig for ip " + ip); + } + } else if (osUsed == OS.LINUX) { + if (runCommand("sudo", "-n", "ifconfig", device, "down") != 0) { + Assert.fail("Cannot sudo ifconfig for ip " + ip); + } + } else { + Assert.fail("OS not supported"); + } + } + + private static final Logger logger = Logger.getLogger(NetUtil.class); + + public static int runCommand(String... command) throws Exception { + return runCommand(10, TimeUnit.SECONDS, command); + } + + public static int runCommand(long timeout, TimeUnit timeoutUnit, String... command) throws Exception { + + logCommand(command); + + // it did not work with a simple isReachable, it could be because there's no root access, so we will try ping executable + ProcessBuilder processBuilder = new ProcessBuilder(command); + final Process process = processBuilder.start(); + + Thread t = new Thread() { + @Override + public void run() { + try { + readStream(process.getInputStream(), true); + } catch (Exception dontCare) { + + } + } + }; + Thread t2 = new Thread() { + @Override + public void run() { + try { + readStream(process.getErrorStream(), true); + } catch (Exception dontCare) { + + } + } + }; + t2.start(); + + int value = process.waitFor(); + + t.join(timeoutUnit.toMillis(timeout)); + Assert.assertFalse(t.isAlive()); + t2.join(timeoutUnit.toMillis(timeout)); + + return value; + } + + private static void logCommand(String[] command) { + StringBuffer logCommand = new StringBuffer(); + for (String c : command) { + logCommand.append(c + " "); + } + System.out.println("NetUTIL command::" + logCommand.toString()); + } + + public static boolean canSudo() { + try { + return runCommand("sudo", "-n", "ifconfig") == 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + @Test + public void testCanSudo() throws Exception { + Assert.assertTrue(canSudo()); + } + + private static void readStream(InputStream stream, boolean error) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + + String inputLine; + while ((inputLine = reader.readLine()) != null) { + if (error) { + logger.warn(inputLine); + } else { + logger.trace(inputLine); + } + } + + reader.close(); + } + +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/network/NetUtilResource.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/network/NetUtilResource.java new file mode 100644 index 00000000000..0f2abd93e15 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/util/network/NetUtilResource.java @@ -0,0 +1,29 @@ +/* + * 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.artemis.tests.util.network; + +import org.junit.rules.ExternalResource; + +public class NetUtilResource extends ExternalResource { + + @Override + protected void after() { + super.after(); + NetUtil.cleanup(); + } +} From 24fa0f920347f49270d1630b86b8b01987e66b92 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Thu, 6 Sep 2018 12:52:25 -0500 Subject: [PATCH 189/207] ARTEMIS-2072 refactor logic to fix tests --- .../amqp/broker/AMQPSessionCallback.java | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java index b32a4c74b41..6b163ae8ca3 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPSessionCallback.java @@ -45,6 +45,7 @@ import org.apache.activemq.artemis.core.server.ServerSession; import org.apache.activemq.artemis.core.server.impl.AddressInfo; import org.apache.activemq.artemis.core.server.impl.ServerConsumerImpl; +import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.core.transaction.Transaction; import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPException; import org.apache.activemq.artemis.protocol.amqp.exceptions.ActiveMQAMQPInternalErrorException; @@ -306,33 +307,39 @@ public QueueQueryResult queueQuery(SimpleString queueName, RoutingType routingTy public boolean checkAddressAndAutocreateIfPossible(SimpleString address, RoutingType routingType) throws Exception { - AddressInfo addressInfo = manager.getServer().getAddressInfo(address); - - // if the address exists go ahead and return - if (addressInfo != null) { - return true; - } - - // if the address and/or queue don't exist then create them if possible - if (routingType == RoutingType.MULTICAST && addressInfo == null) { - if (manager.getServer().getAddressSettingsRepository().getMatch(address.toString()).isAutoCreateAddresses()) { - try { - serverSession.createAddress(address, routingType, true); - } catch (ActiveMQAddressExistsException e) { - // The address may have been created by another thread in the mean time. Catch and do nothing. + boolean result = false; + SimpleString unPrefixedAddress = serverSession.removePrefix(address); + AddressSettings addressSettings = manager.getServer().getAddressSettingsRepository().getMatch(unPrefixedAddress.toString()); + + if (routingType == RoutingType.MULTICAST) { + if (manager.getServer().getAddressInfo(unPrefixedAddress) == null) { + if (addressSettings.isAutoCreateAddresses()) { + try { + serverSession.createAddress(address, routingType, true); + } catch (ActiveMQAddressExistsException e) { + // The address may have been created by another thread in the mean time. Catch and do nothing. + } + result = true; } + } else { + result = true; } - } else if (routingType == RoutingType.ANYCAST && manager.getServer().locateQueue(address) == null) { - if (manager.getServer().getAddressSettingsRepository().getMatch(address.toString()).isAutoCreateQueues()) { - try { - serverSession.createQueue(address, address, routingType, null, false, true, true); - } catch (ActiveMQQueueExistsException e) { - // The queue may have been created by another thread in the mean time. Catch and do nothing. + } else if (routingType == RoutingType.ANYCAST) { + if (manager.getServer().locateQueue(unPrefixedAddress) == null) { + if (addressSettings.isAutoCreateQueues()) { + try { + serverSession.createQueue(address, address, routingType, null, false, true, true); + } catch (ActiveMQQueueExistsException e) { + // The queue may have been created by another thread in the mean time. Catch and do nothing. + } + result = true; } + } else { + result = true; } } - return manager.getServer().getAddressInfo(address) != null; + return result; } From dbfdc18f49cfa3fed89f722d78aa49dd170028b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Thu, 6 Sep 2018 18:41:21 +0100 Subject: [PATCH 190/207] ARTEMIS-2065 Change routing-type isnt destructive. Revert previous fix Keep original ConfigChangeTest Apply new non-destructive fix. Enhance tests to ensure messages in queues are not lost either on reload when running or when config changed on-restart (e.g. queue i not destroyed) --- .../core/server/ActiveMQServerLogger.java | 5 +- .../core/server/impl/ActiveMQServerImpl.java | 54 +++++++++++++------ .../tests/integration/jms/RedeployTest.java | 32 ++++++++--- .../persistence/ConfigChangeTest.java | 41 +++++++------- .../reload-queue-routingtype-updated.xml | 18 ++++--- .../resources/reload-queue-routingtype.xml | 17 +++--- 6 files changed, 107 insertions(+), 60 deletions(-) diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java index 2cc00d39931..8c18a1562bf 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java @@ -44,7 +44,6 @@ import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; import org.apache.activemq.artemis.api.core.Pair; -import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.client.impl.ServerLocatorInternal; import org.apache.activemq.artemis.core.config.Configuration; @@ -1903,8 +1902,8 @@ void slowConsumerDetected(String sessionID, void undeployAddress(SimpleString addressName); @LogMessage(level = Logger.Level.INFO) - @Message(id = 224077, value = "Undeploying {0} queue {1}", format = Message.Format.MESSAGE_FORMAT) - void undeployQueue(RoutingType routingType, SimpleString queueName); + @Message(id = 224077, value = "Undeploying queue {0}", format = Message.Format.MESSAGE_FORMAT) + void undeployQueue(SimpleString queueName); @LogMessage(level = Logger.Level.WARN) @Message(id = 224078, value = "The size of duplicate cache detection () appears to be too large {0}. It should be no greater than the number of messages that can be squeezed into conformation buffer () {1}.", format = Message.Format.MESSAGE_FORMAT) 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 715f984ad24..498f4e9def8 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 @@ -2601,9 +2601,6 @@ public void run() { }, 0, dumpInfoInterval, TimeUnit.MILLISECONDS); } - // Undeploy any addresses and queues not in config - undeployAddressesAndQueueNotInConfiguration(); - // Deploy the rest of the stuff // Deploy predefined addresses @@ -2612,6 +2609,9 @@ public void run() { // Deploy any predefined queues deployQueuesFromConfiguration(); + // Undeploy any addresses and queues not in config + undeployAddressesAndQueueNotInConfiguration(); + // We need to call this here, this gives any dependent server a chance to deploy its own addresses // this needs to be done before clustering is fully activated callActivateCallbacks(); @@ -2695,31 +2695,28 @@ private void undeployAddressesAndQueueNotInConfiguration() throws Exception { private void undeployAddressesAndQueueNotInConfiguration(Configuration configuration) throws Exception { Set addressesInConfig = configuration.getAddressConfigurations().stream() - .map(CoreAddressConfiguration::getName) - .collect(Collectors.toSet()); + .map(CoreAddressConfiguration::getName) + .collect(Collectors.toSet()); - Set queuesInConfig = new HashSet<>(); - for (CoreAddressConfiguration cac : configuration.getAddressConfigurations()) { - for (CoreQueueConfiguration cqc : cac.getQueueConfigurations()) { - // combine the routing-type and queue name as the unique identifier as it's possible to change the routing-type without changing the name - queuesInConfig.add(cqc.getRoutingType().toString() + cqc.getName()); - } - } + Set queuesInConfig = configuration.getAddressConfigurations().stream() + .map(CoreAddressConfiguration::getQueueConfigurations) + .flatMap(List::stream).map(CoreQueueConfiguration::getName) + .collect(Collectors.toSet()); for (SimpleString addressName : listAddressNames()) { AddressSettings addressSettings = getAddressSettingsRepository().getMatch(addressName.toString()); if (!addressesInConfig.contains(addressName.toString()) && addressSettings.getConfigDeleteAddresses() == DeletionPolicy.FORCE) { for (Queue queue : listQueues(addressName)) { - ActiveMQServerLogger.LOGGER.undeployQueue(queue.getRoutingType(), queue.getName()); + ActiveMQServerLogger.LOGGER.undeployQueue(queue.getName()); queue.deleteQueue(true); } ActiveMQServerLogger.LOGGER.undeployAddress(addressName); removeAddressInfo(addressName, null); } else if (addressSettings.getConfigDeleteQueues() == DeletionPolicy.FORCE) { for (Queue queue : listConfiguredQueues(addressName)) { - if (!queuesInConfig.contains(queue.getRoutingType().toString() + queue.getName().toString())) { - ActiveMQServerLogger.LOGGER.undeployQueue(queue.getRoutingType(), queue.getName()); + if (!queuesInConfig.contains(queue.getName().toString())) { + ActiveMQServerLogger.LOGGER.undeployQueue(queue.getName()); queue.deleteQueue(true); } } @@ -2747,15 +2744,38 @@ private void deployAddressesFromConfiguration(Configuration configuration) throw for (CoreAddressConfiguration config : configuration.getAddressConfigurations()) { try { ActiveMQServerLogger.LOGGER.deployAddress(config.getName(), config.getRoutingTypes().toString()); - AddressInfo info = new AddressInfo(SimpleString.toSimpleString(config.getName()), config.getRoutingTypes()); - addOrUpdateAddressInfo(info); + SimpleString address = SimpleString.toSimpleString(config.getName()); + + AddressInfo tobe = new AddressInfo(address, config.getRoutingTypes()); + + //During this stage until all queues re-configured we combine the current (if exists) with to-be routing types to allow changes in queues + AddressInfo current = getAddressInfo(address); + AddressInfo merged = new AddressInfo(address, tobe.getRoutingType()); + if (current != null) { + merged.getRoutingTypes().addAll(current.getRoutingTypes()); + } + addOrUpdateAddressInfo(merged); + deployQueuesFromListCoreQueueConfiguration(config.getQueueConfigurations()); + + //Now all queues updated we apply the actual address info expected tobe. + addOrUpdateAddressInfo(tobe); } catch (Exception e) { ActiveMQServerLogger.LOGGER.problemDeployingAddress(config.getName(), e.getMessage()); } } } + private AddressInfo mergedRoutingTypes(SimpleString address, AddressInfo... addressInfos) { + EnumSet mergedRoutingTypes = EnumSet.noneOf(RoutingType.class); + for (AddressInfo addressInfo : addressInfos) { + if (addressInfo != null) { + mergedRoutingTypes.addAll(addressInfo.getRoutingTypes()); + } + } + return new AddressInfo(address, mergedRoutingTypes); + } + private void deployQueuesFromListCoreQueueConfiguration(List queues) throws Exception { for (CoreQueueConfiguration config : queues) { try { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java index 1d8e7b93f3a..ead96d5353f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java @@ -17,12 +17,6 @@ package org.apache.activemq.artemis.tests.integration.jms; -import javax.jms.Connection; -import javax.jms.JMSException; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Queue; -import javax.jms.Session; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; @@ -32,6 +26,16 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSContext; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.postoffice.QueueBinding; @@ -289,9 +293,14 @@ public void run() { embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); try { + ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://0.0.0.0:61616"); + try (JMSContext context = connectionFactory.createContext()) { + context.createProducer().send(context.createQueue("myAddress"), "hello"); + } + latch.await(10, TimeUnit.SECONDS); Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "myAddress")); - Assert.assertEquals(RoutingType.MULTICAST, getQueue(embeddedActiveMQ, "myQueue").getRoutingType()); + Assert.assertEquals(RoutingType.ANYCAST, getQueue(embeddedActiveMQ, "myQueue").getRoutingType()); Files.copy(url2.openStream(), brokerXML, StandardCopyOption.REPLACE_EXISTING); brokerXML.toFile().setLastModified(System.currentTimeMillis() + 1000); @@ -300,7 +309,14 @@ public void run() { latch.await(10, TimeUnit.SECONDS); Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "myAddress")); - Assert.assertEquals(RoutingType.ANYCAST, getQueue(embeddedActiveMQ, "myQueue").getRoutingType()); + Assert.assertEquals(RoutingType.MULTICAST, getQueue(embeddedActiveMQ, "myQueue").getRoutingType()); + + //Ensures the queue isnt detroyed by checking message sent before change is consumable after (e.g. no message loss) + try (JMSContext context = connectionFactory.createContext()) { + Message message = context.createSharedDurableConsumer(context.createTopic("myAddress"), "myQueue").receive(); + assertEquals("hello", ((TextMessage) message).getText()); + } + } finally { embeddedActiveMQ.stop(); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java index 3a2264f1278..fefc735d9be 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java @@ -20,6 +20,10 @@ import java.util.ArrayList; import java.util.List; +import javax.jms.ConnectionFactory; +import javax.jms.JMSContext; +import javax.jms.Message; +import javax.jms.TextMessage; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.config.Configuration; @@ -27,7 +31,7 @@ import org.apache.activemq.artemis.core.config.CoreQueueConfiguration; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; -import org.apache.activemq.artemis.core.settings.impl.DeletionPolicy; +import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Test; @@ -37,21 +41,8 @@ public class ConfigChangeTest extends ActiveMQTestBase { @Test public void testChangeQueueRoutingTypeOnRestart() throws Exception { - internalTestChangeQueueRoutingTypeOnRestart(false); - } - - @Test - public void testChangeQueueRoutingTypeOnRestartNegative() throws Exception { - internalTestChangeQueueRoutingTypeOnRestart(true); - } - - public void internalTestChangeQueueRoutingTypeOnRestart(boolean negative) throws Exception { - // if negative == true then the queue's routing type should *not* change - Configuration configuration = createDefaultInVMConfig(); - configuration.addAddressesSetting("#", new AddressSettings() - .setConfigDeleteQueues(negative ? DeletionPolicy.OFF : DeletionPolicy.FORCE) - .setConfigDeleteAddresses(negative ? DeletionPolicy.OFF : DeletionPolicy.FORCE)); + configuration.addAddressesSetting("#", new AddressSettings()); List addressConfigurations = new ArrayList(); CoreAddressConfiguration addressConfiguration = new CoreAddressConfiguration() @@ -65,6 +56,14 @@ public void internalTestChangeQueueRoutingTypeOnRestart(boolean negative) throws configuration.setAddressConfigurations(addressConfigurations); server = createServer(true, configuration); server.start(); + + + ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://0"); + try (JMSContext context = connectionFactory.createContext()) { + context.createProducer().send(context.createQueue("myAddress"), "hello"); + } + + server.stop(); addressConfiguration = new CoreAddressConfiguration() @@ -77,10 +76,16 @@ public void internalTestChangeQueueRoutingTypeOnRestart(boolean negative) throws addressConfigurations.clear(); addressConfigurations.add(addressConfiguration); configuration.setAddressConfigurations(addressConfigurations); - server.start(); - assertEquals(negative ? RoutingType.ANYCAST : RoutingType.MULTICAST, server.getAddressInfo(SimpleString.toSimpleString("myAddress")).getRoutingType()); - assertEquals(negative ? RoutingType.ANYCAST : RoutingType.MULTICAST, server.locateQueue(SimpleString.toSimpleString("myQueue")).getRoutingType()); + assertEquals(RoutingType.MULTICAST, server.getAddressInfo(SimpleString.toSimpleString("myAddress")).getRoutingType()); + assertEquals(RoutingType.MULTICAST, server.locateQueue(SimpleString.toSimpleString("myQueue")).getRoutingType()); + + //Ensures the queue isnt detroyed by checking message sent before change is consumable after (e.g. no message loss) + try (JMSContext context = connectionFactory.createContext()) { + Message message = context.createSharedDurableConsumer(context.createTopic("myAddress"), "myQueue").receive(); + assertEquals("hello", ((TextMessage) message).getText()); + } + server.stop(); } } diff --git a/tests/integration-tests/src/test/resources/reload-queue-routingtype-updated.xml b/tests/integration-tests/src/test/resources/reload-queue-routingtype-updated.xml index e5bbe4fe969..a0313593b43 100644 --- a/tests/integration-tests/src/test/resources/reload-queue-routingtype-updated.xml +++ b/tests/integration-tests/src/test/resources/reload-queue-routingtype-updated.xml @@ -23,17 +23,21 @@ under the License. xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd"> - - - FORCE - - + false + + + + + tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576 + + +
- + - +
diff --git a/tests/integration-tests/src/test/resources/reload-queue-routingtype.xml b/tests/integration-tests/src/test/resources/reload-queue-routingtype.xml index 61ae86a9c8d..6737a05e451 100644 --- a/tests/integration-tests/src/test/resources/reload-queue-routingtype.xml +++ b/tests/integration-tests/src/test/resources/reload-queue-routingtype.xml @@ -23,17 +23,20 @@ under the License. xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd"> - - - FORCE - - + false + + + + + tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576 + +
- + - +
From 4b88f38b2df6b3c401542884ffe97c8a49069a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Fri, 7 Sep 2018 06:05:33 +0100 Subject: [PATCH 191/207] ARTEMIS-2076 Make Filter update-able Add Tests Add implementation inline with other queue updatable settings. Enhance tests to ensure queue is not destroyed during config change and messages in queue already are preserved --- .../management/ActiveMQServerControl.java | 1 + .../impl/ActiveMQServerControlImpl.java | 5 +- .../artemis/core/postoffice/PostOffice.java | 2 + .../postoffice/impl/LocalQueueBinding.java | 8 +- .../core/postoffice/impl/PostOfficeImpl.java | 5 + .../artemis/core/server/ActiveMQServer.java | 1 + .../activemq/artemis/core/server/Queue.java | 2 + .../core/server/impl/ActiveMQServerImpl.java | 8 +- .../artemis/core/server/impl/QueueImpl.java | 10 +- .../impl/ScheduledDeliveryHandlerTest.java | 4 + .../tests/integration/jms/RedeployTest.java | 97 +++++++++++++++++++ .../ActiveMQServerControlUsingCoreTest.java | 12 ++- .../persistence/ConfigChangeTest.java | 59 +++++++++++ .../resources/reload-queue-filter-updated.xml | 42 ++++++++ .../test/resources/reload-queue-filter.xml | 42 ++++++++ .../unit/core/postoffice/impl/FakeQueue.java | 5 + .../server/impl/fakes/FakePostOffice.java | 2 + 17 files changed, 290 insertions(+), 15 deletions(-) create mode 100644 tests/integration-tests/src/test/resources/reload-queue-filter-updated.xml create mode 100644 tests/integration-tests/src/test/resources/reload-queue-filter.xml diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java index 6ce945c4113..5719fb6698f 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java @@ -705,6 +705,7 @@ String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String @Operation(desc = "Update a queue", impact = MBeanOperationInfo.ACTION) String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String name, @Parameter(name = "routingType", desc = "The routing type used for this address, MULTICAST or ANYCAST") String routingType, + @Parameter(name = "filter", desc = "The filter to use on the queue") String filter, @Parameter(name = "maxConsumers", desc = "The maximum number of consumers allowed on this queue at any one time") Integer maxConsumers, @Parameter(name = "purgeOnNoConsumers", desc = "Delete this queue when the last consumer disconnects") Boolean purgeOnNoConsumers, @Parameter(name = "exclusive", desc = "If the queue should route exclusively to one consumer") Boolean exclusive, diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java index f708c1dfb6b..ec6b0dfd885 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java @@ -868,12 +868,13 @@ public String updateQueue(String name, Boolean purgeOnNoConsumers, Boolean exclusive, String user) throws Exception { - return updateQueue(name, routingType, maxConsumers, purgeOnNoConsumers, exclusive, null, null, user); + return updateQueue(name, routingType, null, maxConsumers, purgeOnNoConsumers, exclusive, null, null, user); } @Override public String updateQueue(String name, String routingType, + String filter, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, @@ -885,7 +886,7 @@ public String updateQueue(String name, clearIO(); try { - final Queue queue = server.updateQueue(name, routingType != null ? RoutingType.valueOf(routingType) : null, maxConsumers, purgeOnNoConsumers, exclusive, consumersBeforeDispatch, delayBeforeDispatch, user); + final Queue queue = server.updateQueue(name, routingType != null ? RoutingType.valueOf(routingType) : null, filter, maxConsumers, purgeOnNoConsumers, exclusive, consumersBeforeDispatch, delayBeforeDispatch, user); if (queue == null) { throw ActiveMQMessageBundle.BUNDLE.noSuchQueue(new SimpleString(name)); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java index ce1fcfd172d..d31e33b4ce5 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java @@ -24,6 +24,7 @@ import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.filter.Filter; import org.apache.activemq.artemis.core.server.ActiveMQComponent; import org.apache.activemq.artemis.core.server.MessageReference; import org.apache.activemq.artemis.core.server.Queue; @@ -66,6 +67,7 @@ default void reloadAddressInfo(AddressInfo addressInfo) throws Exception { QueueBinding updateQueue(SimpleString name, RoutingType routingType, + Filter filter, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/LocalQueueBinding.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/LocalQueueBinding.java index 176d6140712..79af5d075d7 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/LocalQueueBinding.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/LocalQueueBinding.java @@ -32,8 +32,6 @@ public class LocalQueueBinding implements QueueBinding { private final Queue queue; - private final Filter filter; - private final SimpleString clusterName; private SimpleString name; @@ -45,8 +43,6 @@ public LocalQueueBinding(final SimpleString address, final Queue queue, final Si this.name = queue.getName(); - filter = queue.getFilter(); - clusterName = queue.getName().concat(nodeID); } @@ -57,7 +53,7 @@ public long getID() { @Override public Filter getFilter() { - return filter; + return queue.getFilter(); } @Override @@ -158,7 +154,7 @@ public String toString() { ", queue=" + queue + ", filter=" + - filter + + getFilter() + ", name=" + name + ", clusterName=" + diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index 598c32b46b0..d4cde187934 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -465,6 +465,7 @@ private boolean internalAddressInfo(AddressInfo addressInfo, boolean reload) thr @Override public QueueBinding updateQueue(SimpleString name, RoutingType routingType, + Filter filter, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, @@ -522,6 +523,10 @@ public QueueBinding updateQueue(SimpleString name, changed = true; queue.setDelayBeforeDispatch(delayBeforeDispatch.longValue()); } + if (filter != null && !filter.equals(queue.getFilter())) { + changed = true; + queue.setFilter(filter); + } if (logger.isDebugEnabled()) { if (user == null && queue.getUser() != null) { logger.debug("Ignoring updating Queue to a NULL user"); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java index 1883362eaf6..488c6fdedeb 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java @@ -537,6 +537,7 @@ Queue updateQueue(String name, Queue updateQueue(String name, RoutingType routingType, + String filterString, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java index 70051c0cb99..63d39c7c16e 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java @@ -45,6 +45,8 @@ public interface Queue extends Bindable,CriticalComponent { Filter getFilter(); + void setFilter(Filter filter); + PageSubscription getPageSubscription(); RoutingType getRoutingType(); 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 498f4e9def8..dcb6e02d750 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 @@ -2790,7 +2790,7 @@ private void deployQueuesFromListCoreQueueConfiguration(List filterUpdater = AtomicReferenceFieldUpdater.newUpdater(QueueImpl.class, Filter.class, "filter"); public static final int REDISTRIBUTOR_BATCH_SIZE = 100; @@ -695,7 +696,12 @@ public void setRoutingType(RoutingType routingType) { @Override public Filter getFilter() { - return filter; + return filterUpdater.get(this); + } + + @Override + public void setFilter(Filter filter) { + filterUpdater.set(this, filter); } @Override diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java index 96de8c761be..b21781d2ab8 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java @@ -882,6 +882,10 @@ public Filter getFilter() { return null; } + @Override + public void setFilter(Filter filter) { + } + @Override public PageSubscription getPageSubscription() { return null; diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java index ead96d5353f..600eead68bd 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java @@ -38,6 +38,7 @@ import javax.jms.TextMessage; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.postoffice.Binding; import org.apache.activemq.artemis.core.postoffice.QueueBinding; import org.apache.activemq.artemis.core.security.Role; import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ; @@ -106,6 +107,102 @@ public void run() { } } + @Test + public void testRedeployFilter() throws Exception { + Path brokerXML = getTestDirfile().toPath().resolve("broker.xml"); + URL url1 = RedeployTest.class.getClassLoader().getResource("reload-queue-filter.xml"); + URL url2 = RedeployTest.class.getClassLoader().getResource("reload-queue-filter-updated.xml"); + Files.copy(url1.openStream(), brokerXML); + + EmbeddedActiveMQ embeddedActiveMQ = new EmbeddedActiveMQ(); + embeddedActiveMQ.setConfigResourcePath(brokerXML.toUri().toString()); + embeddedActiveMQ.start(); + + final ReusableLatch latch = new ReusableLatch(1); + + Runnable tick = new Runnable() { + @Override + public void run() { + latch.countDown(); + } + }; + + embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); + + try { + latch.await(10, TimeUnit.SECONDS); + + try (ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); + Connection connection = factory.createConnection(); + Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE)) { + connection.start(); + Queue queue = session.createQueue("myQueue"); + MessageProducer producer = session.createProducer(queue); + Message message = session.createMessage(); + message.setStringProperty("x", "x"); + producer.send(message); + MessageConsumer consumer = session.createConsumer(queue); + assertNotNull(consumer.receive(5000)); + consumer.close(); + } + + //Send a message that should remain in the queue (this ensures config change is non-destructive) + try (ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); + Connection connection = factory.createConnection(); + Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE)) { + connection.start(); + Queue queue = session.createQueue("myQueue"); + MessageProducer producer = session.createProducer(queue); + Message message = session.createTextMessage("hello"); + message.setStringProperty("x", "x"); + producer.send(message); + } + + Binding binding = embeddedActiveMQ.getActiveMQServer().getPostOffice().getBinding(SimpleString.toSimpleString("myQueue")); + + Files.copy(url2.openStream(), brokerXML, StandardCopyOption.REPLACE_EXISTING); + brokerXML.toFile().setLastModified(System.currentTimeMillis() + 1000); + latch.setCount(1); + embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); + latch.await(10, TimeUnit.SECONDS); + + Binding bindingAfterChange = embeddedActiveMQ.getActiveMQServer().getPostOffice().getBinding(SimpleString.toSimpleString("myQueue")); + + assertTrue("Instance should be the same (as should be non destructive)", binding == bindingAfterChange); + assertEquals(binding.getID(), bindingAfterChange.getID()); + + //Check that after the config change we can still consume a message that was sent before, ensuring config change was non-destructive of the queue. + try (ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); + Connection connection = factory.createConnection(); + Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE)) { + connection.start(); + Queue queue = session.createQueue("myQueue"); + MessageConsumer consumer = session.createConsumer(queue); + Message message = consumer.receive(5000); + assertNotNull(message); + assertEquals("hello", ((TextMessage)message).getText()); + consumer.close(); + } + + try (ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); + Connection connection = factory.createConnection(); + Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE)) { + connection.start(); + Queue queue = session.createQueue("myQueue"); + MessageProducer producer = session.createProducer(queue); + Message message = session.createMessage(); + message.setStringProperty("x", "y"); + producer.send(message); + MessageConsumer consumer = session.createConsumer(queue); + assertNotNull(consumer.receive(2000)); + consumer.close(); + } + + } finally { + embeddedActiveMQ.stop(); + } + } + @Test public void testRedeployWithFailover() throws Exception { EmbeddedActiveMQ live = new EmbeddedActiveMQ(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java index 2d78092a51c..f1e7051bd02 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java @@ -156,8 +156,16 @@ public String updateQueue(@Parameter(name = "name", desc = "Name of the queue") } @Override - public String updateQueue(String name, String routingType, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, Integer consumersBeforeDispatch, Long delayBeforeDispatch, String user) throws Exception { - return null; + public String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String name, + @Parameter(name = "routingType", desc = "The routing type used for this address, MULTICAST or ANYCAST") String routingType, + @Parameter(name = "filter", desc = "The filter to use on the queue") String filter, + @Parameter(name = "maxConsumers", desc = "The maximum number of consumers allowed on this queue at any one time") Integer maxConsumers, + @Parameter(name = "purgeOnNoConsumers", desc = "Delete this queue when the last consumer disconnects") Boolean purgeOnNoConsumers, + @Parameter(name = "exclusive", desc = "If the queue should route exclusively to one consumer") Boolean exclusive, + @Parameter(name = "consumersBeforeDispatch", desc = "Number of consumers needed before dispatch can start") Integer consumersBeforeDispatch, + @Parameter(name = "delayBeforeDispatch", desc = "Delay to wait before dispatching if number of consumers before dispatch is not met") Long delayBeforeDispatch, + @Parameter(name = "user", desc = "The user associated with this queue") String user) throws Exception { + return (String) proxy.invokeOperation("updateQueue", name, routingType, filter, maxConsumers, purgeOnNoConsumers, exclusive, consumersBeforeDispatch, delayBeforeDispatch, user); } @Override diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java index fefc735d9be..cc6a6f6484c 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/ConfigChangeTest.java @@ -88,4 +88,63 @@ public void testChangeQueueRoutingTypeOnRestart() throws Exception { server.stop(); } + + @Test + public void testChangeQueueFilterOnRestart() throws Exception { + final String filter1 = "x = 'x'"; + final String filter2 = "x = 'y'"; + + Configuration configuration = createDefaultInVMConfig( ); + configuration.addAddressesSetting("#", new AddressSettings()); + + List addressConfigurations = new ArrayList(); + CoreAddressConfiguration addressConfiguration = new CoreAddressConfiguration() + .setName("myAddress") + .addRoutingType(RoutingType.ANYCAST) + .addQueueConfiguration(new CoreQueueConfiguration() + .setName("myQueue") + .setAddress("myAddress") + .setFilterString(filter1) + .setRoutingType(RoutingType.ANYCAST)); + addressConfigurations.add(addressConfiguration); + configuration.setAddressConfigurations(addressConfigurations); + server = createServer(true, configuration); + server.start(); + + ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://0"); + try (JMSContext context = connectionFactory.createContext()) { + context.createProducer().setProperty("x", "x").send(context.createQueue("myAddress"), "hello"); + } + + long originalBindingId = server.getPostOffice().getBinding(SimpleString.toSimpleString("myQueue")).getID(); + + server.stop(); + + addressConfiguration = new CoreAddressConfiguration() + .setName("myAddress") + .addRoutingType(RoutingType.ANYCAST) + .addQueueConfiguration(new CoreQueueConfiguration() + .setName("myQueue") + .setAddress("myAddress") + .setFilterString(filter2) + .setRoutingType(RoutingType.ANYCAST)); + addressConfigurations.clear(); + addressConfigurations.add(addressConfiguration); + configuration.setAddressConfigurations(addressConfigurations); + + server.start(); + assertEquals(filter2, server.locateQueue(SimpleString.toSimpleString("myQueue")).getFilter().getFilterString().toString()); + + //Ensures the queue is not destroyed by checking message sent before change is consumable after (e.g. no message loss) + try (JMSContext context = connectionFactory.createContext()) { + Message message = context.createConsumer(context.createQueue("myAddress::myQueue")).receive(); + assertEquals("hello", ((TextMessage) message).getText()); + } + + long bindingId = server.getPostOffice().getBinding(SimpleString.toSimpleString("myQueue")).getID(); + assertEquals("Ensure the original queue is not destroyed by checking the binding id is the same", originalBindingId, bindingId); + + server.stop(); + + } } diff --git a/tests/integration-tests/src/test/resources/reload-queue-filter-updated.xml b/tests/integration-tests/src/test/resources/reload-queue-filter-updated.xml new file mode 100644 index 00000000000..37ef67d3889 --- /dev/null +++ b/tests/integration-tests/src/test/resources/reload-queue-filter-updated.xml @@ -0,0 +1,42 @@ + + + + + + + false + + + tcp://0.0.0.0:61616 + + + +
+ + + + + +
+
+
+
diff --git a/tests/integration-tests/src/test/resources/reload-queue-filter.xml b/tests/integration-tests/src/test/resources/reload-queue-filter.xml new file mode 100644 index 00000000000..47dbadc687c --- /dev/null +++ b/tests/integration-tests/src/test/resources/reload-queue-filter.xml @@ -0,0 +1,42 @@ + + + + + + + false + + + tcp://0.0.0.0:61616 + + + +
+ + + + + +
+
+
+
diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java index 71ced7f35a8..5297ab62093 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java @@ -426,6 +426,11 @@ public Filter getFilter() { return null; } + @Override + public void setFilter(Filter filter) { + + } + @Override public long getMessageCount() { return messageCount; diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java index 44e5823b2b7..0bbe8efeec8 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java @@ -24,6 +24,7 @@ import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.filter.Filter; import org.apache.activemq.artemis.core.persistence.impl.nullpm.NullStorageManager; import org.apache.activemq.artemis.core.postoffice.Binding; import org.apache.activemq.artemis.core.postoffice.Bindings; @@ -46,6 +47,7 @@ public class FakePostOffice implements PostOffice { @Override public QueueBinding updateQueue(SimpleString name, RoutingType routingType, + Filter filter, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, From 07aa0f732c61f1b99e5a89049eb075ccabf2fe73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Sat, 8 Sep 2018 02:26:38 +0100 Subject: [PATCH 192/207] NO-JIRA rename queue in test to avoid collision Avoids issue with other tests that could persist same queue name, making intermittent failure. --- .../artemis/tests/integration/jms/RedeployTest.java | 12 ++++++------ .../test/resources/reload-queue-filter-updated.xml | 4 ++-- .../src/test/resources/reload-queue-filter.xml | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java index 600eead68bd..07bc22e2a28 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java @@ -136,7 +136,7 @@ public void run() { Connection connection = factory.createConnection(); Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE)) { connection.start(); - Queue queue = session.createQueue("myQueue"); + Queue queue = session.createQueue("myFilterQueue"); MessageProducer producer = session.createProducer(queue); Message message = session.createMessage(); message.setStringProperty("x", "x"); @@ -151,14 +151,14 @@ public void run() { Connection connection = factory.createConnection(); Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE)) { connection.start(); - Queue queue = session.createQueue("myQueue"); + Queue queue = session.createQueue("myFilterQueue"); MessageProducer producer = session.createProducer(queue); Message message = session.createTextMessage("hello"); message.setStringProperty("x", "x"); producer.send(message); } - Binding binding = embeddedActiveMQ.getActiveMQServer().getPostOffice().getBinding(SimpleString.toSimpleString("myQueue")); + Binding binding = embeddedActiveMQ.getActiveMQServer().getPostOffice().getBinding(SimpleString.toSimpleString("myFilterQueue")); Files.copy(url2.openStream(), brokerXML, StandardCopyOption.REPLACE_EXISTING); brokerXML.toFile().setLastModified(System.currentTimeMillis() + 1000); @@ -166,7 +166,7 @@ public void run() { embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); latch.await(10, TimeUnit.SECONDS); - Binding bindingAfterChange = embeddedActiveMQ.getActiveMQServer().getPostOffice().getBinding(SimpleString.toSimpleString("myQueue")); + Binding bindingAfterChange = embeddedActiveMQ.getActiveMQServer().getPostOffice().getBinding(SimpleString.toSimpleString("myFilterQueue")); assertTrue("Instance should be the same (as should be non destructive)", binding == bindingAfterChange); assertEquals(binding.getID(), bindingAfterChange.getID()); @@ -176,7 +176,7 @@ public void run() { Connection connection = factory.createConnection(); Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE)) { connection.start(); - Queue queue = session.createQueue("myQueue"); + Queue queue = session.createQueue("myFilterQueue"); MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(5000); assertNotNull(message); @@ -188,7 +188,7 @@ public void run() { Connection connection = factory.createConnection(); Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE)) { connection.start(); - Queue queue = session.createQueue("myQueue"); + Queue queue = session.createQueue("myFilterQueue"); MessageProducer producer = session.createProducer(queue); Message message = session.createMessage(); message.setStringProperty("x", "y"); diff --git a/tests/integration-tests/src/test/resources/reload-queue-filter-updated.xml b/tests/integration-tests/src/test/resources/reload-queue-filter-updated.xml index 37ef67d3889..28a96a8c99a 100644 --- a/tests/integration-tests/src/test/resources/reload-queue-filter-updated.xml +++ b/tests/integration-tests/src/test/resources/reload-queue-filter-updated.xml @@ -30,9 +30,9 @@ under the License. -
+
- + diff --git a/tests/integration-tests/src/test/resources/reload-queue-filter.xml b/tests/integration-tests/src/test/resources/reload-queue-filter.xml index 47dbadc687c..bb247b4e20b 100644 --- a/tests/integration-tests/src/test/resources/reload-queue-filter.xml +++ b/tests/integration-tests/src/test/resources/reload-queue-filter.xml @@ -30,9 +30,9 @@ under the License. -
+
- + From 153e0a0260c9b143a2ed21b80b4fc07af5f18428 Mon Sep 17 00:00:00 2001 From: henock Date: Tue, 11 Sep 2018 14:48:17 +0100 Subject: [PATCH 193/207] NO-JIRA - Update SimpleString to give a more useful error message Currently we get. java.lang.IndexOutOfBoundsException: null at org.apache.activemq.artemis.api.core.SimpleString.readSimpleString(SimpleString.java:183) at org.apache.activemq.artemis.api.core.SimpleString$ByteBufSimpleStringPool.create(SimpleString.java:584) .... Should be java.lang.IndexOutOfBoundsException: Error reading in simpleString, length=YYY is greater than readableBytes=XXX at org.apache.activemq.artemis.api.core.SimpleString.readSimpleString(SimpleString.java:183) at org.apache.activemq.artemis.api.core.SimpleString$ByteBufSimpleStringPool.create(SimpleString.java:584) ... --- .../java/org/apache/activemq/artemis/api/core/SimpleString.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/SimpleString.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/SimpleString.java index 96e48b8a9fd..aabffa18742 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/SimpleString.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/SimpleString.java @@ -180,7 +180,7 @@ public static SimpleString readSimpleString(ByteBuf buffer, ByteBufSimpleStringP public static SimpleString readSimpleString(final ByteBuf buffer, final int length) { if (length > buffer.readableBytes()) { - throw new IndexOutOfBoundsException(); + throw new IndexOutOfBoundsException("Error reading in simpleString, length=" + length + " is greater than readableBytes=" + buffer.readableBytes()); } byte[] data = new byte[length]; buffer.readBytes(data); From c417d0b5f8ac67b1c9bde67cd6d6300d4194e2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Sat, 8 Sep 2018 00:23:55 +0100 Subject: [PATCH 194/207] ARTEMIS-2081 listConfiguredQueues returns only queues created by config Extend test case to reproduce problem of client created queues being incorrectly removed on simple reload of config. Add a flag/field to the queues created by configuration/broker.xml so we can correctly filter only queues created/managed by config. Update listConfiguredQueues to use the new queue flag --- .../api/core/management/QueueControl.java | 6 +++ .../management/impl/QueueControlImpl.java | 12 ++++++ .../core/persistence/QueueBindingInfo.java | 4 ++ .../AbstractJournalStorageManager.java | 2 +- .../codec/PersistentQueueBindingEncoding.java | 27 +++++++++++- .../artemis/core/postoffice/PostOffice.java | 3 +- .../core/postoffice/impl/PostOfficeImpl.java | 7 +++- .../activemq/artemis/core/server/Queue.java | 4 ++ .../artemis/core/server/QueueConfig.java | 24 +++++++++-- .../core/server/impl/ActiveMQServerImpl.java | 41 +++++++++++++++++-- .../core/server/impl/LastValueQueue.java | 3 +- .../server/impl/PostOfficeJournalLoader.java | 3 +- .../core/server/impl/QueueFactoryImpl.java | 7 ++-- .../artemis/core/server/impl/QueueImpl.java | 21 ++++++++-- .../impl/ScheduledDeliveryHandlerTest.java | 10 +++++ .../tests/integration/jms/RedeployTest.java | 10 +++++ .../management/QueueControlUsingCoreTest.java | 5 +++ .../reload-address-queues-updated.xml | 4 ++ .../test/resources/reload-address-queues.xml | 4 ++ .../unit/core/postoffice/impl/FakeQueue.java | 10 +++++ .../server/impl/fakes/FakePostOffice.java | 3 +- 21 files changed, 189 insertions(+), 21 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java index d213446c01f..b210530f1c4 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java @@ -222,6 +222,12 @@ public interface QueueControl { @Attribute(desc = "delete this queue when the last consumer disconnects") boolean isPurgeOnNoConsumers(); + /** + * + */ + @Attribute(desc = "is this queue managed by configuration (broker.xml)") + boolean isConfigurationManaged(); + /** * */ diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java index 7377846208b..3db5caef58a 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java @@ -489,6 +489,18 @@ public boolean isPurgeOnNoConsumers() { } } + @Override + public boolean isConfigurationManaged() { + checkStarted(); + + clearIO(); + try { + return queue.isConfigurationManaged(); + } finally { + blockOnIO(); + } + } + @Override public boolean isExclusive() { checkStarted(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java index ebc86fc1bb7..9d7bb7eb159 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java @@ -40,6 +40,10 @@ public interface QueueBindingInfo { boolean isAutoCreated(); + boolean isConfigurationManaged(); + + void setConfigurationManaged(boolean configurationManaged); + SimpleString getUser(); void addQueueStatusEncoding(QueueStatusEncoding status); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java index 8c3cc77f3f4..39511f100bd 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java @@ -1290,7 +1290,7 @@ private void internalQueueBinding(boolean update, final long tx, final Binding b SimpleString filterString = filter == null ? null : filter.getFilterString(); - PersistentQueueBindingEncoding bindingEncoding = new PersistentQueueBindingEncoding(queue.getName(), binding.getAddress(), filterString, queue.getUser(), queue.isAutoCreated(), queue.getMaxConsumers(), queue.isPurgeOnNoConsumers(), queue.isExclusive(), queue.isLastValue(), queue.getConsumersBeforeDispatch(), queue.getDelayBeforeDispatch(), queue.getRoutingType().getType()); + PersistentQueueBindingEncoding bindingEncoding = new PersistentQueueBindingEncoding(queue.getName(), binding.getAddress(), filterString, queue.getUser(), queue.isAutoCreated(), queue.getMaxConsumers(), queue.isPurgeOnNoConsumers(), queue.isExclusive(), queue.isLastValue(), queue.getConsumersBeforeDispatch(), queue.getDelayBeforeDispatch(), queue.getRoutingType().getType(), queue.isConfigurationManaged()); readLock(); try { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java index 0cfe67c097c..a7d52167b7a 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java @@ -56,6 +56,8 @@ public class PersistentQueueBindingEncoding implements EncodingSupport, QueueBin public byte routingType; + public boolean configurationManaged; + public PersistentQueueBindingEncoding() { } @@ -86,6 +88,8 @@ public String toString() { delayBeforeDispatch + ", routingType=" + routingType + + ", configurationManaged=" + + configurationManaged + "]"; } @@ -100,7 +104,8 @@ public PersistentQueueBindingEncoding(final SimpleString name, final boolean lastValue, final int consumersBeforeDispatch, final long delayBeforeDispatch, - final byte routingType) { + final byte routingType, + final boolean configurationManaged) { this.name = name; this.address = address; this.filterString = filterString; @@ -113,6 +118,7 @@ public PersistentQueueBindingEncoding(final SimpleString name, this.consumersBeforeDispatch = consumersBeforeDispatch; this.delayBeforeDispatch = delayBeforeDispatch; this.routingType = routingType; + this.configurationManaged = configurationManaged; } @Override @@ -154,6 +160,16 @@ public boolean isAutoCreated() { return autoCreated; } + @Override + public boolean isConfigurationManaged() { + return configurationManaged; + } + + @Override + public void setConfigurationManaged(boolean configurationManaged) { + this.configurationManaged = configurationManaged; + } + @Override public void addQueueStatusEncoding(QueueStatusEncoding status) { if (queueStatusEncodings == null) { @@ -288,6 +304,11 @@ public void decode(final ActiveMQBuffer buffer) { } else { delayBeforeDispatch = ActiveMQDefaultConfiguration.getDefaultDelayBeforeDispatch(); } + if (buffer.readableBytes() > 0) { + configurationManaged = buffer.readBoolean(); + } else { + configurationManaged = false; + } } @Override @@ -304,6 +325,7 @@ public void encode(final ActiveMQBuffer buffer) { buffer.writeBoolean(lastValue); buffer.writeInt(consumersBeforeDispatch); buffer.writeLong(delayBeforeDispatch); + buffer.writeBoolean(configurationManaged); } @Override @@ -317,7 +339,8 @@ public int getEncodeSize() { DataConstants.SIZE_BOOLEAN + DataConstants.SIZE_BOOLEAN + DataConstants.SIZE_INT + - DataConstants.SIZE_LONG; + DataConstants.SIZE_LONG + + DataConstants.SIZE_BOOLEAN; } private SimpleString createMetadata() { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java index d31e33b4ce5..6ed91b44de2 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java @@ -73,7 +73,8 @@ QueueBinding updateQueue(SimpleString name, Boolean exclusive, Integer consumersBeforeDispatch, Long delayBeforeDispatch, - SimpleString user) throws Exception; + SimpleString user, + Boolean configurationManaged) throws Exception; List listQueuesForAddress(SimpleString address) throws Exception; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index d4cde187934..02abf466c6c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -471,7 +471,8 @@ public QueueBinding updateQueue(SimpleString name, Boolean exclusive, Integer consumersBeforeDispatch, Long delayBeforeDispatch, - SimpleString user) throws Exception { + SimpleString user, + Boolean configurationManaged) throws Exception { synchronized (addressLock) { final QueueBinding queueBinding = (QueueBinding) addressManager.getBinding(name); if (queueBinding == null) { @@ -527,6 +528,10 @@ public QueueBinding updateQueue(SimpleString name, changed = true; queue.setFilter(filter); } + if (configurationManaged != null && !configurationManaged.equals(queue.isConfigurationManaged())) { + changed = true; + queue.setConfigurationManaged(configurationManaged); + } if (logger.isDebugEnabled()) { if (user == null && queue.getUser() != null) { logger.debug("Ignoring updating Queue to a NULL user"); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java index 63d39c7c16e..a8f1095f0eb 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java @@ -94,6 +94,10 @@ public interface Queue extends Bindable,CriticalComponent { void setMaxConsumer(int maxConsumers); + boolean isConfigurationManaged(); + + void setConfigurationManaged(boolean configurationManaged); + void addConsumer(Consumer consumer) throws Exception; void removeConsumer(Consumer consumer); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java index c83e08a7a9c..c79114da5c2 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java @@ -42,6 +42,7 @@ public final class QueueConfig { private final boolean purgeOnNoConsumers; private final int consumersBeforeDispatch; private final long delayBeforeDispatch; + private final boolean configurationManaged; public static final class Builder { @@ -61,6 +62,7 @@ public static final class Builder { private boolean purgeOnNoConsumers; private int consumersBeforeDispatch; private long delayBeforeDispatch; + private boolean configurationManaged; private Builder(final long id, final SimpleString name) { this(id, name, name); @@ -83,6 +85,7 @@ private Builder(final long id, final SimpleString name, final SimpleString addre this.purgeOnNoConsumers = ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(); this.consumersBeforeDispatch = ActiveMQDefaultConfiguration.getDefaultConsumersBeforeDispatch(); this.delayBeforeDispatch = ActiveMQDefaultConfiguration.getDefaultDelayBeforeDispatch(); + this.configurationManaged = false; validateState(); } @@ -99,6 +102,11 @@ private void validateState() { } } + public Builder configurationManaged(final boolean configurationManaged) { + this.configurationManaged = configurationManaged; + return this; + } + public Builder filter(final Filter filter) { this.filter = filter; return this; @@ -185,7 +193,7 @@ public QueueConfig build() { } else { pageSubscription = null; } - return new QueueConfig(id, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, lastValue, consumersBeforeDispatch, delayBeforeDispatch, purgeOnNoConsumers); + return new QueueConfig(id, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, lastValue, consumersBeforeDispatch, delayBeforeDispatch, purgeOnNoConsumers, configurationManaged); } } @@ -233,7 +241,8 @@ private QueueConfig(final long id, final boolean lastValue, final int consumersBeforeDispatch, final long delayBeforeDispatch, - final boolean purgeOnNoConsumers) { + final boolean purgeOnNoConsumers, + final boolean configurationManaged) { this.id = id; this.address = address; this.name = name; @@ -250,6 +259,7 @@ private QueueConfig(final long id, this.maxConsumers = maxConsumers; this.consumersBeforeDispatch = consumersBeforeDispatch; this.delayBeforeDispatch = delayBeforeDispatch; + this.configurationManaged = configurationManaged; } public long id() { @@ -316,6 +326,10 @@ public long delayBeforeDispatch() { return delayBeforeDispatch; } + public boolean isConfigurationManaged() { + return configurationManaged; + } + @Override public boolean equals(Object o) { if (this == o) @@ -357,6 +371,8 @@ public boolean equals(Object o) { return false; if (purgeOnNoConsumers != that.purgeOnNoConsumers) return false; + if (configurationManaged != that.configurationManaged) + return false; return user != null ? user.equals(that.user) : that.user == null; } @@ -379,6 +395,7 @@ public int hashCode() { result = 31 * result + consumersBeforeDispatch; result = 31 * result + Long.hashCode(delayBeforeDispatch); result = 31 * result + (purgeOnNoConsumers ? 1 : 0); + result = 31 * result + (configurationManaged ? 1 : 0); return result; } @@ -400,6 +417,7 @@ public String toString() { + ", lastValue=" + lastValue + ", consumersBeforeDispatch=" + consumersBeforeDispatch + ", delayBeforeDispatch=" + delayBeforeDispatch - + ", purgeOnNoConsumers=" + purgeOnNoConsumers + '}'; + + ", purgeOnNoConsumers=" + purgeOnNoConsumers + + ", configurationManaged=" + configurationManaged + '}'; } } 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 dcb6e02d750..b5a49eec307 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 @@ -2729,7 +2729,7 @@ private Set listAddressNames() { } private List listConfiguredQueues(SimpleString address) throws Exception { - return listQueues(address).stream().filter(queue -> !queue.isAutoCreated() && !queue.isInternalQueue()).collect(Collectors.toList()); + return listQueues(address).stream().filter(queue -> queue.isConfigurationManaged()).collect(Collectors.toList()); } private List listQueues(SimpleString address) throws Exception { @@ -2794,7 +2794,7 @@ private void deployQueuesFromListCoreQueueConfiguration(List queueBindingInfosMap, .lastValue(queueBindingInfo.isLastValue()) .consumersBeforeDispatch(queueBindingInfo.getConsumersBeforeDispatch()) .delayBeforeDispatch(queueBindingInfo.getDelayBeforeDispatch()) - .routingType(RoutingType.getType(queueBindingInfo.getRoutingType())); + .routingType(RoutingType.getType(queueBindingInfo.getRoutingType())) + .configurationManaged((queueBindingInfo.isConfigurationManaged())); final Queue queue = queueFactory.createQueueWith(queueConfigBuilder.build()); queue.setConsumersRefCount(new QueueManagerImpl(((PostOfficeImpl)postOffice).getServer(), queueBindingInfo.getQueueName())); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java index 24b36e6111b..c8835d8caf9 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java @@ -74,11 +74,10 @@ public void setPostOffice(final PostOffice postOffice) { public Queue createQueueWith(final QueueConfig config) { final Queue queue; if (config.isLastValue()) { - queue = new LastValueQueue(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isExclusive(), config.consumersBeforeDispatch(), config.delayBeforeDispatch(), config.isPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); + queue = new LastValueQueue(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isExclusive(), config.consumersBeforeDispatch(), config.delayBeforeDispatch(), config.isPurgeOnNoConsumers(), config.isConfigurationManaged(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } else { - queue = new QueueImpl(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isExclusive(), config.consumersBeforeDispatch(), config.delayBeforeDispatch(), config.isPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); + queue = new QueueImpl(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isExclusive(), config.consumersBeforeDispatch(), config.delayBeforeDispatch(), config.isPurgeOnNoConsumers(), config.isConfigurationManaged(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } - server.getCriticalAnalyzer().add(queue); return queue; } @@ -102,7 +101,7 @@ public Queue createQueue(final long persistenceID, Queue queue; if (addressSettings.isDefaultLastValueQueue()) { - queue = new LastValueQueue(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, ActiveMQDefaultConfiguration.getDefaultRoutingType(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultExclusive(), ActiveMQDefaultConfiguration.getDefaultConsumersBeforeDispatch(), ActiveMQDefaultConfiguration.getDefaultDelayBeforeDispatch(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); + queue = new LastValueQueue(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, ActiveMQDefaultConfiguration.getDefaultRoutingType(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultExclusive(), ActiveMQDefaultConfiguration.getDefaultConsumersBeforeDispatch(), ActiveMQDefaultConfiguration.getDefaultDelayBeforeDispatch(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), false, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } else { queue = new QueueImpl(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java index 2891350e6a2..69d43360928 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java @@ -279,9 +279,11 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { public volatile long dispatchStartTime = -1; - private int consumersBeforeDispatch = 0; + private volatile int consumersBeforeDispatch = 0; - private long delayBeforeDispatch = 0; + private volatile long delayBeforeDispatch = 0; + + private volatile boolean configurationManaged; /** @@ -429,7 +431,7 @@ public QueueImpl(final long id, final ArtemisExecutor executor, final ActiveMQServer server, final QueueFactory factory) { - this(id, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, null, null, purgeOnNoConsumers, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executor, server, factory); + this(id, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, null, null, purgeOnNoConsumers, false, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executor, server, factory); } public QueueImpl(final long id, @@ -447,6 +449,7 @@ public QueueImpl(final long id, final Integer consumersBeforeDispatch, final Long delayBeforeDispatch, final Boolean purgeOnNoConsumers, + final boolean configurationManaged, final ScheduledExecutorService scheduledExecutor, final PostOffice postOffice, final StorageManager storageManager, @@ -486,6 +489,8 @@ public QueueImpl(final long id, this.delayBeforeDispatch = delayBeforeDispatch == null ? ActiveMQDefaultConfiguration.getDefaultDelayBeforeDispatch() : delayBeforeDispatch; + this.configurationManaged = configurationManaged; + this.postOffice = postOffice; this.storageManager = storageManager; @@ -662,6 +667,16 @@ public synchronized void setMaxConsumer(int maxConsumers) { this.maxConsumers = maxConsumers; } + @Override + public boolean isConfigurationManaged() { + return configurationManaged; + } + + @Override + public synchronized void setConfigurationManaged(boolean configurationManaged) { + this.configurationManaged = configurationManaged; + } + @Override public SimpleString getName() { return name; diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java index b21781d2ab8..3561e6f13e1 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java @@ -833,6 +833,16 @@ public void setMaxConsumer(int maxConsumers) { } + @Override + public boolean isConfigurationManaged() { + return false; + } + + @Override + public void setConfigurationManaged(boolean configurationManaged) { + + } + @Override public void recheckRefCount(OperationContext context) { } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java index 07bc22e2a28..cec56a0b071 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java @@ -320,8 +320,15 @@ public void run() { embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); + ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(); + try (JMSContext jmsContext = connectionFactory.createContext()) { + jmsContext.createSharedDurableConsumer(jmsContext.createTopic("config_test_consumer_created_queues"),"mySub").receive(100); + } + try { latch.await(10, TimeUnit.SECONDS); + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_consumer_created_queues").contains("mySub")); + Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal_no_queue")); Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal")); Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_removal")); @@ -344,6 +351,9 @@ public void run() { embeddedActiveMQ.getActiveMQServer().getReloadManager().setTick(tick); latch.await(10, TimeUnit.SECONDS); + //Ensure queues created by clients (NOT by broker.xml are not removed when we reload). + Assert.assertTrue(listQueuesNamesForAddress(embeddedActiveMQ, "config_test_consumer_created_queues").contains("mySub")); + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal_no_queue")); Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_address_removal")); Assert.assertNotNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_removal")); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java index 0a62334fafe..26ada034f90 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java @@ -146,6 +146,11 @@ public boolean isPurgeOnNoConsumers() { return (Boolean) proxy.retrieveAttributeValue("purgeOnNoConsumers"); } + @Override + public boolean isConfigurationManaged() { + return (Boolean) proxy.retrieveAttributeValue("configurationManaged"); + } + @Override public boolean isExclusive() { return (Boolean) proxy.retrieveAttributeValue("exclusive"); diff --git a/tests/integration-tests/src/test/resources/reload-address-queues-updated.xml b/tests/integration-tests/src/test/resources/reload-address-queues-updated.xml index f8d1d918109..fd73f11db9e 100644 --- a/tests/integration-tests/src/test/resources/reload-address-queues-updated.xml +++ b/tests/integration-tests/src/test/resources/reload-address-queues-updated.xml @@ -117,6 +117,10 @@ under the License. +
+ + +
diff --git a/tests/integration-tests/src/test/resources/reload-address-queues.xml b/tests/integration-tests/src/test/resources/reload-address-queues.xml index ebd0f4e9bf5..74c9d08f6cd 100644 --- a/tests/integration-tests/src/test/resources/reload-address-queues.xml +++ b/tests/integration-tests/src/test/resources/reload-address-queues.xml @@ -120,6 +120,10 @@ under the License. +
+ + +
diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java index 5297ab62093..7c1297debd8 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java @@ -105,6 +105,16 @@ public void setMaxConsumer(int maxConsumers) { } + @Override + public boolean isConfigurationManaged() { + return false; + } + + @Override + public void setConfigurationManaged(boolean configurationManaged) { + + } + @Override public boolean isInternalQueue() { // no-op diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java index 0bbe8efeec8..5f128ead7d7 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java @@ -53,7 +53,8 @@ public QueueBinding updateQueue(SimpleString name, Boolean exclusive, Integer consumersBeforeDispatch, Long delayBeforeDispatch, - SimpleString user) throws Exception { + SimpleString user, + Boolean configurationManaged) throws Exception { return null; } From e065e3e960ac8cc6505a71ec4f67b58b4ffb5990 Mon Sep 17 00:00:00 2001 From: Timothy Bish Date: Tue, 11 Sep 2018 15:42:24 -0400 Subject: [PATCH 195/207] ARTEMIS-2082 Reset buffer valid flag after re-encoding the message Once a re-encode of the message is done the buffer is not being marked as valid and so subsequent checks on the buffer are all assuming the message data is not valid and re-encoding over and over. This can lead to poor performance in some cases and corrupted data in others. --- .../protocol/amqp/broker/AMQPMessage.java | 3 +- .../amqp/AmqpExpiredMessageTest.java | 65 +++++++-- .../amqp/JMSTransactedRedeliveryBugTest.java | 132 ++++++++++++++++++ 3 files changed, 191 insertions(+), 9 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSTransactedRedeliveryBugTest.java diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java index 27d571e00c2..c0f9d102c15 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java @@ -694,7 +694,8 @@ private void encodeProtonMessage() { getProtonMessage().encode(new NettyWritable(buffer)); byte[] bytes = new byte[buffer.writerIndex()]; buffer.readBytes(bytes); - this.data = ReadableBuffer.ByteBufferReader.wrap(ByteBuffer.wrap(bytes)); + data = ReadableBuffer.ByteBufferReader.wrap(ByteBuffer.wrap(bytes)); + bufferValid = true; } finally { buffer.release(); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpExpiredMessageTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpExpiredMessageTest.java index f5795b6e966..b130ba8ad3d 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpExpiredMessageTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpExpiredMessageTest.java @@ -16,9 +16,9 @@ */ package org.apache.activemq.artemis.tests.integration.amqp; +import java.util.UUID; import java.util.concurrent.TimeUnit; -import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.server.Queue; import org.apache.activemq.artemis.tests.util.Wait; import org.apache.activemq.transport.amqp.client.AmqpClient; @@ -339,11 +339,10 @@ public void internalSendExpiry(boolean restartServer) throws Throwable { message.setDurable(true); message.setText("Test-Message"); message.setDeliveryAnnotation("shouldDisappear", 1); - message.setAbsoluteExpiryTime(System.currentTimeMillis() + 1000); + message.setAbsoluteExpiryTime(System.currentTimeMillis() + 250); sender.send(message); - org.apache.activemq.artemis.core.server.Queue dlq = server.locateQueue(SimpleString.toSimpleString(getDeadLetterAddress())); - + Queue dlq = getProxyToQueue(getDeadLetterAddress()); assertTrue("Message not movied to DLQ", Wait.waitFor(() -> dlq.getMessageCount() > 0, 5000, 500)); connection.close(); @@ -361,11 +360,61 @@ public void internalSendExpiry(boolean restartServer) throws Throwable { receiver.flow(20); message = receiver.receive(5, TimeUnit.SECONDS); - Assert.assertNotNull(message); - Assert.assertEquals(getQueueName(), message.getMessageAnnotation(org.apache.activemq.artemis.api.core.Message.HDR_ORIGINAL_ADDRESS.toString())); - Assert.assertNull(message.getDeliveryAnnotation("shouldDisappear")); - Assert.assertNull(receiver.receiveNoWait()); + assertNotNull(message); + assertEquals(getQueueName(), message.getMessageAnnotation(org.apache.activemq.artemis.api.core.Message.HDR_ORIGINAL_ADDRESS.toString())); + assertNull(message.getDeliveryAnnotation("shouldDisappear")); + assertNull(receiver.receiveNoWait()); + } finally { + connection.close(); + } + } + + @Test(timeout = 60000) + public void testDLQdMessageCanBeRedeliveredMultipleTimes() throws Throwable { + AmqpClient client = createAmqpClient(); + AmqpConnection connection = client.connect(); + + try { + AmqpSession session = connection.createSession(); + AmqpSender sender = session.createSender(getQueueName()); + + AmqpMessage message = new AmqpMessage(); + message.setDurable(true); + message.setTimeToLive(250); + message.setText("Test-Message"); + message.setMessageId(UUID.randomUUID().toString()); + message.setApplicationProperty("key", "value"); + sender.send(message); + + Queue dlqView = getProxyToQueue(getDeadLetterAddress()); + assertTrue("Message not movied to DLQ", Wait.waitFor(() -> dlqView.getMessageCount() > 0, 5000, 200)); + + // Read and Modify the message for redelivery repeatedly + AmqpReceiver receiver = session.createReceiver(getDeadLetterAddress()); + receiver.flow(20); + + message = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull(message); + assertEquals(0, message.getWrappedMessage().getDeliveryCount()); + + message.modified(true, false); + + message = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull(message); + assertEquals(1, message.getWrappedMessage().getDeliveryCount()); + + message.modified(true, false); + + message = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull(message); + assertEquals(2, message.getWrappedMessage().getDeliveryCount()); + + message.modified(true, false); + + message = receiver.receive(5, TimeUnit.SECONDS); + assertNotNull(message); + assertEquals(3, message.getWrappedMessage().getDeliveryCount()); } finally { connection.close(); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSTransactedRedeliveryBugTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSTransactedRedeliveryBugTest.java new file mode 100644 index 00000000000..9066a20b5e3 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSTransactedRedeliveryBugTest.java @@ -0,0 +1,132 @@ +/* + * 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.artemis.tests.integration.amqp; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.core.server.impl.AddressInfo; +import org.apache.activemq.artemis.core.settings.impl.AddressSettings; +import org.apache.activemq.artemis.junit.Wait; +import org.junit.Test; + +public class JMSTransactedRedeliveryBugTest extends JMSClientTestSupport { + + private static final String INITIAL_QUEUE_NAME = "InitialQueue"; + private static final String FINAL_QUEUE_NAME = "FinalQueue"; + + private static final SimpleString INITIAL_QUEUE_SS = new SimpleString(INITIAL_QUEUE_NAME); + private static final SimpleString FINAL_QUEUE_SS = new SimpleString(FINAL_QUEUE_NAME); + + @Override + protected void addConfiguration(ActiveMQServer server) { + server.getAddressSettingsRepository().addMatch(INITIAL_QUEUE_NAME, new AddressSettings().setExpiryAddress(FINAL_QUEUE_SS)); + } + + @Override + protected void createAddressAndQueues(ActiveMQServer server) throws Exception { + super.createAddressAndQueues(server); + server.addAddressInfo(new AddressInfo(INITIAL_QUEUE_SS, RoutingType.ANYCAST)); + server.createQueue(INITIAL_QUEUE_SS, RoutingType.ANYCAST, INITIAL_QUEUE_SS, null, true, false, -1, false, true); + server.addAddressInfo(new AddressInfo(FINAL_QUEUE_SS, RoutingType.ANYCAST)); + server.createQueue(FINAL_QUEUE_SS, RoutingType.ANYCAST, FINAL_QUEUE_SS, null, true, false, -1, false, true); + } + + @Override + protected String getJmsConnectionURIOptions() { + return "amqp.traceFrames=true"; + } + + @Test + public void testAMQPProducerAMQPConsumer() throws Exception { + Connection producerConnection = createConnection(); + Connection consumerConnection = createConnection(); + + try { + Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue producerQueue = producerSession.createQueue(INITIAL_QUEUE_NAME); + MessageProducer producer = producerSession.createProducer(producerQueue); + + TextMessage sentMessage = producerSession.createTextMessage(); + sentMessage.setStringProperty("something", "KEY"); + sentMessage.setText("how are you"); + producer.send(sentMessage, DeliveryMode.PERSISTENT, 4, 10); + + // Simulate a small pause, else both messages could be consumed if + // consumer is fast enough + Wait.assertTrue("Message should have expired", () -> getProxyToQueue(FINAL_QUEUE_NAME).getMessageCount() == 1); + + Session consumerSession = consumerConnection.createSession(true, Session.SESSION_TRANSACTED); + Queue consumerQueue = consumerSession.createQueue(FINAL_QUEUE_NAME); + MessageConsumer consumer = consumerSession.createConsumer(consumerQueue); + + Message msg = consumer.receive(1_000); + assertNotNull(msg); + assertEquals("1", msg.getStringProperty("JMSXDeliveryCount")); + assertEquals("KEY", msg.getStringProperty("something")); + assertEquals("how are you", ((TextMessage) msg).getText()); + + consumerSession.rollback(); + + msg = consumer.receive(1_000); + assertNotNull(msg); + assertEquals("2", msg.getStringProperty("JMSXDeliveryCount")); + assertEquals("KEY", msg.getStringProperty("something")); + assertEquals("how are you", ((TextMessage) msg).getText()); + + consumerSession.rollback(); + + msg = consumer.receive(1_000); + assertNotNull(msg); + assertEquals("3", msg.getStringProperty("JMSXDeliveryCount")); + assertEquals("KEY", msg.getStringProperty("something")); + assertEquals("how are you", ((TextMessage) msg).getText()); + + consumerSession.rollback(); + + msg = consumer.receive(1_000); + assertNotNull(msg); + assertEquals("4", msg.getStringProperty("JMSXDeliveryCount")); + assertEquals("KEY", msg.getStringProperty("something")); + assertEquals("how are you", ((TextMessage) msg).getText()); + + consumerSession.rollback(); + + msg = consumer.receive(1_000); + assertNotNull(msg); + assertEquals("5", msg.getStringProperty("JMSXDeliveryCount")); + assertEquals("KEY", msg.getStringProperty("something")); + assertEquals("how are you", ((TextMessage) msg).getText()); + + consumerSession.commit(); + + consumer.close(); + } finally { + producerConnection.close(); + consumerConnection.close(); + } + } +} \ No newline at end of file From 7a7304bb286b334c21afe3131c5957f3762116bc Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Fri, 7 Sep 2018 11:13:19 +0200 Subject: [PATCH 196/207] ARTEMIS-2078 AMQP large msg aren't consumed by clusters without persistence NullStorageLargeServerMessage is not correctly exposing its body buffer to allow CoreAmqpConverter to convert it into an AMQPMessage --- .../amqp/converter/CoreAmqpConverter.java | 10 +- .../nullpm/NullStorageLargeServerMessage.java | 11 +- .../spi/core/protocol/EmbedMessageUtil.java | 4 +- .../AMQPLargeMessageClusterTest.java | 165 ++++++++++++++++++ 4 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/AMQPLargeMessageClusterTest.java diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java index b287d0c137a..200321c16b0 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/CoreAmqpConverter.java @@ -18,6 +18,7 @@ package org.apache.activemq.artemis.protocol.amqp.converter; import static org.apache.activemq.artemis.api.core.FilterConstants.NATIVE_MESSAGE_ID; +import static org.apache.activemq.artemis.api.core.Message.EMBEDDED_TYPE; import static org.apache.activemq.artemis.api.core.Message.HDR_SCHEDULED_DELIVERY_TIME; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_DATA; import static org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport.AMQP_NULL; @@ -82,6 +83,7 @@ import org.apache.activemq.artemis.protocol.amqp.util.NettyWritable; import org.apache.activemq.artemis.protocol.amqp.util.TLSEncode; import org.apache.activemq.artemis.reader.MessageUtil; +import org.apache.activemq.artemis.spi.core.protocol.EmbedMessageUtil; import org.apache.qpid.proton.amqp.Binary; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.UnsignedByte; @@ -120,7 +122,13 @@ public static AMQPMessage fromCore(ICoreMessage coreMessage) throws Exception { if (coreMessage == null) { return null; } - + if (coreMessage.isServerMessage() && coreMessage.isLargeMessage() && coreMessage.getType() == EMBEDDED_TYPE) { + //large AMQP messages received across cluster nodes + final Message message = EmbedMessageUtil.extractEmbedded(coreMessage); + if (message instanceof AMQPMessage) { + return (AMQPMessage) message; + } + } ServerJMSMessage message = ServerJMSMessage.wrapCoreMessage(coreMessage); message.decode(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageLargeServerMessage.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageLargeServerMessage.java index edd37b73992..62807462813 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageLargeServerMessage.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageLargeServerMessage.java @@ -16,8 +16,10 @@ */ package org.apache.activemq.artemis.core.persistence.impl.nullpm; -import org.apache.activemq.artemis.api.core.ActiveMQBuffers; +import io.netty.buffer.Unpooled; +import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.Message; +import org.apache.activemq.artemis.core.buffers.impl.ChannelBufferWrapper; import org.apache.activemq.artemis.core.io.SequentialFile; import org.apache.activemq.artemis.core.message.impl.CoreMessage; import org.apache.activemq.artemis.core.server.LargeServerMessage; @@ -39,13 +41,18 @@ public void releaseResources() { @Override public synchronized void addBytes(final byte[] bytes) { if (buffer == null) { - buffer = ActiveMQBuffers.dynamicBuffer(bytes.length).byteBuf(); + buffer = Unpooled.buffer(bytes.length); } // expand the buffer buffer.writeBytes(bytes); } + @Override + public synchronized ActiveMQBuffer getReadOnlyBodyBuffer() { + return new ChannelBufferWrapper(buffer.slice(0, buffer.writerIndex()).asReadOnly()); + } + @Override public void deleteFile() throws Exception { // nothing to be done here.. we don really have a file on this Storage diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/protocol/EmbedMessageUtil.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/protocol/EmbedMessageUtil.java index 8f4e102ec89..53eb27bc320 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/protocol/EmbedMessageUtil.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/protocol/EmbedMessageUtil.java @@ -51,9 +51,7 @@ public static Message extractEmbedded(ICoreMessage message) { ActiveMQBuffer buffer = message.getReadOnlyBodyBuffer(); if (buffer.readableBytes() < signature.length || !checkSignature(buffer)) { - if (!logger.isTraceEnabled()) { - logger.trace("Message type " + Message.EMBEDDED_TYPE + " was used for something other than embed messages, ignoring content and treating as a regular message"); - } + logger.tracef("Message type %d was used for something other than embed messages, ignoring content and treating as a regular message", Message.EMBEDDED_TYPE); return message; } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/AMQPLargeMessageClusterTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/AMQPLargeMessageClusterTest.java new file mode 100644 index 00000000000..e8ac13edcdb --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/AMQPLargeMessageClusterTest.java @@ -0,0 +1,165 @@ +/** + * 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.artemis.tests.integration.cluster.distribution; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import java.util.Arrays; +import java.util.concurrent.ThreadLocalRandom; + +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; +import org.apache.activemq.artemis.core.settings.impl.AddressSettings; +import org.apache.activemq.artemis.protocol.amqp.broker.ProtonProtocolManagerFactory; +import org.apache.qpid.jms.JmsConnectionFactory; +import org.hamcrest.core.IsInstanceOf; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(value = Parameterized.class) +public class AMQPLargeMessageClusterTest extends ClusterTestBase { + + private static final int RECEIVE_TIMEOUT_MILLIS = 20_000; + private static final int MESSAGE_SIZE = 1024 * 1024; + private static final int MESSAGES = 1; + + private final boolean persistenceEnabled; + private final boolean compressLargeMessages; + + @Parameterized.Parameters(name = "persistenceEnabled = {0}, compressLargeMessages = {1}") + public static Iterable persistenceEnabled() { + return Arrays.asList(new Object[][]{{true, false}, {false, false}, {true, true}, {false, true}}); + } + + public AMQPLargeMessageClusterTest(boolean persistenceEnabled, boolean compressLargeMessages) { + this.persistenceEnabled = persistenceEnabled; + this.compressLargeMessages = compressLargeMessages; + } + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + + start(); + } + + private void start() throws Exception { + setupServers(); + + setRedistributionDelay(0); + } + + protected boolean isNetty() { + return true; + } + + @Test(timeout = RECEIVE_TIMEOUT_MILLIS * (MESSAGES + 1)) + public void testSendReceiveLargeMessage() throws Exception { + setupCluster(MessageLoadBalancingType.ON_DEMAND); + + startServers(0, 1); + + setupSessionFactory(0, isNetty()); + setupSessionFactory(1, isNetty()); + final String queueName = "queues.0"; + + createQueue(0, queueName, queueName, null, false, null, null, RoutingType.ANYCAST); + createQueue(1, queueName, queueName, null, false, null, null, RoutingType.ANYCAST); + + waitForBindings(0, queueName, 1, 0, true); + waitForBindings(1, queueName, 1, 0, true); + + waitForBindings(0, queueName, /**/1, 0, false); + waitForBindings(1, queueName, 1, 0, false); + String producerUri = "amqp://localhost:61616"; + if (compressLargeMessages) { + producerUri = producerUri + "?compressLargeMessages=true"; + } + final JmsConnectionFactory producerFactory = new JmsConnectionFactory(producerUri); + try (Connection producerConnection = producerFactory.createConnection(); Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE)) { + producerConnection.start(); + final Destination queue = producerSession.createQueue(queueName); + String consumerUri = "amqp://localhost:61617"; + if (compressLargeMessages) { + consumerUri = consumerUri + "?compressLargeMessages=true"; + } + final JmsConnectionFactory consumerConnectionFactory = new JmsConnectionFactory(consumerUri); + try (Connection consumerConnection = consumerConnectionFactory.createConnection(); Session consumerSession = consumerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer consumer = consumerSession.createConsumer(queue); MessageProducer producer = producerSession.createProducer(queue)) { + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + consumerConnection.start(); + final byte[] largeMessageContent = new byte[MESSAGE_SIZE]; + final byte[] receivedContent = new byte[largeMessageContent.length]; + ThreadLocalRandom.current().nextBytes(largeMessageContent); + for (int i = 0; i < MESSAGES; i++) { + final BytesMessage sentMessage = producerSession.createBytesMessage(); + sentMessage.writeBytes(largeMessageContent); + producer.send(sentMessage); + final Message receivedMessage = consumer.receive(RECEIVE_TIMEOUT_MILLIS); + Assert.assertNotNull("A message should be received in " + RECEIVE_TIMEOUT_MILLIS + " ms", receivedMessage); + Assert.assertThat(receivedMessage, IsInstanceOf.instanceOf(sentMessage.getClass())); + Assert.assertEquals(largeMessageContent.length, ((BytesMessage) receivedMessage).readBytes(receivedContent)); + Assert.assertArrayEquals(largeMessageContent, receivedContent); + } + } + } + } + + protected void setupCluster(final MessageLoadBalancingType messageLoadBalancingType) throws Exception { + setupClusterConnection("cluster0", "queues", messageLoadBalancingType, 1, isNetty(), 0, 1); + setupClusterConnection("cluster1", "queues", messageLoadBalancingType, 1, isNetty(), 1, 0); + } + + protected void setRedistributionDelay(final long delay) { + AddressSettings as = new AddressSettings().setRedistributionDelay(delay); + + getServer(0).getAddressSettingsRepository().addMatch("queues.*", as); + getServer(1).getAddressSettingsRepository().addMatch("queues.*", as); + } + + protected void setupServers() throws Exception { + setupServer(0, persistenceEnabled, isNetty()); + setupServer(1, persistenceEnabled, isNetty()); + + servers[0].addProtocolManagerFactory(new ProtonProtocolManagerFactory()); + servers[1].addProtocolManagerFactory(new ProtonProtocolManagerFactory()); + } + + protected void stopServers() throws Exception { + closeAllConsumers(); + + closeAllSessionFactories(); + + closeAllServerLocatorsFactories(); + + stopServers(0, 1); + + clearServer(0, 1); + } + +} + From 3c7252adbca1d497e0c89dd8247f0b2f20e2425e Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Mon, 3 Sep 2018 08:57:11 +0200 Subject: [PATCH 197/207] ARTEMIS-2068 save reading any file to get AMQP large msg size ServerJMSBytesMessage::getBodyLength can save reading the whole large message file by reading just its file size --- .../artemis/api/core/ICoreMessage.java | 5 ++++ .../core/message/impl/CoreMessage.java | 7 +++++ .../artemis/message/CoreMessageTest.java | 8 ++++++ .../converter/jms/ServerJMSBytesMessage.java | 2 +- .../impl/journal/LargeServerMessageImpl.java | 25 ++++++++++++++++++ .../integration/client/LargeMessageTest.java | 26 +++++++++++++++++++ 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/ICoreMessage.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/ICoreMessage.java index f0eb1b675d2..66c4cdfe185 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/ICoreMessage.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/ICoreMessage.java @@ -41,6 +41,11 @@ public interface ICoreMessage extends Message { */ ActiveMQBuffer getReadOnlyBodyBuffer(); + /** + * Returns the length in bytes of the body buffer. + */ + int getBodyBufferSize(); + /** * Returns a readOnlyBodyBuffer or a decompressed one if the message is compressed. * or the large message buffer. diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java index 323d9f4dd00..5272200e01d 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java @@ -217,6 +217,13 @@ public ActiveMQBuffer getReadOnlyBodyBuffer() { return new ChannelBufferWrapper(buffer.slice(BODY_OFFSET, endOfBodyPosition - BUFFER_HEADER_SPACE).setIndex(0, endOfBodyPosition - BUFFER_HEADER_SPACE).asReadOnly()); } + @Override + public int getBodyBufferSize() { + checkEncode(); + internalWritableBuffer(); + return endOfBodyPosition - BUFFER_HEADER_SPACE; + } + /** * This will return the proper buffer to represent the data of the Message. If compressed it will decompress. * If large, it will read from the file or streaming. diff --git a/artemis-core-client/src/test/java/org/apache/activemq/artemis/message/CoreMessageTest.java b/artemis-core-client/src/test/java/org/apache/activemq/artemis/message/CoreMessageTest.java index 310b4ed160f..5ba2a5b1e89 100644 --- a/artemis-core-client/src/test/java/org/apache/activemq/artemis/message/CoreMessageTest.java +++ b/artemis-core-client/src/test/java/org/apache/activemq/artemis/message/CoreMessageTest.java @@ -82,6 +82,14 @@ public void testPassThrough() { Assert.assertEquals(TEXT, TextMessageUtil.readBodyText(decodedMessage.getReadOnlyBodyBuffer()).toString()); } + @Test + public void testBodyBufferSize() { + final CoreMessage decodedMessage = decodeMessage(); + final int bodyBufferSize = decodedMessage.getBodyBufferSize(); + final int readonlyBodyBufferReadableBytes = decodedMessage.getReadOnlyBodyBuffer().readableBytes(); + Assert.assertEquals(bodyBufferSize, readonlyBodyBufferReadableBytes); + } + /** The message is received, then sent to the other side untouched */ @Test public void sendThroughPackets() { diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/jms/ServerJMSBytesMessage.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/jms/ServerJMSBytesMessage.java index a94cfde25f2..f7f2a0de49b 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/jms/ServerJMSBytesMessage.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/converter/jms/ServerJMSBytesMessage.java @@ -54,7 +54,7 @@ public ServerJMSBytesMessage(ICoreMessage message) { @Override public long getBodyLength() throws JMSException { - return message.getReadOnlyBodyBuffer().readableBytes(); + return message.getBodyBufferSize(); } @Override diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/LargeServerMessageImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/LargeServerMessageImpl.java index d9409464516..257141ebb29 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/LargeServerMessageImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/LargeServerMessageImpl.java @@ -223,6 +223,31 @@ public ActiveMQBuffer getReadOnlyBodyBuffer() { } } + @Override + public int getBodyBufferSize() { + final boolean closeFile = file == null || !file.isOpen(); + try { + openFile(); + final long fileSize = file.size(); + int fileSizeAsInt = (int) fileSize; + if (fileSizeAsInt < 0) { + logger.warnf("suspicious large message file size of %d bytes for %s, will use %d instead.", + fileSize, file.getFileName(), Integer.MAX_VALUE); + fileSizeAsInt = Integer.MAX_VALUE; + } + return fileSizeAsInt; + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + if (closeFile) { + try { + file.close(); + } catch (Exception ignored) { + } + } + } + } + @Override public boolean isLargeMessage() { return true; diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/LargeMessageTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/LargeMessageTest.java index 025d00a3b60..1d9075d213f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/LargeMessageTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/LargeMessageTest.java @@ -2289,6 +2289,32 @@ public void testIgnoreStreaming() throws Exception { log.debug("Thread done"); } + @Test + public void testLargeMessageBodySize() throws Exception { + ActiveMQServer server = createServer(true, isNetty(), storeType); + + server.start(); + + LargeServerMessageImpl fileMessage = new LargeServerMessageImpl((JournalStorageManager) server.getStorageManager()); + + fileMessage.setMessageID(1005); + + Assert.assertEquals(0, fileMessage.getBodyBufferSize()); + + for (int i = 0; i < largeMessageSize; i++) { + fileMessage.addBytes(new byte[]{ActiveMQTestBase.getSamplebyte(i)}); + } + + Assert.assertEquals(largeMessageSize, fileMessage.getBodyBufferSize()); + + // The server would be doing this + fileMessage.putLongProperty(Message.HDR_LARGE_BODY_SIZE, largeMessageSize); + + fileMessage.releaseResources(); + + Assert.assertEquals(largeMessageSize, fileMessage.getBodyBufferSize()); + } + // The ClientConsumer should be able to also send ServerLargeMessages as that's done by the CoreBridge @Test public void testSendServerMessage() throws Exception { From d2d9a0e63471690a9851af1d9574fc4fc8d8bfa8 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Tue, 4 Sep 2018 15:15:06 -0400 Subject: [PATCH 198/207] ARTEMIS-2074 URI not transposing ServerLocator on ConnectionFactory This closes #2286 As it superceedes it --- .../artemis/jms/client/ActiveMQConnectionFactory.java | 8 ++++++++ .../apache/activemq/artemis/uri/AbstractCFSchema.java | 9 +++++++++ .../org/apache/activemq/artemis/uri/InVMSchema.java | 4 +++- .../apache/activemq/artemis/uri/JGroupsSchema.java | 2 +- .../org/apache/activemq/artemis/uri/TCPSchema.java | 2 +- .../org/apache/activemq/artemis/uri/UDPSchema.java | 2 +- .../tests/integration/jms/SimpleJNDIClientTest.java | 11 +++++++++++ 7 files changed, 34 insertions(+), 4 deletions(-) diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java index a0d036c0ffe..df4611e442f 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java @@ -67,6 +67,14 @@ public class ActiveMQConnectionFactory extends JNDIStorable implements Connectio private static final long serialVersionUID = 6730844785641767519L; + public ServerLocator setUseTopologyForLoadBalancing(boolean useTopologyForLoadBalancing) { + return serverLocator.setUseTopologyForLoadBalancing(useTopologyForLoadBalancing); + } + + public boolean getUseTopologyForLoadBalancing() { + return serverLocator.getUseTopologyForLoadBalancing(); + } + private ServerLocator serverLocator; private String clientID; diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/AbstractCFSchema.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/AbstractCFSchema.java index 9b407dbed8b..54bd574e992 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/AbstractCFSchema.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/AbstractCFSchema.java @@ -36,4 +36,13 @@ protected JMSConnectionOptions newConectionOptions(URI uri, Map return BeanSupport.setData(uri, new JMSConnectionOptions(), query); } + + protected ActiveMQConnectionFactory setData(URI uri, + Map query, + ActiveMQConnectionFactory factory) throws Exception { + BeanSupport.setData(uri, factory.getServerLocator(), query); + return BeanSupport.setData(uri, factory, query); + } + + } diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/InVMSchema.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/InVMSchema.java index 73e826a7cc9..15a5ce2f340 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/InVMSchema.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/InVMSchema.java @@ -39,7 +39,9 @@ protected ActiveMQConnectionFactory internalNewObject(URI uri, String name) throws Exception { JMSConnectionOptions options = newConectionOptions(uri, query); ActiveMQConnectionFactory factory = ActiveMQJMSClient.createConnectionFactoryWithoutHA(options.getFactoryTypeEnum(), InVMTransportConfigurationSchema.createTransportConfiguration(uri, query, name, "org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory")); - return BeanSupport.setData(uri, factory, query); + BeanSupport.setData(uri, factory, query); + BeanSupport.setData(uri, factory.getServerLocator(), query); + return factory; } @Override diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/JGroupsSchema.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/JGroupsSchema.java index 2d820188449..653d6e18ed3 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/JGroupsSchema.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/JGroupsSchema.java @@ -52,7 +52,7 @@ public ActiveMQConnectionFactory internalNewObject(URI uri, } else { factory = ActiveMQJMSClient.createConnectionFactoryWithoutHA(dcConfig, options.getFactoryTypeEnum()); } - return BeanSupport.setData(uri, factory, query); + return setData(uri, query, factory); } @Override diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/TCPSchema.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/TCPSchema.java index 8ea3cfc166b..eddff33e9f7 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/TCPSchema.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/TCPSchema.java @@ -57,7 +57,7 @@ protected ActiveMQConnectionFactory internalNewObject(URI uri, factory = ActiveMQJMSClient.createConnectionFactoryWithoutHA(options.getFactoryTypeEnum(), tcs); } - return BeanSupport.setData(uri, factory, query); + return setData(uri, query, factory); } @Override diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/UDPSchema.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/UDPSchema.java index 45f7ebfa766..cb4e21770ea 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/UDPSchema.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/uri/UDPSchema.java @@ -49,7 +49,7 @@ public ActiveMQConnectionFactory internalNewObject(URI uri, } else { factory = ActiveMQJMSClient.createConnectionFactoryWithoutHA(dgc, options.getFactoryTypeEnum()); } - return BeanSupport.setData(uri, factory, query); + return setData(uri, query, factory); } @Override diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java index 2acdb0bff73..a18248e26e9 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java @@ -719,4 +719,15 @@ public void test1xNamingNegative() throws NamingException, JMSException { connection.close(); } + + @Test + public void testUseTopologyForLoadBalancing() throws Exception { + Hashtable props = new Hashtable<>(); + props.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory"); + props.put("connectionFactory.ConnectionFactory", "vm://0?useTopologyForLoadBalancing=false"); + Context ctx = new InitialContext(props); + + ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("ConnectionFactory"); + assertFalse(((ActiveMQConnectionFactory)connectionFactory).getServerLocator().getUseTopologyForLoadBalancing()); + } } \ No newline at end of file From d1939620c03dab8f4a3ac5b7da159b8c27f078d6 Mon Sep 17 00:00:00 2001 From: Timothy Bish Date: Tue, 11 Sep 2018 17:04:39 -0400 Subject: [PATCH 199/207] ARTEMIS-2067 Clean up some code in the AMQP protocol handling paths Cleans up some of the code on the proton event handler, most noteable: 1. Fix IOCallback creation on each outbound send, use single instance as the handler only ever does a flush and has no attached state. 2. Fix redundent locking and unlocking of connection lock on the event path that already ensures that lock is held. 3. Set presettle state on the server sender at attach as it cannot change afterwards so checking on every message is not needed. 4. Improve buffer type checking on receive to reduce amount of work --- .../proton/ProtonServerReceiverContext.java | 32 +++------ .../proton/ProtonServerSenderContext.java | 70 ++++++++----------- .../amqp/proton/handler/ProtonHandler.java | 38 +++++----- .../transaction/ProtonTransactionHandler.java | 57 ++++++--------- .../ProtonServerReceiverContextTest.java | 4 +- 5 files changed, 82 insertions(+), 119 deletions(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java index 30dd10aeeac..cdd1362d43a 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContext.java @@ -205,23 +205,20 @@ private RoutingType getRoutingType(Symbol[] symbols, SimpleString address) { } /* - * called when Proton receives a message to be delivered via a Delivery. - * - * This may be called more than once per deliver so we have to cache the buffer until we have received it all. - * - * */ + * called when Proton receives a message to be delivered via a Delivery. + * + * This may be called more than once per deliver so we have to cache the buffer until we have received it all. + */ @Override public void onMessage(Delivery delivery) throws ActiveMQAMQPException { - Receiver receiver; try { + Receiver receiver = ((Receiver) delivery.getLink()); - if (!delivery.isReadable()) { + if (receiver.current() != delivery) { return; } if (delivery.isAborted()) { - receiver = ((Receiver) delivery.getLink()); - // Aborting implicitly remotely settles, so advance // receiver to the next delivery and settle locally. receiver.advance(); @@ -233,16 +230,11 @@ public void onMessage(Delivery delivery) throws ActiveMQAMQPException { } return; - } - - if (delivery.isPartial()) { + } else if (delivery.isPartial()) { return; } - receiver = ((Receiver) delivery.getLink()); - Transaction tx = null; - ReadableBuffer data = receiver.recv(); receiver.advance(); @@ -267,13 +259,9 @@ public void onMessage(Delivery delivery) throws ActiveMQAMQPException { condition.setDescription(e.getMessage()); rejected.setError(condition); - connection.lock(); - try { - delivery.disposition(rejected); - delivery.settle(); - } finally { - connection.unlock(); - } + + delivery.disposition(rejected); + delivery.settle(); } } diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerSenderContext.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerSenderContext.java index 0b40ee21409..24dcff03aa8 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerSenderContext.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerSenderContext.java @@ -87,6 +87,8 @@ public class ProtonServerSenderContext extends ProtonInitializable implements Pr private static final Symbol SHARED = Symbol.valueOf("shared"); private static final Symbol GLOBAL = Symbol.valueOf("global"); + private final ConnectionFlushIOCallback connectionFlusher = new ConnectionFlushIOCallback(); + private Consumer brokerConsumer; protected final AMQPSessionContext protonSession; @@ -101,6 +103,7 @@ public class ProtonServerSenderContext extends ProtonInitializable implements Pr private boolean shared = false; private boolean global = false; private boolean isVolatile = false; + private boolean preSettle; private SimpleString tempQueueName; public ProtonServerSenderContext(AMQPConnectionContext connection, @@ -417,6 +420,9 @@ public void initialise() throws Exception { } } + // Detect if sender is in pre-settle mode. + preSettle = sender.getRemoteSenderSettleMode() == SenderSettleMode.SETTLED; + // We need to update the source with any filters we support otherwise the client // is free to consider the attach as having failed if we don't send back what we // do support or if we send something we don't support the client won't know we @@ -538,17 +544,7 @@ public void onMessage(Delivery delivery) throws ActiveMQAMQPException { try { Message message = ((MessageReference) delivery.getContext()).getMessage(); - - boolean preSettle = sender.getRemoteSenderSettleMode() == SenderSettleMode.SETTLED; - - DeliveryState remoteState; - - connection.lock(); - try { - remoteState = delivery.getRemoteState(); - } finally { - connection.unlock(); - } + DeliveryState remoteState = delivery.getRemoteState(); boolean settleImmediate = true; if (remoteState instanceof Accepted) { @@ -558,8 +554,7 @@ public void onMessage(Delivery delivery) throws ActiveMQAMQPException { return; } // we have to individual ack as we can't guarantee we will get the delivery updates - // (including acks) in order - // from dealer, a perf hit but a must + // (including acks) in order from dealer, a performance hit but a must try { sessionSPI.ack(null, brokerConsumer, message); } catch (Exception e) { @@ -580,16 +575,10 @@ public void onMessage(Delivery delivery) throws ActiveMQAMQPException { TransactionalState txAccepted = new TransactionalState(); txAccepted.setOutcome(Accepted.getInstance()); txAccepted.setTxnId(txState.getTxnId()); - connection.lock(); - try { - delivery.disposition(txAccepted); - } finally { - connection.unlock(); - } + delivery.disposition(txAccepted); } // we have to individual ack as we can't guarantee we will get the delivery - // updates (including acks) in order - // from dealer, a perf hit but a must + // (including acks) in order from dealer, a performance hit but a must try { sessionSPI.ack(tx, brokerConsumer, message); tx.addDelivery(delivery, this); @@ -636,23 +625,24 @@ public void onMessage(Delivery delivery) throws ActiveMQAMQPException { } if (settleImmediate) { - settle(delivery); + delivery.settle(); } } finally { - sessionSPI.afterIO(new IOCallback() { - @Override - public void done() { - connection.flush(); - } + sessionSPI.afterIO(connectionFlusher); + sessionSPI.resetContext(oldContext); + } + } - @Override - public void onError(int errorCode, String errorMessage) { - connection.flush(); - } - }); + private final class ConnectionFlushIOCallback implements IOCallback { + @Override + public void done() { + connection.flush(); + } - sessionSPI.resetContext(oldContext); + @Override + public void onError(int errorCode, String errorMessage) { + connection.flush(); } } @@ -681,16 +671,12 @@ public int deliverMessage(MessageReference messageReference, int deliveryCount, AMQPMessage message = CoreAmqpConverter.checkAMQP(messageReference.getMessage()); sessionSPI.invokeOutgoing(message, (ActiveMQProtonRemotingConnection) transportConnection.getProtocolConnection()); - // presettle means we can settle the message on the dealer side before we send it, i.e. - // for browsers - boolean preSettle = sender.getRemoteSenderSettleMode() == SenderSettleMode.SETTLED; - // we only need a tag if we are going to settle later byte[] tag = preSettle ? new byte[0] : protonSession.getTag(); // Let the Message decide how to present the message bytes - boolean attemptRelease = true; ReadableBuffer sendBuffer = message.getSendBuffer(deliveryCount); + boolean releaseRequired = sendBuffer instanceof NettyReadable; try { int size = sendBuffer.remaining(); @@ -713,14 +699,13 @@ public int deliverMessage(MessageReference messageReference, int deliveryCount, delivery.setMessageFormat((int) message.getMessageFormat()); delivery.setContext(messageReference); - if (sendBuffer instanceof NettyReadable) { + if (releaseRequired) { sender.send(sendBuffer); // Above send copied, so release now if needed - attemptRelease = false; + releaseRequired = false; ((NettyReadable) sendBuffer).getByteBuf().release(); } else { // Don't have pooled content, no need to release or copy. - attemptRelease = false; sender.sendNoCopy(sendBuffer); } @@ -731,6 +716,7 @@ public int deliverMessage(MessageReference messageReference, int deliveryCount, } else { sender.advance(); } + connection.flush(); } finally { connection.unlock(); @@ -738,7 +724,7 @@ public int deliverMessage(MessageReference messageReference, int deliveryCount, return size; } finally { - if (attemptRelease && sendBuffer instanceof NettyReadable) { + if (releaseRequired) { ((NettyReadable) sendBuffer).getByteBuf().release(); } } diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java index 38ca7a7978f..694c1d36e78 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java @@ -243,24 +243,9 @@ public void inputBuffer(ByteBuf buffer) { int capacity = transport.capacity(); if (!receivedFirstPacket) { - try { - byte auth = buffer.getByte(4); - if (auth == SASL || auth == BARE) { - if (isServer) { - dispatchAuth(auth == SASL); - } else if (auth == BARE && clientSASLMechanism == null) { - dispatchAuthSuccess(); - } - /* - * there is a chance that if SASL Handshake has been carried out that the capacity may change. - * */ - capacity = transport.capacity(); - } - } catch (Throwable e) { - log.warn(e.getMessage(), e); - } - - receivedFirstPacket = true; + handleFirstPacket(buffer); + // there is a chance that if SASL Handshake has been carried out that the capacity may change. + capacity = transport.capacity(); } if (capacity > 0) { @@ -537,4 +522,21 @@ public void createClientSASL() { sasl.client(); sasl.setListener(this); } + + private void handleFirstPacket(ByteBuf buffer) { + try { + byte auth = buffer.getByte(4); + if (auth == SASL || auth == BARE) { + if (isServer) { + dispatchAuth(auth == SASL); + } else if (auth == BARE && clientSASLMechanism == null) { + dispatchAuthSuccess(); + } + } + } catch (Throwable e) { + log.warn(e.getMessage(), e); + } + + receivedFirstPacket = true; + } } diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/transaction/ProtonTransactionHandler.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/transaction/ProtonTransactionHandler.java index 28573e0664d..9ccc1964e31 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/transaction/ProtonTransactionHandler.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/transaction/ProtonTransactionHandler.java @@ -73,32 +73,27 @@ public void onMessage(Delivery delivery) throws ActiveMQAMQPException { ByteBuffer buffer; MessageImpl msg; - connection.lock(); - try { - // Replenish coordinator receiver credit on exhaustion so sender can continue - // transaction declare and discahrge operations. - if (receiver.getCredit() < amqpLowMark) { - receiver.flow(amqpCredit); - } + // Replenish coordinator receiver credit on exhaustion so sender can continue + // transaction declare and discahrge operations. + if (receiver.getCredit() < amqpLowMark) { + receiver.flow(amqpCredit); + } - // Declare is generally 7 bytes and discharge is around 48 depending on the - // encoded size of the TXN ID. Decode buffer has a bit of extra space but if - // the incoming request is to big just use a scratch buffer. - if (delivery.available() > DECODE_BUFFER.capacity()) { - buffer = ByteBuffer.allocate(delivery.available()); - } else { - buffer = (ByteBuffer) DECODE_BUFFER.clear(); - } + // Declare is generally 7 bytes and discharge is around 48 depending on the + // encoded size of the TXN ID. Decode buffer has a bit of extra space but if + // the incoming request is to big just use a scratch buffer. + if (delivery.available() > DECODE_BUFFER.capacity()) { + buffer = ByteBuffer.allocate(delivery.available()); + } else { + buffer = (ByteBuffer) DECODE_BUFFER.clear(); + } - // Update Buffer for the next incoming command. - buffer.limit(receiver.recv(buffer.array(), buffer.arrayOffset(), buffer.capacity())); + // Update Buffer for the next incoming command. + buffer.limit(receiver.recv(buffer.array(), buffer.arrayOffset(), buffer.capacity())); - receiver.advance(); + receiver.advance(); - msg = decodeMessage(buffer); - } finally { - connection.unlock(); - } + msg = decodeMessage(buffer); Object action = ((AmqpValue) msg.getBody()).getValue(); if (action instanceof Declare) { @@ -160,23 +155,13 @@ public void onError(int errorCode, String errorMessage) { } } catch (ActiveMQAMQPException amqpE) { log.warn(amqpE.getMessage(), amqpE); - connection.lock(); - try { - delivery.settle(); - delivery.disposition(createRejected(amqpE.getAmqpError(), amqpE.getMessage())); - } finally { - connection.unlock(); - } + delivery.settle(); + delivery.disposition(createRejected(amqpE.getAmqpError(), amqpE.getMessage())); connection.flush(); } catch (Throwable e) { log.warn(e.getMessage(), e); - connection.lock(); - try { - delivery.settle(); - delivery.disposition(createRejected(Symbol.getSymbol("failed"), e.getMessage())); - } finally { - connection.unlock(); - } + delivery.settle(); + delivery.disposition(createRejected(Symbol.getSymbol("failed"), e.getMessage())); connection.flush(); } } diff --git a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContextTest.java b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContextTest.java index 88dfe3a9d37..a157ef1435a 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContextTest.java +++ b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/proton/ProtonServerReceiverContextTest.java @@ -49,17 +49,19 @@ private void doOnMessageWithAbortedDeliveryTestImpl(boolean drain) throws Active ProtonServerReceiverContext rc = new ProtonServerReceiverContext(null, mockConnContext, null, mockReceiver); Delivery mockDelivery = mock(Delivery.class); - when(mockDelivery.isReadable()).thenReturn(true); when(mockDelivery.isAborted()).thenReturn(true); when(mockDelivery.isPartial()).thenReturn(true); when(mockDelivery.getLink()).thenReturn(mockReceiver); + when(mockReceiver.current()).thenReturn(mockDelivery); + if (drain) { when(mockReceiver.getDrain()).thenReturn(true); } rc.onMessage(mockDelivery); + verify(mockReceiver, times(1)).current(); verify(mockReceiver, times(1)).advance(); verify(mockDelivery, times(1)).settle(); From 5d69e3594f875041f2d59ecb8cf0bcea067ecad4 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 12 Sep 2018 16:22:20 -0400 Subject: [PATCH 200/207] ARTEMIS-2074 Fixing RA properties --- .../artemis/jms/client/ActiveMQConnectionFactory.java | 6 +++--- .../activemq/artemis/ra/ActiveMQRAProperties.java | 11 +++++++++++ .../activemq/artemis/ra/ActiveMQResourceAdapter.java | 10 ++++++++++ .../unit/ra/ActiveMQResourceAdapterConfigTest.java | 6 ++++++ .../unit/ra/ConnectionFactoryPropertiesTest.java | 2 ++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java index df4611e442f..77091927abb 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java @@ -67,11 +67,11 @@ public class ActiveMQConnectionFactory extends JNDIStorable implements Connectio private static final long serialVersionUID = 6730844785641767519L; - public ServerLocator setUseTopologyForLoadBalancing(boolean useTopologyForLoadBalancing) { - return serverLocator.setUseTopologyForLoadBalancing(useTopologyForLoadBalancing); + public void setUseTopologyForLoadBalancing(boolean useTopologyForLoadBalancing) { + serverLocator.setUseTopologyForLoadBalancing(useTopologyForLoadBalancing); } - public boolean getUseTopologyForLoadBalancing() { + public boolean isUseTopologyForLoadBalancing() { return serverLocator.getUseTopologyForLoadBalancing(); } diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAProperties.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAProperties.java index e56e80d9915..10b281d922d 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAProperties.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAProperties.java @@ -20,6 +20,7 @@ import java.util.Hashtable; import org.apache.activemq.artemis.api.core.ActiveMQException; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.utils.PasswordMaskingUtil; /** @@ -37,6 +38,8 @@ public class ActiveMQRAProperties extends ConnectionFactoryProperties implements private static boolean trace = ActiveMQRALogger.LOGGER.isTraceEnabled(); protected boolean allowLocalTransactions; + protected boolean useTopologyForLoadBalancing = ActiveMQClient.DEFAULT_USE_TOPOLOGY_FOR_LOADBALANCING; + /** * The user name */ @@ -250,6 +253,14 @@ public synchronized void init() throws ActiveMQException { initialized = true; } + public void setUseTopologyForLoadBalancing(boolean useTopologyForLoadBalancing) { + this.useTopologyForLoadBalancing = useTopologyForLoadBalancing; + } + + public boolean isUseTopologyForLoadBalancing() { + return useTopologyForLoadBalancing; + } + public String getCodec() { return passwordCodec; } diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java index 0b4cbb4a9ad..7a21184a666 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java @@ -908,6 +908,14 @@ public void setProducerMaxRate(final Integer producerMaxRate) { raProperties.setProducerMaxRate(producerMaxRate); } + public void setUseTopologyForLoadBalancing(boolean useTopologyForLoadBalancing) { + raProperties.setUseTopologyForLoadBalancing(useTopologyForLoadBalancing); + } + + public boolean isUseTopologyForLoadBalancing() { + return raProperties.isUseTopologyForLoadBalancing(); + } + /** * Get producer window size * @@ -1792,6 +1800,8 @@ public ActiveMQConnectionFactory newConnectionFactory(ConnectionFactoryPropertie throw new IllegalArgumentException("must provide either TransportType or DiscoveryGroupAddress and DiscoveryGroupPort for ResourceAdapter Connection Factory"); } + cf.setUseTopologyForLoadBalancing(raProperties.isUseTopologyForLoadBalancing()); + cf.setEnableSharedClientID(true); cf.setEnable1xPrefixes(raProperties.isEnable1xPrefixes() == null ? false : raProperties.isEnable1xPrefixes()); setParams(cf, overrideProperties); diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ActiveMQResourceAdapterConfigTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ActiveMQResourceAdapterConfigTest.java index 0473db406ff..8d923b7ecd3 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ActiveMQResourceAdapterConfigTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ActiveMQResourceAdapterConfigTest.java @@ -420,6 +420,12 @@ public class ActiveMQResourceAdapterConfigTest extends ActiveMQTestBase { " Enable1xPrefixes" + " boolean" + " " + + " " + + " " + + " ***add***" + + " UseTopologyForLoadBalancing" + + " boolean" + + " " + " "; private static String rootConfig = "" + config + commentedOutConfigs + ""; diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ConnectionFactoryPropertiesTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ConnectionFactoryPropertiesTest.java index b463e303832..4f0885f5bab 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ConnectionFactoryPropertiesTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/ra/ConnectionFactoryPropertiesTest.java @@ -29,6 +29,7 @@ import org.apache.activemq.artemis.ra.ActiveMQResourceAdapter; import org.apache.activemq.artemis.ra.ConnectionFactoryProperties; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.junit.Assert; import org.junit.Test; import static java.beans.Introspector.getBeanInfo; @@ -80,6 +81,7 @@ public class ConnectionFactoryPropertiesTest extends ActiveMQTestBase { @Test public void testCompareConnectionFactoryAndResourceAdapterProperties() throws Exception { SortedSet connectionFactoryProperties = findAllPropertyNames(ActiveMQConnectionFactory.class); + Assert.assertTrue(connectionFactoryProperties.contains("useTopologyForLoadBalancing")); connectionFactoryProperties.removeAll(UNSUPPORTED_CF_PROPERTIES); SortedSet raProperties = findAllPropertyNames(ActiveMQResourceAdapter.class); raProperties.removeAll(UNSUPPORTED_RA_PROPERTIES); From 5dbd0a74d229f7dcf7cae91eca91694d21861f4f Mon Sep 17 00:00:00 2001 From: Timothy Bish Date: Wed, 12 Sep 2018 16:43:57 -0400 Subject: [PATCH 201/207] NO-JIRA Fix condition timeouts to avoid spurious failures The waits for expiration are set at the same value as the expiration interval default to in the test support setup so on some runs there is a race when waiting for the message to expire and the expiry task. --- .../tests/integration/amqp/AmqpExpiredMessageTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpExpiredMessageTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpExpiredMessageTest.java index b130ba8ad3d..b8d9beedddf 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpExpiredMessageTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpExpiredMessageTest.java @@ -85,7 +85,6 @@ public void testExpiryThroughTTL() throws Exception { Thread.sleep(100); - // Now try and get the message AmqpReceiver receiver = session.createReceiver(getQueueName()); receiver.flow(1); @@ -343,7 +342,7 @@ public void internalSendExpiry(boolean restartServer) throws Throwable { sender.send(message); Queue dlq = getProxyToQueue(getDeadLetterAddress()); - assertTrue("Message not movied to DLQ", Wait.waitFor(() -> dlq.getMessageCount() > 0, 5000, 500)); + assertTrue("Message not movied to DLQ", Wait.waitFor(() -> dlq.getMessageCount() > 0, 7000, 500)); connection.close(); @@ -388,7 +387,7 @@ public void testDLQdMessageCanBeRedeliveredMultipleTimes() throws Throwable { sender.send(message); Queue dlqView = getProxyToQueue(getDeadLetterAddress()); - assertTrue("Message not movied to DLQ", Wait.waitFor(() -> dlqView.getMessageCount() > 0, 5000, 200)); + assertTrue("Message not movied to DLQ", Wait.waitFor(() -> dlqView.getMessageCount() > 0, 7000, 200)); // Read and Modify the message for redelivery repeatedly AmqpReceiver receiver = session.createReceiver(getDeadLetterAddress()); From 369e475af629d2159abae3a7b3e4162b924f7a3a Mon Sep 17 00:00:00 2001 From: Timothy Bish Date: Wed, 12 Sep 2018 12:42:45 -0400 Subject: [PATCH 202/207] ARTEMIS-2083 Decode only the relavent portions of the message Ensure that the Body of the message is never decoded in the partial decode phase of the message processing and also gaurd against the decode of ApplicationProperties which should be done lazily. Add lazy decode of DeliveryAnnotations as they are not used at present. --- .../protocol/amqp/broker/AMQPMessage.java | 138 +++++++++--------- .../amqp/message/AMQPMessageTest.java | 137 ++++++++++++++++- 2 files changed, 201 insertions(+), 74 deletions(-) diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java index c0f9d102c15..cff52290c20 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPMessage.java @@ -53,9 +53,9 @@ import org.apache.qpid.proton.amqp.messaging.Header; import org.apache.qpid.proton.amqp.messaging.MessageAnnotations; import org.apache.qpid.proton.amqp.messaging.Properties; -import org.apache.qpid.proton.amqp.messaging.Section; import org.apache.qpid.proton.codec.DecoderImpl; import org.apache.qpid.proton.codec.ReadableBuffer; +import org.apache.qpid.proton.codec.TypeConstructor; import org.apache.qpid.proton.codec.WritableBuffer; import org.apache.qpid.proton.message.Message; import org.apache.qpid.proton.message.impl.MessageImpl; @@ -93,6 +93,7 @@ public class AMQPMessage extends RefCountMessage { private DeliveryAnnotations _deliveryAnnotations; private MessageAnnotations _messageAnnotations; private Properties _properties; + private int deliveryAnnotationsPosition = -1; private int appLocation = -1; private ApplicationProperties applicationProperties; private long scheduledTime = -1; @@ -195,16 +196,30 @@ private ApplicationProperties getApplicationProperties() { buffer.position(appLocation); TLSEncode.getDecoder().setBuffer(buffer); Object section = TLSEncode.getDecoder().readObject(); - if (section instanceof ApplicationProperties) { - this.applicationProperties = (ApplicationProperties) section; - } - this.appLocation = -1; + applicationProperties = (ApplicationProperties) section; + appLocation = -1; TLSEncode.getDecoder().setBuffer(null); } return applicationProperties; } + private DeliveryAnnotations getDeliveryAnnotations() { + parseHeaders(); + + if (_deliveryAnnotations == null && deliveryAnnotationsPosition >= 0) { + ReadableBuffer buffer = data.duplicate(); + buffer.position(deliveryAnnotationsPosition); + TLSEncode.getDecoder().setBuffer(buffer); + Object section = TLSEncode.getDecoder().readObject(); + _deliveryAnnotations = (DeliveryAnnotations) section; + deliveryAnnotationsPosition = -1; + TLSEncode.getDecoder().setBuffer(null); + } + + return _deliveryAnnotations; + } + private synchronized void parseHeaders() { if (!parsedHeaders) { if (data == null) { @@ -380,83 +395,63 @@ private synchronized void partialDecode(ReadableBuffer buffer) { decoder.setBuffer(buffer.rewind()); _header = null; + expiration = 0; + headerEnds = 0; + messagePaylodStart = 0; _deliveryAnnotations = null; _messageAnnotations = null; _properties = null; applicationProperties = null; - Section section = null; + appLocation = -1; + deliveryAnnotationsPosition = -1; try { - if (buffer.hasRemaining()) { - section = (Section) decoder.readObject(); - } - - if (section instanceof Header) { - _header = (Header) section; - headerEnds = buffer.position(); - messagePaylodStart = headerEnds; - this.durable = _header.getDurable(); - - if (_header.getTtl() != null) { - this.expiration = System.currentTimeMillis() + _header.getTtl().intValue(); - } - - if (buffer.hasRemaining()) { - section = (Section) decoder.readObject(); - } else { - section = null; - } - - } else { - // meaning there is no header - headerEnds = 0; - } - if (section instanceof DeliveryAnnotations) { - _deliveryAnnotations = (DeliveryAnnotations) section; - - // Advance the start beyond the delivery annotations so they are not written - // out on send of the message. - messagePaylodStart = buffer.position(); - - if (buffer.hasRemaining()) { - section = (Section) decoder.readObject(); - } else { - section = null; - } - } - if (section instanceof MessageAnnotations) { - _messageAnnotations = (MessageAnnotations) section; - - if (buffer.hasRemaining()) { - section = (Section) decoder.readObject(); - } else { - section = null; - } - } - if (section instanceof Properties) { - _properties = (Properties) section; - - if (_properties.getAbsoluteExpiryTime() != null && _properties.getAbsoluteExpiryTime().getTime() > 0) { - this.expiration = _properties.getAbsoluteExpiryTime().getTime(); - } - - // We don't read the next section on purpose, as we will parse ApplicationProperties - // lazily - section = null; - } - - if (section instanceof ApplicationProperties) { - applicationProperties = (ApplicationProperties) section; - } else { - if (buffer.hasRemaining()) { - this.appLocation = buffer.position(); + while (buffer.hasRemaining()) { + int constructorPos = buffer.position(); + TypeConstructor constructor = decoder.readConstructor(); + if (Header.class.equals(constructor.getTypeClass())) { + _header = (Header) constructor.readValue(); + headerEnds = messagePaylodStart = buffer.position(); + durable = _header.getDurable(); + if (_header.getTtl() != null) { + expiration = System.currentTimeMillis() + _header.getTtl().intValue(); + } + } else if (DeliveryAnnotations.class.equals(constructor.getTypeClass())) { + // Don't decode these as they are not used by the broker at all and are + // discarded on send, mark for lazy decode if ever needed. + constructor.skipValue(); + deliveryAnnotationsPosition = constructorPos; + messagePaylodStart = buffer.position(); + } else if (MessageAnnotations.class.equals(constructor.getTypeClass())) { + _messageAnnotations = (MessageAnnotations) constructor.readValue(); + } else if (Properties.class.equals(constructor.getTypeClass())) { + _properties = (Properties) constructor.readValue(); + + if (_properties.getAbsoluteExpiryTime() != null && _properties.getAbsoluteExpiryTime().getTime() > 0) { + expiration = _properties.getAbsoluteExpiryTime().getTime(); + } + + // Next is either Application Properties or the rest of the message, leave it for + // lazy decode of the ApplicationProperties should there be any. Check first though + // as we don't want to actually decode the body which could be expensive. + if (buffer.hasRemaining()) { + constructor = decoder.peekConstructor(); + if (ApplicationProperties.class.equals(constructor.getTypeClass())) { + appLocation = buffer.position(); + } + } + break; + } else if (ApplicationProperties.class.equals(constructor.getTypeClass())) { + // Lazy decoding will start at the TypeConstructor of these ApplicationProperties + appLocation = constructorPos; + break; } else { - this.appLocation = -1; + break; } } } finally { decoder.setByteBuffer(null); - data.position(0); + buffer.position(0); } } @@ -1082,6 +1077,7 @@ public AMQPMessage setAnnotation(SimpleString key, Object value) { public void reencode() { parseHeaders(); getApplicationProperties(); + getDeliveryAnnotations(); if (_header != null) getProtonMessage().setHeader(_header); if (_deliveryAnnotations != null) getProtonMessage().setDeliveryAnnotations(_deliveryAnnotations); if (_messageAnnotations != null) getProtonMessage().setMessageAnnotations(_messageAnnotations); diff --git a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/message/AMQPMessageTest.java b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/message/AMQPMessageTest.java index 42ffaeeca90..a6a29a0e3fc 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/message/AMQPMessageTest.java +++ b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/message/AMQPMessageTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.nio.charset.StandardCharsets; import java.util.Date; @@ -32,13 +33,20 @@ import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.protocol.amqp.broker.AMQPMessage; import org.apache.activemq.artemis.protocol.amqp.broker.AMQPMessagePersisterV2; +import org.apache.activemq.artemis.protocol.amqp.util.NettyReadable; import org.apache.activemq.artemis.protocol.amqp.util.NettyWritable; +import org.apache.activemq.artemis.protocol.amqp.util.TLSEncode; import org.apache.activemq.artemis.spi.core.protocol.EmbedMessageUtil; import org.apache.activemq.artemis.utils.RandomUtil; +import org.apache.qpid.proton.amqp.UnsignedByte; import org.apache.qpid.proton.amqp.UnsignedInteger; +import org.apache.qpid.proton.amqp.UnsignedLong; import org.apache.qpid.proton.amqp.messaging.ApplicationProperties; import org.apache.qpid.proton.amqp.messaging.Header; import org.apache.qpid.proton.amqp.messaging.Properties; +import org.apache.qpid.proton.codec.EncoderImpl; +import org.apache.qpid.proton.codec.EncodingCodes; +import org.apache.qpid.proton.codec.ReadableBuffer; import org.apache.qpid.proton.message.Message; import org.apache.qpid.proton.message.impl.MessageImpl; import org.junit.Assert; @@ -58,7 +66,7 @@ public void testVerySimple() { protonMessage.setProperties(properties); protonMessage.getHeader().setDeliveryCount(new UnsignedInteger(7)); protonMessage.getHeader().setDurable(Boolean.TRUE); - protonMessage.setApplicationProperties(new ApplicationProperties(new HashMap())); + protonMessage.setApplicationProperties(new ApplicationProperties(new HashMap<>())); AMQPMessage decoded = encodeAndDecodeMessage(protonMessage); @@ -76,7 +84,7 @@ public void testApplicationPropertiesReencode() { protonMessage.setProperties(properties); protonMessage.getHeader().setDeliveryCount(new UnsignedInteger(7)); protonMessage.getHeader().setDurable(Boolean.TRUE); - HashMap map = new HashMap(); + HashMap map = new HashMap<>(); map.put("key", "string1"); protonMessage.setApplicationProperties(new ApplicationProperties(map)); @@ -97,7 +105,6 @@ public void testApplicationPropertiesReencode() { assertEquals(true, newDecoded.getHeader().getDurable()); assertEquals("newAddress", newDecoded.getAddress()); assertEquals("string1", newDecoded.getObjectProperty("key")); - } @Test @@ -281,7 +288,131 @@ public void testExtraProperty() { Assert.assertEquals("someAddress", readMessage.getAddress()); Assert.assertArrayEquals(original, readMessage.getExtraBytesProperty(name)); } + } + + private static final UnsignedLong AMQPVALUE_DESCRIPTOR = UnsignedLong.valueOf(0x0000000000000077L); + private static final UnsignedLong APPLICATION_PROPERTIES_DESCRIPTOR = UnsignedLong.valueOf(0x0000000000000074L); + private static final UnsignedLong DELIVERY_ANNOTATIONS_DESCRIPTOR = UnsignedLong.valueOf(0x0000000000000071L); + + @Test + public void testPartialDecodeIgnoresDeliveryAnnotationsByDefault() { + Header header = new Header(); + header.setDurable(true); + header.setPriority(UnsignedByte.valueOf((byte) 6)); + + ByteBuf encodedBytes = Unpooled.buffer(1024); + NettyWritable writable = new NettyWritable(encodedBytes); + + EncoderImpl encoder = TLSEncode.getEncoder(); + encoder.setByteBuffer(writable); + encoder.writeObject(header); + + // Signal body of AmqpValue but write corrupt underlying type info + encodedBytes.writeByte(EncodingCodes.DESCRIBED_TYPE_INDICATOR); + encodedBytes.writeByte(EncodingCodes.SMALLULONG); + encodedBytes.writeByte(DELIVERY_ANNOTATIONS_DESCRIPTOR.byteValue()); + encodedBytes.writeByte(EncodingCodes.MAP8); + encodedBytes.writeByte(2); // Size + encodedBytes.writeByte(2); // Elements + // Use bad encoding code on underlying type of map key which will fail the decode if run + encodedBytes.writeByte(255); + + ReadableBuffer readable = new NettyReadable(encodedBytes); + + AMQPMessage message = null; + try { + message = new AMQPMessage(0, readable, null, null); + } catch (Exception decodeError) { + fail("Should not have encountered an exception on partial decode: " + decodeError.getMessage()); + } + try { + // This should perform the lazy decode of the DeliveryAnnotations portion of the message + message.reencode(); + fail("Should have thrown an error when attempting to decode the ApplicationProperties which are malformed."); + } catch (Exception ex) { + // Expected decode to fail when building full message. + } + } + + @Test + public void testPartialDecodeIgnoresApplicationPropertiesByDefault() { + Header header = new Header(); + header.setDurable(true); + header.setPriority(UnsignedByte.valueOf((byte) 6)); + + ByteBuf encodedBytes = Unpooled.buffer(1024); + NettyWritable writable = new NettyWritable(encodedBytes); + + EncoderImpl encoder = TLSEncode.getEncoder(); + encoder.setByteBuffer(writable); + encoder.writeObject(header); + + // Signal body of AmqpValue but write corrupt underlying type info + encodedBytes.writeByte(EncodingCodes.DESCRIBED_TYPE_INDICATOR); + encodedBytes.writeByte(EncodingCodes.SMALLULONG); + encodedBytes.writeByte(APPLICATION_PROPERTIES_DESCRIPTOR.byteValue()); + // Use bad encoding code on underlying type + encodedBytes.writeByte(255); + + ReadableBuffer readable = new NettyReadable(encodedBytes); + + AMQPMessage message = null; + try { + message = new AMQPMessage(0, readable, null, null); + } catch (Exception decodeError) { + fail("Should not have encountered an exception on partial decode: " + decodeError.getMessage()); + } + + assertTrue(message.isDurable()); + + try { + // This should perform the lazy decode of the ApplicationProperties portion of the message + message.getStringProperty("test"); + fail("Should have thrown an error when attempting to decode the ApplicationProperties which are malformed."); + } catch (Exception ex) { + // Expected decode to fail when building full message. + } + } + + @Test + public void testPartialDecodeIgnoresBodyByDefault() { + Header header = new Header(); + header.setDurable(true); + header.setPriority(UnsignedByte.valueOf((byte) 6)); + + ByteBuf encodedBytes = Unpooled.buffer(1024); + NettyWritable writable = new NettyWritable(encodedBytes); + + EncoderImpl encoder = TLSEncode.getEncoder(); + encoder.setByteBuffer(writable); + encoder.writeObject(header); + + // Signal body of AmqpValue but write corrupt underlying type info + encodedBytes.writeByte(EncodingCodes.DESCRIBED_TYPE_INDICATOR); + encodedBytes.writeByte(EncodingCodes.SMALLULONG); + encodedBytes.writeByte(AMQPVALUE_DESCRIPTOR.byteValue()); + // Use bad encoding code on underlying type + encodedBytes.writeByte(255); + + ReadableBuffer readable = new NettyReadable(encodedBytes); + + AMQPMessage message = null; + try { + message = new AMQPMessage(0, readable, null, null); + } catch (Exception decodeError) { + fail("Should not have encountered an exception on partial decode: " + decodeError.getMessage()); + } + + assertTrue(message.isDurable()); + + try { + // This will decode the body section if present in order to present it as a Proton Message object + message.getProtonMessage(); + fail("Should have thrown an error when attempting to decode the body which is malformed."); + } catch (Exception ex) { + // Expected decode to fail when building full message. + } } private AMQPMessage encodeAndDecodeMessage(MessageImpl message) { From 25a7d3bffcc3d3e7370a293b9a8f07f880b93ebd Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 12 Sep 2018 17:55:04 -0400 Subject: [PATCH 203/207] ARTEMIS-2074 Fixing Covery scan findings --- .../jms/client/ActiveMQConnectionFactory.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java index 77091927abb..946ea956703 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java @@ -67,14 +67,6 @@ public class ActiveMQConnectionFactory extends JNDIStorable implements Connectio private static final long serialVersionUID = 6730844785641767519L; - public void setUseTopologyForLoadBalancing(boolean useTopologyForLoadBalancing) { - serverLocator.setUseTopologyForLoadBalancing(useTopologyForLoadBalancing); - } - - public boolean isUseTopologyForLoadBalancing() { - return serverLocator.getUseTopologyForLoadBalancing(); - } - private ServerLocator serverLocator; private String clientID; @@ -543,6 +535,16 @@ public synchronized void setCallFailoverTimeout(final long callTimeout) { serverLocator.setCallFailoverTimeout(callTimeout); } + public synchronized void setUseTopologyForLoadBalancing(boolean useTopologyForLoadBalancing) { + checkWrite(); + serverLocator.setUseTopologyForLoadBalancing(useTopologyForLoadBalancing); + } + + public synchronized boolean isUseTopologyForLoadBalancing() { + checkWrite(); + return serverLocator.getUseTopologyForLoadBalancing(); + } + public synchronized int getConsumerWindowSize() { return serverLocator.getConsumerWindowSize(); } From 6361079aa0533f31ad8b1b8e390fc70fda5ad217 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Thu, 6 Sep 2018 18:05:06 -0400 Subject: [PATCH 204/207] ARTEMIS-2084: Failover will not work with network cable disconnect on core protocol --- .../client/impl/ClientSessionFactoryImpl.java | 12 +++++++- .../remoting/impl/netty/NettyConnector.java | 8 +++++ .../artemis/tests/util/ActiveMQTestBase.java | 8 +++++ .../cluster/failover/FailoverTestBase.java | 4 +-- .../failover/NetworkFailureFailoverTest.java | 30 ++++++++++++++----- .../ra/ActiveMQRAClusteredTestBase.java | 3 +- 6 files changed, 52 insertions(+), 13 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java index 4723c88e0e6..daac8f34981 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java @@ -51,6 +51,7 @@ import org.apache.activemq.artemis.core.protocol.core.impl.ActiveMQSessionContext; import org.apache.activemq.artemis.core.remoting.FailureListener; import org.apache.activemq.artemis.core.remoting.impl.TransportConfigurationUtil; +import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnector; import org.apache.activemq.artemis.core.server.ActiveMQComponent; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.spi.core.remoting.BufferHandler; @@ -1060,7 +1061,16 @@ protected Connection openTransportConnection(final Connector connector) { } protected Connector createConnector(ConnectorFactory connectorFactory, TransportConfiguration configuration) { - return connectorFactory.createConnector(configuration.getParams(), new DelegatingBufferHandler(), this, closeExecutor, threadPool, scheduledThreadPool, clientProtocolManager); + Connector connector = connectorFactory.createConnector(configuration.getParams(), new DelegatingBufferHandler(), this, closeExecutor, threadPool, scheduledThreadPool, clientProtocolManager); + if (connector instanceof NettyConnector) { + NettyConnector nettyConnector = (NettyConnector) connector; + if (nettyConnector.getConnectTimeoutMillis() < 0) { + nettyConnector.setConnectTimeoutMillis((int)serverLocator.getConnectionTTL()); + } + + } + + return connector; } private void checkTransportKeys(final ConnectorFactory factory, final TransportConfiguration tc) { diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java index 284d0b90531..2ef5fed2dba 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyConnector.java @@ -843,6 +843,14 @@ public Connection createConnection() { // Public -------------------------------------------------------- + public int getConnectTimeoutMillis() { + return connectTimeoutMillis; + } + + public void setConnectTimeoutMillis(int connectTimeoutMillis) { + this.connectTimeoutMillis = connectTimeoutMillis; + } + // Package protected --------------------------------------------- // Protected ----------------------------------------------------- diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java b/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java index 2cd5d564cbe..9075ac6581e 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java @@ -552,6 +552,14 @@ protected Map generateInVMParams(final int node) { return params; } + + /** This exists as an extension point for tests, so tests can replace it */ + protected ClusterConnectionConfiguration createBasicClusterConfig(String connectorName, + String... connectors) { + return basicClusterConnectionConfig(connectorName, connectors); + } + + protected static final ClusterConnectionConfiguration basicClusterConnectionConfig(String connectorName, String... connectors) { ArrayList connectors0 = new ArrayList<>(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/FailoverTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/FailoverTestBase.java index 70e625a8570..2a75f94eafd 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/FailoverTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/FailoverTestBase.java @@ -165,11 +165,11 @@ protected void createConfigs() throws Exception { TransportConfiguration liveConnector = getConnectorTransportConfiguration(true); TransportConfiguration backupConnector = getConnectorTransportConfiguration(false); - backupConfig = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(getAcceptorTransportConfiguration(false)).setHAPolicyConfiguration(new SharedStoreSlavePolicyConfiguration()).addConnectorConfiguration(liveConnector.getName(), liveConnector).addConnectorConfiguration(backupConnector.getName(), backupConnector).addClusterConfiguration(basicClusterConnectionConfig(backupConnector.getName(), liveConnector.getName())); + backupConfig = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(getAcceptorTransportConfiguration(false)).setHAPolicyConfiguration(new SharedStoreSlavePolicyConfiguration()).addConnectorConfiguration(liveConnector.getName(), liveConnector).addConnectorConfiguration(backupConnector.getName(), backupConnector).addClusterConfiguration(createBasicClusterConfig(backupConnector.getName(), liveConnector.getName())); backupServer = createTestableServer(backupConfig); - liveConfig = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(getAcceptorTransportConfiguration(true)).setHAPolicyConfiguration(new SharedStoreMasterPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())).addConnectorConfiguration(liveConnector.getName(), liveConnector); + liveConfig = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(getAcceptorTransportConfiguration(true)).setHAPolicyConfiguration(new SharedStoreMasterPolicyConfiguration()).addClusterConfiguration(createBasicClusterConfig(liveConnector.getName())).addConnectorConfiguration(liveConnector.getName(), liveConnector); liveServer = createTestableServer(liveConfig); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NetworkFailureFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NetworkFailureFailoverTest.java index 10115024dc8..c2e539fb4d5 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NetworkFailureFailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/NetworkFailureFailoverTest.java @@ -13,6 +13,7 @@ package org.apache.activemq.artemis.tests.integration.cluster.failover; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -35,10 +36,12 @@ import org.apache.activemq.artemis.api.core.client.TopologyMember; import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal; import org.apache.activemq.artemis.core.client.impl.Topology; +import org.apache.activemq.artemis.core.config.ClusterConnectionConfiguration; import org.apache.activemq.artemis.core.protocol.core.Packet; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.CreateSessionMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionSendMessage; import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; +import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; import org.apache.activemq.artemis.junit.Wait; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.tests.util.network.NetUtil; @@ -138,8 +141,6 @@ protected TransportConfiguration getNettyConnectorTransportConfiguration(final b server1Params.put(TransportConstants.HOST_PROP_NAME, "localhost"); } - server1Params.put(TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); - return new TransportConfiguration(NETTY_CONNECTOR_FACTORY, server1Params); } @@ -151,7 +152,6 @@ public void testFailoverAfterNetFailure() throws Exception { Assert.assertTrue(NetUtil.checkIP(LIVE_IP)); Map params = new HashMap<>(); params.put(TransportConstants.HOST_PROP_NAME, LIVE_IP); - params.put(TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); TransportConfiguration tc = createTransportConfiguration(true, false, params); final AtomicInteger countSent = new AtomicInteger(0); @@ -195,8 +195,8 @@ public void run() { locator.setReconnectAttempts(-1); locator.setConfirmationWindowSize(-1); locator.setProducerWindowSize(-1); - locator.setClientFailureCheckPeriod(100); locator.setConnectionTTL(1000); + locator.setClientFailureCheckPeriod(100); ClientSessionFactoryInternal sfProducer = createSessionFactoryAndWaitForTopology(locator, 2); sfProducer.addFailureListener(new SessionFailureListener() { @Override @@ -312,7 +312,6 @@ public void testNetFailureConsume() throws Exception { Assert.assertTrue(NetUtil.checkIP(LIVE_IP)); Map params = new HashMap<>(); params.put(TransportConstants.HOST_PROP_NAME, LIVE_IP); - params.put(TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); TransportConfiguration tc = createTransportConfiguration(true, false, params); final AtomicInteger countSent = new AtomicInteger(0); @@ -442,7 +441,6 @@ public void testFailoverCreateSessionOnFailure() throws Exception { Assert.assertTrue(NetUtil.checkIP(LIVE_IP)); Map params = new HashMap<>(); params.put(TransportConstants.HOST_PROP_NAME, LIVE_IP); - params.put(TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); TransportConfiguration tc = createTransportConfiguration(true, false, params); final AtomicInteger countSent = new AtomicInteger(0); @@ -459,7 +457,6 @@ public boolean intercept(Packet packet, RemotingConnection connection) throws Ac try { NetUtil.netDown(LIVE_IP); System.out.println("Blocking traffic"); - Thread.sleep(3000); // this is important to let stuff to block blockedAt.set(sentMessages.get()); latchDown.countDown(); } catch (Exception e) { @@ -555,7 +552,6 @@ public void testInterruptFailingThread() throws Exception { Assert.assertTrue(NetUtil.checkIP(LIVE_IP)); Map params = new HashMap<>(); params.put(TransportConstants.HOST_PROP_NAME, LIVE_IP); - params.put(TransportConstants.NETTY_CONNECT_TIMEOUT, 1000); TransportConfiguration tc = createTransportConfiguration(true, false, params); final AtomicInteger countSent = new AtomicInteger(0); @@ -685,4 +681,22 @@ public void run() { t.join(); } + + + @Override + protected ClusterConnectionConfiguration createBasicClusterConfig(String connectorName, + String... connectors) { + ArrayList connectors0 = new ArrayList<>(); + for (String c : connectors) { + connectors0.add(c); + } + ClusterConnectionConfiguration clusterConnectionConfiguration = new ClusterConnectionConfiguration(). + setName("cluster1").setAddress("jms").setConnectorName(connectorName). + setRetryInterval(1000).setDuplicateDetection(false).setMaxHops(1).setClientFailureCheckPeriod(100).setConnectionTTL(1000). + setConfirmationWindowSize(1).setMessageLoadBalancingType(MessageLoadBalancingType.STRICT). + setStaticConnectors(connectors0); + + return clusterConnectionConfiguration; + } + } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ActiveMQRAClusteredTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ActiveMQRAClusteredTestBase.java index 9e3abf28ddd..cd14afe508d 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ActiveMQRAClusteredTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ActiveMQRAClusteredTestBase.java @@ -25,7 +25,6 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.jms.server.impl.JMSServerManagerImpl; -import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Before; public class ActiveMQRAClusteredTestBase extends ActiveMQRATestBase { @@ -73,7 +72,7 @@ protected Configuration createSecondaryDefaultConfig(boolean secondary) throws E index = 1; } - ConfigurationImpl configuration = createBasicConfig(index).setJMXManagementEnabled(false).clearAcceptorConfigurations().addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, invmMap)).addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, nettyMap)).addConnectorConfiguration(secondaryConnectorName, secondaryConnector).addConnectorConfiguration(primaryConnectorName, primaryConnector).addClusterConfiguration(ActiveMQTestBase.basicClusterConnectionConfig(secondaryConnectorName, primaryConnectorName).setReconnectAttempts(0)); + ConfigurationImpl configuration = createBasicConfig(index).setJMXManagementEnabled(false).clearAcceptorConfigurations().addAcceptorConfiguration(new TransportConfiguration(INVM_ACCEPTOR_FACTORY, invmMap)).addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, nettyMap)).addConnectorConfiguration(secondaryConnectorName, secondaryConnector).addConnectorConfiguration(primaryConnectorName, primaryConnector).addClusterConfiguration(basicClusterConnectionConfig(secondaryConnectorName, primaryConnectorName).setReconnectAttempts(0)); recreateDataDirectories(getTestDir(), index, false); From 7e09e1b3502361eb0ff51fe56af904922cf7e421 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Fri, 31 Aug 2018 20:32:48 +0200 Subject: [PATCH 205/207] ARTEMIS-2070 broker can reduce buffer copies with large messages --- .../tools/xml/XMLMessageExporter.java | 27 ++++++--- .../core/client/impl/ClientMessageImpl.java | 17 ++---- .../core/client/impl/ClientProducerImpl.java | 11 ++-- .../core/message/LargeBodyEncoder.java | 6 -- .../core/message/impl/CoreMessage.java | 25 ++++---- .../impl/journal/JournalStorageManager.java | 26 ++++++++ .../impl/journal/LargeServerMessageImpl.java | 37 ++++++------ .../nullpm/NullStorageLargeServerMessage.java | 20 +++++++ .../core/server/LargeServerMessage.java | 3 + .../core/server/impl/ServerConsumerImpl.java | 60 ++++++++++++++----- .../core/server/impl/ServerSessionImpl.java | 8 +-- 11 files changed, 151 insertions(+), 89 deletions(-) diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XMLMessageExporter.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XMLMessageExporter.java index a2fbaddef8a..f8063d1747d 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XMLMessageExporter.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XMLMessageExporter.java @@ -18,10 +18,9 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; +import java.nio.ByteBuffer; import java.util.List; -import org.apache.activemq.artemis.api.core.ActiveMQBuffer; -import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ICoreMessage; import org.apache.activemq.artemis.api.core.Message; @@ -34,7 +33,7 @@ /** This is an Utility class that will import the outputs in XML format. */ public class XMLMessageExporter { - private static final Long LARGE_MESSAGE_CHUNK_SIZE = 1000L; + private static final int LARGE_MESSAGE_CHUNK_SIZE = 1000; private XMLStreamWriter xmlWriter; @@ -70,6 +69,15 @@ public void printMessageBody(Message message, boolean encodeTextMessageUTF8) thr xmlWriter.writeEndElement(); // end MESSAGE_BODY } + private static ByteBuffer acquireHeapBodyBuffer(ByteBuffer chunkBytes, int requiredCapacity) { + if (chunkBytes == null || chunkBytes.capacity() != requiredCapacity) { + chunkBytes = ByteBuffer.allocate(requiredCapacity); + } else { + chunkBytes.clear(); + } + return chunkBytes; + } + public void printLargeMessageBody(LargeServerMessage message) throws XMLStreamException { xmlWriter.writeAttribute(XmlDataConstants.MESSAGE_IS_LARGE, Boolean.TRUE.toString()); LargeBodyEncoder encoder = null; @@ -78,18 +86,19 @@ public void printLargeMessageBody(LargeServerMessage message) throws XMLStreamEx encoder = message.toCore().getBodyEncoder(); encoder.open(); long totalBytesWritten = 0; - Long bufferSize; + int bufferSize; long bodySize = encoder.getLargeBodySize(); + ByteBuffer buffer = null; for (long i = 0; i < bodySize; i += LARGE_MESSAGE_CHUNK_SIZE) { - Long remainder = bodySize - totalBytesWritten; + long remainder = bodySize - totalBytesWritten; if (remainder >= LARGE_MESSAGE_CHUNK_SIZE) { bufferSize = LARGE_MESSAGE_CHUNK_SIZE; } else { - bufferSize = remainder; + bufferSize = (int) remainder; } - ActiveMQBuffer buffer = ActiveMQBuffers.fixedBuffer(bufferSize.intValue()); - encoder.encode(buffer, bufferSize.intValue()); - xmlWriter.writeCData(XmlDataExporterUtil.encode(buffer.toByteBuffer().array())); + buffer = acquireHeapBodyBuffer(buffer, bufferSize); + encoder.encode(buffer); + xmlWriter.writeCData(XmlDataExporterUtil.encode(buffer.array())); totalBytesWritten += bufferSize; } encoder.close(); diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientMessageImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientMessageImpl.java index d14c64ee31b..52ceb992dd4 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientMessageImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientMessageImpl.java @@ -21,8 +21,6 @@ import java.io.OutputStream; import java.nio.ByteBuffer; -import org.apache.activemq.artemis.api.core.ActiveMQBuffer; -import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQPropertyConversionException; import org.apache.activemq.artemis.api.core.Message; @@ -400,17 +398,10 @@ public long getLargeBodySize() { } @Override - public int encode(final ByteBuffer bufferRead) throws ActiveMQException { - ActiveMQBuffer buffer1 = ActiveMQBuffers.wrappedBuffer(bufferRead); - return encode(buffer1, bufferRead.capacity()); - } - - @Override - public int encode(final ActiveMQBuffer bufferOut, final int size) { - byte[] bytes = new byte[size]; - buffer.readBytes(bytes); - bufferOut.writeBytes(bytes, 0, size); - return size; + public int encode(final ByteBuffer bufferRead) { + final int remaining = bufferRead.remaining(); + buffer.readBytes(bufferRead); + return remaining; } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientProducerImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientProducerImpl.java index 0ad1999316c..5cda0c433a9 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientProducerImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientProducerImpl.java @@ -18,10 +18,9 @@ import java.io.IOException; import java.io.InputStream; +import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicLong; -import org.apache.activemq.artemis.api.core.ActiveMQBuffer; -import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ICoreMessage; import org.apache.activemq.artemis.api.core.Message; @@ -381,16 +380,18 @@ private void largeMessageSendServer(final boolean sendBlocking, final int chunkLength = (int) Math.min((bodySize - pos), minLargeMessageSize); - final ActiveMQBuffer bodyBuffer = ActiveMQBuffers.fixedBuffer(chunkLength); + final ByteBuffer bodyBuffer = ByteBuffer.allocate(chunkLength); - context.encode(bodyBuffer, chunkLength); + final int encodedSize = context.encode(bodyBuffer); + + assert encodedSize == chunkLength; pos += chunkLength; lastChunk = pos >= bodySize; SendAcknowledgementHandler messageHandler = lastChunk ? handler : null; - int creditsUsed = sessionContext.sendServerLargeMessageChunk(msgI, -1, sendBlocking, lastChunk, bodyBuffer.toByteBuffer().array(), messageHandler); + int creditsUsed = sessionContext.sendServerLargeMessageChunk(msgI, -1, sendBlocking, lastChunk, bodyBuffer.array(), messageHandler); credits.acquireCredits(creditsUsed); } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/LargeBodyEncoder.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/LargeBodyEncoder.java index 7a248b4b97d..87e5ba6cec7 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/LargeBodyEncoder.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/LargeBodyEncoder.java @@ -18,7 +18,6 @@ import java.nio.ByteBuffer; -import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQException; /** @@ -43,11 +42,6 @@ public interface LargeBodyEncoder { */ int encode(ByteBuffer bufferRead) throws ActiveMQException; - /** - * This method must not be called directly by ActiveMQ Artemis clients. - */ - int encode(ActiveMQBuffer bufferOut, int size) throws ActiveMQException; - /** * This method must not be called directly by ActiveMQ Artemis clients. */ diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java index 5272200e01d..cc79c2c830d 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java @@ -254,14 +254,14 @@ public ActiveMQBuffer getDataBuffer() { } private ActiveMQBuffer getLargeMessageBuffer() throws ActiveMQException { - ActiveMQBuffer buffer; LargeBodyEncoder encoder = getBodyEncoder(); encoder.open(); int bodySize = (int) encoder.getLargeBodySize(); - - buffer = new ChannelBufferWrapper(UnpooledByteBufAllocator.DEFAULT.heapBuffer(bodySize)); - - encoder.encode(buffer, bodySize); + final ActiveMQBuffer buffer = new ChannelBufferWrapper(UnpooledByteBufAllocator.DEFAULT.heapBuffer(bodySize)); + buffer.byteBuf().ensureWritable(bodySize); + final ByteBuffer nioBuffer = buffer.byteBuf().internalNioBuffer(0, bodySize); + encoder.encode(nioBuffer); + buffer.writerIndex(bodySize); encoder.close(); return buffer; } @@ -1154,16 +1154,11 @@ public long getLargeBodySize() { } @Override - public int encode(final ByteBuffer bufferRead) throws ActiveMQException { - ActiveMQBuffer buffer = ActiveMQBuffers.wrappedBuffer(bufferRead); - return encode(buffer, bufferRead.capacity()); - } - - @Override - public int encode(final ActiveMQBuffer bufferOut, final int size) { - bufferOut.byteBuf().writeBytes(buffer, lastPos, size); - lastPos += size; - return size; + public int encode(final ByteBuffer bufferRead) { + final int remaining = bufferRead.remaining(); + buffer.getBytes(lastPos, bufferRead); + lastPos += remaining; + return remaining; } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java index 867f2d43ece..1d954c8b168 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalStorageManager.java @@ -783,6 +783,32 @@ public void stopReplication() { } } + public final void addBytesToLargeMessage(final SequentialFile file, + final long messageId, + final ActiveMQBuffer bytes) throws Exception { + readLock(); + try { + file.position(file.size()); + if (bytes.byteBuf() != null && bytes.byteBuf().nioBufferCount() == 1) { + final ByteBuffer nioBytes = bytes.byteBuf().internalNioBuffer(bytes.readerIndex(), bytes.readableBytes()); + file.writeDirect(nioBytes, false); + + if (isReplicated()) { + //copy defensively bytes + final byte[] bytesCopy = new byte[bytes.readableBytes()]; + bytes.getBytes(bytes.readerIndex(), bytesCopy); + replicator.largeMessageWrite(messageId, bytesCopy); + } + } else { + final byte[] bytesCopy = new byte[bytes.readableBytes()]; + bytes.readBytes(bytesCopy); + addBytesToLargeMessage(file, messageId, bytesCopy); + } + } finally { + readUnLock(); + } + } + @Override public final void addBytesToLargeMessage(final SequentialFile file, final long messageId, diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/LargeServerMessageImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/LargeServerMessageImpl.java index 257141ebb29..110070c2049 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/LargeServerMessageImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/LargeServerMessageImpl.java @@ -53,10 +53,6 @@ public final class LargeServerMessageImpl extends CoreMessage implements LargeSe // We should only use the NIO implementation on the Journal private SequentialFile file; - // set when a copyFrom is called - // The actual copy is done when finishCopy is called - private SequentialFile pendingCopy; - private long bodySize = -1; private final AtomicInteger delayDeletionCount = new AtomicInteger(0); @@ -131,6 +127,21 @@ public synchronized void addBytes(final byte[] bytes) throws Exception { bodySize += bytes.length; } + @Override + public synchronized void addBytes(final ActiveMQBuffer bytes) throws Exception { + validateFile(); + + if (!file.isOpen()) { + file.open(); + } + + final int readableBytes = bytes.readableBytes(); + + storageManager.addBytesToLargeMessage(file, getMessageID(), bytes); + + bodySize += readableBytes; + } + @Override public synchronized int getEncodeSize() { return getHeadersAndPropertiesEncodeSize(); @@ -488,22 +499,6 @@ public int encode(final ByteBuffer bufferRead) throws ActiveMQException { } } - @Override - public int encode(final ActiveMQBuffer bufferOut, final int size) throws ActiveMQException { - // This could maybe be optimized (maybe reading directly into bufferOut) - ByteBuffer bufferRead = ByteBuffer.allocate(size); - - int bytesRead = encode(bufferRead); - - bufferRead.flip(); - - if (bytesRead > 0) { - bufferOut.writeBytes(bufferRead.array(), 0, bytesRead); - } - - return bytesRead; - } - /* (non-Javadoc) * @see org.apache.activemq.artemis.core.message.LargeBodyEncoder#getLargeBodySize() */ @@ -512,4 +507,6 @@ public long getLargeBodySize() throws ActiveMQException { return getBodySize(); } } + + } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageLargeServerMessage.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageLargeServerMessage.java index 62807462813..d709e28e2fa 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageLargeServerMessage.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageLargeServerMessage.java @@ -48,11 +48,31 @@ public synchronized void addBytes(final byte[] bytes) { buffer.writeBytes(bytes); } + @Override + public synchronized void addBytes(ActiveMQBuffer bytes) { + final int readableBytes = bytes.readableBytes(); + if (buffer == null) { + buffer = Unpooled.buffer(readableBytes); + } + + // expand the buffer + buffer.ensureWritable(readableBytes); + assert buffer.hasArray(); + final int writerIndex = buffer.writerIndex(); + bytes.readBytes(buffer.array(), buffer.arrayOffset() + writerIndex, readableBytes); + buffer.writerIndex(writerIndex + readableBytes); + } + @Override public synchronized ActiveMQBuffer getReadOnlyBodyBuffer() { return new ChannelBufferWrapper(buffer.slice(0, buffer.writerIndex()).asReadOnly()); } + @Override + public synchronized int getBodyBufferSize() { + return buffer.writerIndex(); + } + @Override public void deleteFile() throws Exception { // nothing to be done here.. we don really have a file on this Storage diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/LargeServerMessage.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/LargeServerMessage.java index a80e3692b33..69592bb70df 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/LargeServerMessage.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/LargeServerMessage.java @@ -16,6 +16,7 @@ */ package org.apache.activemq.artemis.core.server; +import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ICoreMessage; import org.apache.activemq.artemis.core.io.SequentialFile; @@ -28,6 +29,8 @@ public interface LargeServerMessage extends ReplicatedLargeMessage, ICoreMessage @Override void addBytes(byte[] bytes) throws Exception; + void addBytes(ActiveMQBuffer bytes) throws Exception; + /** * We have to copy the large message content in case of DLQ and paged messages * For that we need to pre-mark the LargeMessage with a flag when it is paged diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java index ddd0b717200..470aeb658e5 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerConsumerImpl.java @@ -17,6 +17,7 @@ package org.apache.activemq.artemis.core.server.impl; import java.math.BigDecimal; +import java.nio.ByteBuffer; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -27,8 +28,6 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.apache.activemq.artemis.api.core.ActiveMQBuffer; -import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException; import org.apache.activemq.artemis.api.core.Message; @@ -1182,12 +1181,29 @@ private final class LargeMessageDeliverer { private LargeBodyEncoder context; + private ByteBuffer chunkBytes; + private LargeMessageDeliverer(final LargeServerMessage message, final MessageReference ref) throws Exception { largeMessage = message; largeMessage.incrementDelayDeletionCount(); this.ref = ref; + + this.chunkBytes = null; + } + + private ByteBuffer acquireHeapBodyBuffer(int requiredCapacity) { + if (this.chunkBytes == null || this.chunkBytes.capacity() != requiredCapacity) { + this.chunkBytes = ByteBuffer.allocate(requiredCapacity); + } else { + this.chunkBytes.clear(); + } + return this.chunkBytes; + } + + private void releaseHeapBodyBuffer() { + this.chunkBytes = null; } public boolean deliver() throws Exception { @@ -1207,7 +1223,7 @@ public boolean deliver() throws Exception { logger.trace(this + "::FlowControl::delivery largeMessage interrupting as there are no more credits, available=" + availableCredits); } - + releaseHeapBodyBuffer(); return false; } @@ -1223,7 +1239,11 @@ public boolean deliver() throws Exception { int packetSize = callback.sendLargeMessage(ref, currentLargeMessage, ServerConsumerImpl.this, context.getLargeBodySize(), ref.getDeliveryCount()); if (availableCredits != null) { - availableCredits.addAndGet(-packetSize); + final int credits = availableCredits.addAndGet(-packetSize); + + if (credits <= 0) { + releaseHeapBodyBuffer(); + } if (logger.isTraceEnabled()) { logger.trace(this + "::FlowControl::" + @@ -1246,32 +1266,38 @@ public boolean deliver() throws Exception { logger.trace(this + "::FlowControl::deliverLargeMessage Leaving loop of send LargeMessage because of credits, available=" + availableCredits); } - + releaseHeapBodyBuffer(); return false; } - int localChunkLen = 0; + final int localChunkLen = (int) Math.min(sizePendingLargeMessage - positionPendingLargeMessage, minLargeMessageSize); - localChunkLen = (int) Math.min(sizePendingLargeMessage - positionPendingLargeMessage, minLargeMessageSize); + final ByteBuffer bodyBuffer = acquireHeapBodyBuffer(localChunkLen); - ActiveMQBuffer bodyBuffer = ActiveMQBuffers.fixedBuffer(localChunkLen); + assert bodyBuffer.remaining() == localChunkLen; - context.encode(bodyBuffer, localChunkLen); + final int readBytes = context.encode(bodyBuffer); - byte[] body; + assert readBytes == localChunkLen; - if (bodyBuffer.toByteBuffer().hasArray()) { - body = bodyBuffer.toByteBuffer().array(); - } else { - body = new byte[0]; - } + final byte[] body = bodyBuffer.array(); + + assert body.length == readBytes; + + //It is possible to recycle the same heap body buffer because it won't be cached by sendLargeMessageContinuation + //given that requiresResponse is false: ChannelImpl::send will use the resend cache only if + //resendCache != null && packet.isRequiresConfirmations() int packetSize = callback.sendLargeMessageContinuation(ServerConsumerImpl.this, body, positionPendingLargeMessage + localChunkLen < sizePendingLargeMessage, false); int chunkLen = body.length; if (availableCredits != null) { - availableCredits.addAndGet(-packetSize); + final int credits = availableCredits.addAndGet(-packetSize); + + if (credits <= 0) { + releaseHeapBodyBuffer(); + } if (logger.isTraceEnabled()) { logger.trace(this + "::FlowControl::largeMessage deliver continuation, packetSize=" + @@ -1304,6 +1330,8 @@ public boolean deliver() throws Exception { public void finish() throws Exception { synchronized (lock) { + releaseHeapBodyBuffer(); + if (largeMessage == null) { // handleClose could be calling close while handle is also calling finish. // As a result one of them could get here after the largeMessage is already gone. diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java index d868a2fb913..3354d3d7b0f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java @@ -1398,13 +1398,11 @@ public RoutingStatus send(final Message message, private LargeServerMessage messageToLargeMessage(Message message) throws Exception { ICoreMessage coreMessage = message.toCore(); LargeServerMessage lsm = getStorageManager().createLargeMessage(storageManager.generateID(), coreMessage); - ActiveMQBuffer buffer = coreMessage.getReadOnlyBodyBuffer(); - byte[] body = new byte[buffer.readableBytes()]; - buffer.readBytes(body); - lsm.addBytes(body); + final int readableBytes = buffer.readableBytes(); + lsm.addBytes(buffer); lsm.releaseResources(); - lsm.putLongProperty(Message.HDR_LARGE_BODY_SIZE, body.length); + lsm.putLongProperty(Message.HDR_LARGE_BODY_SIZE, readableBytes); return lsm; } From 3b34127bb3ce6720ff794551a2877ba9794f0960 Mon Sep 17 00:00:00 2001 From: andytaylor Date: Wed, 5 Sep 2018 08:41:06 +0100 Subject: [PATCH 206/207] ARTEMIS-2075 - allow Extra backups to try to replicate more than once https://issues.apache.org/jira/browse/ARTEMIS-2075 --- .../config/ActiveMQDefaultConfiguration.java | 6 +++ .../core/config/ConfigurationUtils.java | 4 +- .../config/ha/ReplicaPolicyConfiguration.java | 10 ++++ .../ha/ReplicatedPolicyConfiguration.java | 10 ++++ .../impl/FileConfigurationParser.java | 4 ++ .../core/server/cluster/ha/ReplicaPolicy.java | 15 +++++- .../server/cluster/ha/ReplicatedPolicy.java | 13 ++++- .../AnyLiveNodeLocatorForReplication.java | 9 +++- .../NamedLiveNodeLocatorForReplication.java | 13 +++-- .../impl/SharedNothingBackupActivation.java | 2 +- .../schema/artemis-configuration.xsd | 14 +++++ .../impl/HAPolicyConfigurationTest.java | 2 + .../resources/replica-hapolicy-config.xml | 1 + .../resources/replicated-hapolicy-config.xml | 1 + .../failover/ReplicatedFailoverTest.java | 54 +++++++++++++++++++ 15 files changed, 147 insertions(+), 11 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java index 45397f386c9..bd7ce5167ff 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java @@ -504,6 +504,8 @@ public static String getDefaultHapolicyBackupStrategy() { //how long we wait for vote result, 30 secs private static int DEFAULT_QUORUM_VOTE_WAIT = 30; + private static long DEFAULT_RETRY_REPLICATION_WAIT = 2000; + public static int DEFAULT_QUORUM_SIZE = -1; public static final boolean DEFAULT_ANALYZE_CRITICAL = true; @@ -1384,4 +1386,8 @@ public static long getDefaultVoteRetryWait() { public static int getDefaultQuorumVoteWait() { return DEFAULT_QUORUM_VOTE_WAIT; } + + public static long getDefaultRetryReplicationWait() { + return DEFAULT_RETRY_REPLICATION_WAIT; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java index 6d4661b4626..a3149473a62 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ConfigurationUtils.java @@ -73,11 +73,11 @@ public static HAPolicy getHAPolicy(HAPolicyConfiguration conf, } case REPLICATED: { ReplicatedPolicyConfiguration pc = (ReplicatedPolicyConfiguration) conf; - return new ReplicatedPolicy(pc.isCheckForLiveServer(), pc.getGroupName(), pc.getClusterName(), pc.getInitialReplicationSyncTimeout(), server.getNetworkHealthCheck(), pc.getVoteOnReplicationFailure(), pc.getQuorumSize(), pc.getVoteRetries(), pc.getVoteRetryWait(), pc.getQuorumVoteWait()); + return new ReplicatedPolicy(pc.isCheckForLiveServer(), pc.getGroupName(), pc.getClusterName(), pc.getInitialReplicationSyncTimeout(), server.getNetworkHealthCheck(), pc.getVoteOnReplicationFailure(), pc.getQuorumSize(), pc.getVoteRetries(), pc.getVoteRetryWait(), pc.getQuorumVoteWait(), pc.getRetryReplicationWait()); } case REPLICA: { ReplicaPolicyConfiguration pc = (ReplicaPolicyConfiguration) conf; - return new ReplicaPolicy(pc.getClusterName(), pc.getMaxSavedReplicatedJournalsSize(), pc.getGroupName(), pc.isRestartBackup(), pc.isAllowFailBack(), pc.getInitialReplicationSyncTimeout(), getScaleDownPolicy(pc.getScaleDownConfiguration()), server.getNetworkHealthCheck(), pc.getVoteOnReplicationFailure(), pc.getQuorumSize(), pc.getVoteRetries(), pc.getVoteRetryWait(), pc.getQuorumVoteWait()); + return new ReplicaPolicy(pc.getClusterName(), pc.getMaxSavedReplicatedJournalsSize(), pc.getGroupName(), pc.isRestartBackup(), pc.isAllowFailBack(), pc.getInitialReplicationSyncTimeout(), getScaleDownPolicy(pc.getScaleDownConfiguration()), server.getNetworkHealthCheck(), pc.getVoteOnReplicationFailure(), pc.getQuorumSize(), pc.getVoteRetries(), pc.getVoteRetryWait(), pc.getQuorumVoteWait(), pc.getRetryReplicationWait()); } case SHARED_STORE_MASTER: { SharedStoreMasterPolicyConfiguration pc = (SharedStoreMasterPolicyConfiguration) conf; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java index e02bb5603a3..4bf24a7263f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicaPolicyConfiguration.java @@ -49,6 +49,8 @@ public class ReplicaPolicyConfiguration implements HAPolicyConfiguration { private int quorumVoteWait = ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(); + private long retryReplicationWait = ActiveMQDefaultConfiguration.getDefaultRetryReplicationWait(); + public ReplicaPolicyConfiguration() { } @@ -170,4 +172,12 @@ public int getQuorumVoteWait() { public void setQuorumVoteWait(int quorumVoteWait) { this.quorumVoteWait = quorumVoteWait; } + + public long getRetryReplicationWait() { + return retryReplicationWait; + } + + public void setRetryReplicationWait(long retryReplicationWait) { + this.retryReplicationWait = retryReplicationWait; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java index 3acad777673..162f09559c0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/ha/ReplicatedPolicyConfiguration.java @@ -39,6 +39,8 @@ public class ReplicatedPolicyConfiguration implements HAPolicyConfiguration { private int quorumVoteWait = ActiveMQDefaultConfiguration.getDefaultQuorumVoteWait(); + private Long retryReplicationWait = ActiveMQDefaultConfiguration.getDefaultRetryReplicationWait(); + public ReplicatedPolicyConfiguration() { } @@ -129,4 +131,12 @@ public ReplicatedPolicyConfiguration setQuorumVoteWait(int quorumVoteWait) { this.quorumVoteWait = quorumVoteWait; return this; } + + public void setRetryReplicationWait(Long retryReplicationWait) { + this.retryReplicationWait = retryReplicationWait; + } + + public Long getRetryReplicationWait() { + return retryReplicationWait; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java index b1b2c0ec084..9bc292b8c30 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java @@ -1336,6 +1336,8 @@ private ReplicatedPolicyConfiguration createReplicatedHaPolicy(Element policyNod configuration.setVoteRetryWait(getLong(policyNode, "vote-retry-wait", configuration.getVoteRetryWait(), Validators.GT_ZERO)); + configuration.setRetryReplicationWait(getLong(policyNode, "retry-replication-wait", configuration.getVoteRetryWait(), Validators.GT_ZERO)); + configuration.setQuorumSize(getInteger(policyNode, "quorum-size", configuration.getQuorumSize(), Validators.MINUS_ONE_OR_GT_ZERO)); return configuration; @@ -1367,6 +1369,8 @@ private ReplicaPolicyConfiguration createReplicaHaPolicy(Element policyNode) { configuration.setVoteRetryWait(getLong(policyNode, "vote-retry-wait", configuration.getVoteRetryWait(), Validators.GT_ZERO)); + configuration.setRetryReplicationWait(getLong(policyNode, "retry-replication-wait", configuration.getVoteRetryWait(), Validators.GT_ZERO)); + configuration.setQuorumSize(getInteger(policyNode, "quorum-size", configuration.getQuorumSize(), Validators.MINUS_ONE_OR_GT_ZERO)); return configuration; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicaPolicy.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicaPolicy.java index 7a4b06bc042..36e65f02e9c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicaPolicy.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicaPolicy.java @@ -59,6 +59,8 @@ public class ReplicaPolicy extends BackupPolicy { private final int quorumVoteWait; + private long retryReplicationWait; + public ReplicaPolicy(final NetworkHealthCheck networkHealthCheck, int quorumVoteWait) { this.networkHealthCheck = networkHealthCheck; this.quorumVoteWait = quorumVoteWait; @@ -84,7 +86,8 @@ public ReplicaPolicy(String clusterName, int quorumSize, int voteRetries, long voteRetryWait, - int quorumVoteWait) { + int quorumVoteWait, + long retryReplicationWait) { this.clusterName = clusterName; this.maxSavedReplicatedJournalsSize = maxSavedReplicatedJournalsSize; this.groupName = groupName; @@ -94,10 +97,12 @@ public ReplicaPolicy(String clusterName, this.quorumSize = quorumSize; this.voteRetries = voteRetries; this.voteRetryWait = voteRetryWait; + this.retryReplicationWait = retryReplicationWait; this.scaleDownPolicy = scaleDownPolicy; this.networkHealthCheck = networkHealthCheck; this.voteOnReplicationFailure = voteOnReplicationFailure; this.quorumVoteWait = quorumVoteWait; + this.retryReplicationWait = retryReplicationWait; } public ReplicaPolicy(String clusterName, @@ -247,4 +252,12 @@ public long getVoteRetryWait() { public int getQuorumVoteWait() { return quorumVoteWait; } + + public long getRetryReplicationWait() { + return retryReplicationWait; + } + + public void setretryReplicationWait(long retryReplicationWait) { + this.retryReplicationWait = retryReplicationWait; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicatedPolicy.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicatedPolicy.java index e170429ac49..99b98fec529 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicatedPolicy.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/cluster/ha/ReplicatedPolicy.java @@ -54,6 +54,8 @@ public class ReplicatedPolicy implements HAPolicy { private long voteRetryWait; + private long retryReplicationWait; + /* * this are only used as the policy when the server is started as a live after a failover * */ @@ -78,7 +80,8 @@ public ReplicatedPolicy(boolean checkForLiveServer, int quorumSize, int voteRetries, long voteRetryWait, - int quorumVoteWait) { + int quorumVoteWait, + long retryReplicationWait) { this.checkForLiveServer = checkForLiveServer; this.groupName = groupName; this.clusterName = clusterName; @@ -89,6 +92,7 @@ public ReplicatedPolicy(boolean checkForLiveServer, this.voteRetries = voteRetries; this.voteRetryWait = voteRetryWait; this.quorumVoteWait = quorumVoteWait; + this.retryReplicationWait = retryReplicationWait; } public ReplicatedPolicy(boolean checkForLiveServer, @@ -159,6 +163,7 @@ public ReplicaPolicy getReplicaPolicy() { replicaPolicy.setVoteOnReplicationFailure(voteOnReplicationFailure); replicaPolicy.setVoteRetries(voteRetries); replicaPolicy.setVoteRetryWait(voteRetryWait); + replicaPolicy.setretryReplicationWait(retryReplicationWait); if (clusterName != null && clusterName.length() > 0) { replicaPolicy.setClusterName(clusterName); } @@ -241,4 +246,8 @@ public void setQuorumSize(int quorumSize) { public int getQuorumVoteWait() { return quorumVoteWait; } -} \ No newline at end of file + + public long getRetryReplicationWait() { + return retryReplicationWait; + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/AnyLiveNodeLocatorForReplication.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/AnyLiveNodeLocatorForReplication.java index a446b2e7012..015339aafee 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/AnyLiveNodeLocatorForReplication.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/AnyLiveNodeLocatorForReplication.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -40,14 +41,16 @@ public class AnyLiveNodeLocatorForReplication extends LiveNodeLocator { private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private final ActiveMQServerImpl server; + private final long retryReplicationWait; Map> untriedConnectors = new HashMap<>(); Map> triedConnectors = new HashMap<>(); private String nodeID; - public AnyLiveNodeLocatorForReplication(SharedNothingBackupQuorum backupQuorum, ActiveMQServerImpl server) { + public AnyLiveNodeLocatorForReplication(SharedNothingBackupQuorum backupQuorum, ActiveMQServerImpl server, long retryReplicationWait) { super(backupQuorum); this.server = server; + this.retryReplicationWait = retryReplicationWait; } @Override @@ -66,7 +69,9 @@ public void locateNode(long timeout) throws ActiveMQException { ConcurrentUtil.await(condition, timeout); } else { while (untriedConnectors.isEmpty()) { - condition.await(); + condition.await(retryReplicationWait, TimeUnit.MILLISECONDS); + untriedConnectors.putAll(triedConnectors); + triedConnectors.clear(); } } } catch (InterruptedException e) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/NamedLiveNodeLocatorForReplication.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/NamedLiveNodeLocatorForReplication.java index a3c50fbe120..624808d1f0d 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/NamedLiveNodeLocatorForReplication.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/NamedLiveNodeLocatorForReplication.java @@ -16,8 +16,10 @@ */ package org.apache.activemq.artemis.core.server.impl; +import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -40,13 +42,16 @@ public class NamedLiveNodeLocatorForReplication extends LiveNodeLocator { private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private final String backupGroupName; + private final long retryReplicationWait; private Queue> liveConfigurations = new LinkedList<>(); + private ArrayList> triedConfigurations = new ArrayList<>(); private String nodeID; - public NamedLiveNodeLocatorForReplication(String backupGroupName, SharedNothingBackupQuorum quorumManager) { + public NamedLiveNodeLocatorForReplication(String backupGroupName, SharedNothingBackupQuorum quorumManager, long retryReplicationWait) { super(quorumManager); this.backupGroupName = backupGroupName; + this.retryReplicationWait = retryReplicationWait; } @Override @@ -64,7 +69,9 @@ public void locateNode(long timeout) throws ActiveMQException { ConcurrentUtil.await(condition, timeout); } else { while (liveConfigurations.size() == 0) { - condition.await(); + condition.await(retryReplicationWait, TimeUnit.MILLISECONDS); + liveConfigurations.addAll(triedConfigurations); + triedConfigurations.clear(); } } } catch (InterruptedException e) { @@ -112,7 +119,7 @@ public Pair getLiveConfiguration public void notifyRegistrationFailed(boolean alreadyReplicating) { try { lock.lock(); - liveConfigurations.poll(); + triedConfigurations.add(liveConfigurations.poll()); super.notifyRegistrationFailed(alreadyReplicating); } finally { lock.unlock(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingBackupActivation.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingBackupActivation.java index 863923de067..d1f0a05cb83 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingBackupActivation.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/SharedNothingBackupActivation.java @@ -142,7 +142,7 @@ public void run() { TopologyMember member = (TopologyMember) activationParams.get(ActivationParams.REPLICATION_ENDPOINT); nodeLocator = new NamedNodeIdNodeLocator(member.getNodeId(), new Pair<>(member.getLive(), member.getBackup())); } else { - nodeLocator = replicaPolicy.getGroupName() == null ? new AnyLiveNodeLocatorForReplication(backupQuorum, activeMQServer) : new NamedLiveNodeLocatorForReplication(replicaPolicy.getGroupName(), backupQuorum); + nodeLocator = replicaPolicy.getGroupName() == null ? new AnyLiveNodeLocatorForReplication(backupQuorum, activeMQServer, replicaPolicy.getRetryReplicationWait()) : new NamedLiveNodeLocatorForReplication(replicaPolicy.getGroupName(), backupQuorum, replicaPolicy.getRetryReplicationWait()); } ClusterController clusterController = activeMQServer.getClusterManager().getClusterController(); clusterController.addClusterTopologyListenerForReplication(nodeLocator); diff --git a/artemis-server/src/main/resources/schema/artemis-configuration.xsd b/artemis-server/src/main/resources/schema/artemis-configuration.xsd index 04e29316aac..685dec4dfbb 100644 --- a/artemis-server/src/main/resources/schema/artemis-configuration.xsd +++ b/artemis-server/src/main/resources/schema/artemis-configuration.xsd @@ -2277,6 +2277,13 @@ + + + + If we start as a replica how long to wait (in milliseconds) before trying to replicate again after failing to find a replica + + + @@ -2380,6 +2387,13 @@ + + + + How long to wait (in milliseconds) before trying to replicate again after failing to find a replica + + + diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/HAPolicyConfigurationTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/HAPolicyConfigurationTest.java index 2a236134a29..fd9b5238b5c 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/HAPolicyConfigurationTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/HAPolicyConfigurationTest.java @@ -139,6 +139,7 @@ public void ReplicatedTest() throws Exception { assertTrue(replicatedPolicy.isCheckForLiveServer()); assertEquals(replicatedPolicy.getClusterName(), "abcdefg"); assertEquals(replicatedPolicy.getInitialReplicationSyncTimeout(), 9876); + assertEquals(replicatedPolicy.getRetryReplicationWait(), 12345); } finally { server.stop(); } @@ -161,6 +162,7 @@ public void ReplicaTest() throws Exception { assertFalse(replicaPolicy.isRestartBackup()); assertTrue(replicaPolicy.isAllowFailback()); assertEquals(replicaPolicy.getInitialReplicationSyncTimeout(), 9876); + assertEquals(replicaPolicy.getRetryReplicationWait(), 12345); ScaleDownPolicy scaleDownPolicy = replicaPolicy.getScaleDownPolicy(); assertNotNull(scaleDownPolicy); assertEquals(scaleDownPolicy.getGroupName(), "boo!"); diff --git a/artemis-server/src/test/resources/replica-hapolicy-config.xml b/artemis-server/src/test/resources/replica-hapolicy-config.xml index 3b2c3baa695..2ed37242052 100644 --- a/artemis-server/src/test/resources/replica-hapolicy-config.xml +++ b/artemis-server/src/test/resources/replica-hapolicy-config.xml @@ -31,6 +31,7 @@ false true 9876 + 12345 boo! diff --git a/artemis-server/src/test/resources/replicated-hapolicy-config.xml b/artemis-server/src/test/resources/replicated-hapolicy-config.xml index fb2a60243c6..22274790dd0 100644 --- a/artemis-server/src/test/resources/replicated-hapolicy-config.xml +++ b/artemis-server/src/test/resources/replicated-hapolicy-config.xml @@ -27,6 +27,7 @@ true abcdefg 9876 + 12345 diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/ReplicatedFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/ReplicatedFailoverTest.java index 01a3ba64b4a..c6ee8b4a737 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/ReplicatedFailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/ReplicatedFailoverTest.java @@ -25,8 +25,10 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; +import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.component.WebServerComponent; +import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.ha.ReplicaPolicyConfiguration; import org.apache.activemq.artemis.core.config.ha.ReplicatedPolicyConfiguration; import org.apache.activemq.artemis.core.server.ActiveMQServer; @@ -35,6 +37,7 @@ import org.apache.activemq.artemis.dto.AppDTO; import org.apache.activemq.artemis.dto.WebServerDTO; import org.apache.activemq.artemis.junit.Wait; +import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -45,11 +48,13 @@ public class ReplicatedFailoverTest extends FailoverTest { boolean isReplicatedFailbackTest = false; + boolean isExtraBackupGroupNameReplicates = false; @Rule public TestRule watcher = new TestWatcher() { @Override protected void starting(Description description) { isReplicatedFailbackTest = description.getMethodName().equals("testReplicatedFailback") || description.getMethodName().equals("testLoop"); + isExtraBackupGroupNameReplicates = description.getMethodName().equals("testExtraBackupGroupNameReplicates"); } }; @@ -73,6 +78,49 @@ private void waitForSync(ActiveMQServer server) throws Exception { Wait.waitFor(server::isReplicaSync); } + @Test + public void testExtraBackupReplicates() throws Exception { + Configuration secondBackupConfig = backupConfig.copy(); + TransportConfiguration tc = secondBackupConfig.getAcceptorConfigurations().iterator().next(); + TestableServer secondBackupServer = createTestableServer(secondBackupConfig); + tc.getParams().put("serverId", "2"); + secondBackupConfig.setBindingsDirectory(getBindingsDir(1, true)).setJournalDirectory(getJournalDir(1, true)).setPagingDirectory(getPageDir(1, true)).setLargeMessagesDirectory(getLargeMessagesDir(1, true)).setSecurityEnabled(false); + + waitForRemoteBackupSynchronization(backupServer.getServer()); + + secondBackupServer.start(); + Thread.sleep(5000); + backupServer.stop(); + waitForSync(secondBackupServer.getServer()); + waitForRemoteBackupSynchronization(secondBackupServer.getServer()); + + } + + @Test + public void testExtraBackupGroupNameReplicates() throws Exception { + ReplicaPolicyConfiguration backupReplicaPolicyConfiguration = (ReplicaPolicyConfiguration) backupServer.getServer().getConfiguration().getHAPolicyConfiguration(); + backupReplicaPolicyConfiguration.setGroupName("foo"); + + ReplicatedPolicyConfiguration replicatedPolicyConfiguration = (ReplicatedPolicyConfiguration) liveServer.getServer().getConfiguration().getHAPolicyConfiguration(); + replicatedPolicyConfiguration.setGroupName("foo"); + + Configuration secondBackupConfig = backupConfig.copy(); + TransportConfiguration tc = secondBackupConfig.getAcceptorConfigurations().iterator().next(); + TestableServer secondBackupServer = createTestableServer(secondBackupConfig); + tc.getParams().put("serverId", "2"); + secondBackupConfig.setBindingsDirectory(getBindingsDir(1, true)).setJournalDirectory(getJournalDir(1, true)).setPagingDirectory(getPageDir(1, true)).setLargeMessagesDirectory(getLargeMessagesDir(1, true)).setSecurityEnabled(false); + ReplicaPolicyConfiguration replicaPolicyConfiguration = (ReplicaPolicyConfiguration) secondBackupConfig.getHAPolicyConfiguration(); + replicaPolicyConfiguration.setGroupName("foo"); + waitForRemoteBackupSynchronization(backupServer.getServer()); + + secondBackupServer.start(); + Thread.sleep(5000); + backupServer.stop(); + waitForSync(secondBackupServer.getServer()); + waitForRemoteBackupSynchronization(secondBackupServer.getServer()); + + } + @Test(timeout = 120000) /* * default maxSavedReplicatedJournalsSize is 2, this means the backup will fall back to replicated only twice, after this @@ -213,6 +261,12 @@ protected void setupHAPolicyConfiguration() { } else { super.setupHAPolicyConfiguration(); } + + if (isExtraBackupGroupNameReplicates) { + ((ReplicatedPolicyConfiguration) liveConfig.getHAPolicyConfiguration()).setGroupName("foo"); + ((ReplicaPolicyConfiguration) backupConfig.getHAPolicyConfiguration()).setGroupName("foo"); + + } } @Override From ee723bd0e20cea8174ba4c90891ca3161c041686 Mon Sep 17 00:00:00 2001 From: Romain Pelisse Date: Thu, 13 Sep 2018 17:02:58 +0200 Subject: [PATCH 207/207] JBEAP-14106 - Improve validation of MDB activation config properties values --- .../activemq/artemis/ra/inflow/ActiveMQActivation.java | 2 +- .../artemis/ra/inflow/ActiveMQActivationSpec.java | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java index b0f0afff8e0..55c5d39b463 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java @@ -556,7 +556,7 @@ protected void setupDestination() throws Exception { calculatedDestinationName = spec.getQueuePrefix() + calculatedDestinationName; } - logger.debug("Unable to retrieve " + destinationName + " from JNDI. Creating a new " + destinationType.getName() + " named " + calculatedDestinationName + " to be used by the MDB."); + logger.warn("Unable to retrieve " + destinationName + " from JNDI. Creating a new " + destinationType.getName() + " named " + calculatedDestinationName + " to be used by the MDB."); // If there is no binding on naming, we will just create a new instance if (isTopic) { diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivationSpec.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivationSpec.java index 1252486852d..105408a1071 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivationSpec.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivationSpec.java @@ -406,7 +406,9 @@ public void setAcknowledgeMode(final String value) { } else if ("AUTO_ACKNOWLEDGE".equalsIgnoreCase(value) || "Auto-acknowledge".equalsIgnoreCase(value)) { acknowledgeMode = Session.AUTO_ACKNOWLEDGE; } else { - throw new IllegalArgumentException("Unsupported acknowledgement mode " + value); + final String message = "Unsupported acknowledgement mode " + value; + logger.warn(message); + throw new IllegalArgumentException(message); } } @@ -603,7 +605,11 @@ public void setMaxSession(final Integer value) { logger.trace("setMaxSession(" + value + ")"); } - maxSession = value; + if ( value < 0 ) { + logger.warn("Invalid number of session (negative):" + value +", defaulting to 1."); + maxSession = 1; + } else + maxSession = value; } /**