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 d3f4473215..e981444939 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 @@ -1903,5 +1903,8 @@ public interface ActiveMQServerControl { @Parameter(name = "password", desc = "User's password") String password, @Parameter(name = "roles", desc = "User's role (comma separated)") String roles, @Parameter(name = "plaintext", desc = "whether or not to store the password in plaintext or hash it") boolean plaintext) throws Exception; + + @Operation(desc = "forces the broker to reload its configuration file", impact = MBeanOperationInfo.ACTION) + void reloadConfigurationFile() throws Exception; } 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 46129f0114..8306a6dea8 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 @@ -4385,5 +4385,10 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active String path = configurationUrl.getPath(); return new PropertiesLoginModuleConfigurator(getSecurityDomain(), path.substring(0, path.lastIndexOf("/"))); } + + @Override + public void reloadConfigurationFile() throws Exception { + server.reloadConfigurationFile(); + } } 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 9580adccea..2eb7cd095f 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 @@ -54,7 +54,7 @@ import org.apache.activemq.artemis.core.server.impl.AddressInfo; 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.metrics.MetricsManager; -import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerFederationPlugin; +import org.apache.activemq.artemis.core.server.mirror.MirrorController; import org.apache.activemq.artemis.core.server.plugin.ActiveMQPluginRunnable; import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerAddressPlugin; import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBasePlugin; @@ -63,12 +63,12 @@ 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.ActiveMQServerFederationPlugin; 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.ActiveMQServerResourcePlugin; import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerSessionPlugin; import org.apache.activemq.artemis.core.server.reload.ReloadManager; -import org.apache.activemq.artemis.core.server.mirror.MirrorController; import org.apache.activemq.artemis.core.settings.HierarchicalRepository; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.core.transaction.ResourceManager; @@ -929,4 +929,6 @@ public interface ActiveMQServer extends ServiceComponent { String getInternalNamingPrefix(); double getDiskStoreUsage(); + + void reloadConfigurationFile() throws Exception; } 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 5c50982f05..baab78b246 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 @@ -157,6 +157,7 @@ import org.apache.activemq.artemis.core.server.impl.jdbc.JdbcNodeManager; 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.metrics.MetricsManager; +import org.apache.activemq.artemis.core.server.mirror.MirrorController; import org.apache.activemq.artemis.core.server.plugin.ActiveMQPluginRunnable; import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerAddressPlugin; import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerBasePlugin; @@ -170,10 +171,8 @@ import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerMessagePlugi import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerQueuePlugin; import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerResourcePlugin; 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; -import org.apache.activemq.artemis.core.server.mirror.MirrorController; import org.apache.activemq.artemis.core.server.transformer.Transformer; import org.apache.activemq.artemis.core.settings.HierarchicalRepository; import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy; @@ -3103,7 +3102,7 @@ public class ActiveMQServerImpl implements ActiveMQServer { this.reloadManager = new ReloadManagerImpl(getScheduledPool(), executorFactory.getExecutor(), configuration.getConfigurationFileRefreshPeriod()); if (configuration.getConfigurationUrl() != null && getScheduledPool() != null) { - reloadManager.addCallback(configuration.getConfigurationUrl(), new ConfigurationFileReloader()); + reloadManager.addCallback(configuration.getConfigurationUrl(), uri -> reloadConfigurationFile(uri)); } if (System.getProperty("logging.configuration") != null) { @@ -4190,22 +4189,23 @@ public class ActiveMQServerImpl implements ActiveMQServer { } - private final class ConfigurationFileReloader implements ReloadCallback { + @Override + public void reloadConfigurationFile() throws Exception { + reloadConfigurationFile(configuration.getConfigurationUrl()); + } - @Override - public void reload(URL uri) throws Exception { - Configuration config = new FileConfigurationParser().parseMainConfig(uri.openStream()); - LegacyJMSConfiguration legacyJMSConfiguration = new LegacyJMSConfiguration(config); - legacyJMSConfiguration.parseConfiguration(uri.openStream()); - configuration.setSecurityRoles(config.getSecurityRoles()); - configuration.setAddressesSettings(config.getAddressesSettings()); - configuration.setDivertConfigurations(config.getDivertConfigurations()); - configuration.setAddressConfigurations(config.getAddressConfigurations()); - configuration.setQueueConfigs(config.getQueueConfigs()); - configurationReloadDeployed.set(false); - if (isActive()) { - deployReloadableConfigFromConfiguration(); - } + private void reloadConfigurationFile(URL uri) throws Exception { + Configuration config = new FileConfigurationParser().parseMainConfig(uri.openStream()); + LegacyJMSConfiguration legacyJMSConfiguration = new LegacyJMSConfiguration(config); + legacyJMSConfiguration.parseConfiguration(uri.openStream()); + configuration.setSecurityRoles(config.getSecurityRoles()); + configuration.setAddressesSettings(config.getAddressesSettings()); + configuration.setDivertConfigurations(config.getDivertConfigurations()); + configuration.setAddressConfigurations(config.getAddressConfigurations()); + configuration.setQueueConfigs(config.getQueueConfigs()); + configurationReloadDeployed.set(false); + if (isActive()) { + deployReloadableConfigFromConfiguration(); } } diff --git a/docs/user-manual/en/configuration-index.md b/docs/user-manual/en/configuration-index.md index d245220673..4027a5037a 100644 --- a/docs/user-manual/en/configuration-index.md +++ b/docs/user-manual/en/configuration-index.md @@ -50,10 +50,13 @@ For further information on XInclude see: 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. +reloaded. For example, if `broker.xml` is including `my-address-settings.xml` and `my-address-settings.xml` is modified +those changes won't be reloaded automatically. To force a reload in this situation there are 2 main options: + +1. Use the `reloadConfiguration` management operation on the `ActiveMQServerControl`. +2. Update the timestamp on `broker.xml` using something like the [touch](https://en.wikipedia.org/wiki/Touch_%28Unix%29) + command. The next time the broker inspects `broker.xml` for automatic reload it will see the updated timestamp and + trigger a reload of `broker.xml` and all its included files. ### System properties diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ProgrammaticRedeployTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ProgrammaticRedeployTest.java new file mode 100644 index 0000000000..14fb9d426e --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/ProgrammaticRedeployTest.java @@ -0,0 +1,124 @@ +/** + * 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; + +import javax.jms.ConnectionFactory; +import javax.jms.JMSContext; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.postoffice.QueueBinding; +import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ; +import org.apache.activemq.artemis.core.server.impl.AddressInfo; +import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.junit.Assert; +import org.junit.Test; + +public class ProgrammaticRedeployTest extends ActiveMQTestBase { + + @Test + /** + * This is basically a copy of org.apache.activemq.artemis.tests.integration.jms.RedeployTest#testRedeployAddressQueue(). + * However, this test disables automatic configuration reload and uses the management API to do it instead. + */ + public void testRedeployAddressQueue() throws Exception { + Path brokerXML = getTestDirfile().toPath().resolve("broker.xml"); + URL url1 = ProgrammaticRedeployTest.class.getClassLoader().getResource("reload-address-queues-programmatic.xml"); + URL url2 = ProgrammaticRedeployTest.class.getClassLoader().getResource("reload-address-queues-updated-programmatic.xml"); + Files.copy(url1.openStream(), brokerXML); + + EmbeddedActiveMQ embeddedActiveMQ = new EmbeddedActiveMQ(); + embeddedActiveMQ.setConfigResourcePath(brokerXML.toUri().toString()); + embeddedActiveMQ.start(); + + ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(); + try (JMSContext jmsContext = connectionFactory.createContext()) { + jmsContext.createSharedDurableConsumer(jmsContext.createTopic("config_test_consumer_created_queues"),"mySub").receive(100); + } + + try { + 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")); + 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); + + embeddedActiveMQ.getActiveMQServer().reloadConfigurationFile(); + + //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")); + 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()); + + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_change_queue")); + Assert.assertNull(getAddressInfo(embeddedActiveMQ, "config_test_queue_removal_queue_1")); + } finally { + embeddedActiveMQ.stop(); + } + } + + private AddressInfo getAddressInfo(EmbeddedActiveMQ embeddedActiveMQ, String address) { + return embeddedActiveMQ.getActiveMQServer().getPostOffice().getAddressInfo(SimpleString.toSimpleString(address)); + } + + 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(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/management/ActiveMQServerControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java index 5d04f6c66f..882e5caf40 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 @@ -446,6 +446,11 @@ public class ActiveMQServerControlUsingCoreTest extends ActiveMQServerControlTes proxy.invokeOperation("resetUser", username, password, roles, plaintext); } + @Override + public void reloadConfigurationFile() throws Exception { + proxy.invokeOperation("reloadConfigurationFile"); + } + @Override public String getUptime() { return null; diff --git a/tests/integration-tests/src/test/resources/reload-address-queues-programmatic.xml b/tests/integration-tests/src/test/resources/reload-address-queues-programmatic.xml new file mode 100644 index 0000000000..05cce1d329 --- /dev/null +++ b/tests/integration-tests/src/test/resources/reload-address-queues-programmatic.xml @@ -0,0 +1,160 @@ + + + + + + + + 0.0.0.0 + + 9223372036854775807 + + false + + false + + + NIO + + ./data/paging + + ./data/bindings + + ./data/journal + + ./data/large-messages + + 2 + + -1 + + + 40000 + + + + + + tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576 + + + tcp://0.0.0.0:5672?protocols=AMQP + + + tcp://0.0.0.0:61613?protocols=STOMP + + + tcp://0.0.0.0:5445?protocols=HORNETQ,STOMP + + + tcp://0.0.0.0:1883?protocols=MQTT + + + + + + + + + + + + + + + + + + + + + false + DLQ + ExpiryQueue + 0 + 10Mb + 10 + BLOCK + + + + false + DLQ + ExpiryQueue + 0 + 10Mb + 10 + BLOCK + FORCE + FORCE + + + + + _ + + + +
+ + +
+
+ + + + +
+
+ + + +
+
+ + +
+
+ + + + +
+
+ + + +
+
+ + + +
+
+
+
diff --git a/tests/integration-tests/src/test/resources/reload-address-queues-updated-programmatic.xml b/tests/integration-tests/src/test/resources/reload-address-queues-updated-programmatic.xml new file mode 100644 index 0000000000..8e4575fb95 --- /dev/null +++ b/tests/integration-tests/src/test/resources/reload-address-queues-updated-programmatic.xml @@ -0,0 +1,141 @@ + + + + + + + + 0.0.0.0 + + 9223372036854775807 + + false + + false + + + NIO + + ./data/paging + + ./data/bindings + + ./data/journal + + ./data/large-messages + + 2 + + -1 + + + 40000 + + + + + + tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576 + + + tcp://0.0.0.0:5672?protocols=AMQP + + + tcp://0.0.0.0:61613?protocols=STOMP + + + tcp://0.0.0.0:5445?protocols=HORNETQ,STOMP + + + tcp://0.0.0.0:1883?protocols=MQTT + + + + + + + + + + + + + + + + + + + + + 0 + 10Mb + 10 + BLOCK + + + + false + DLQ + ExpiryQueue + 0 + 10Mb + 10 + BLOCK + FORCE + FORCE + + + + + _ + + + +
+ + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+