This is an automated email from the ASF dual-hosted git repository.
tabish pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git
The following commit(s) were added to refs/heads/main by this push:
new 0af5c0decc ARTEMIS-5763 reload config after node manager lock
acquisition to ensure startup has current config
0af5c0decc is described below
commit 0af5c0deccf9b6436cb430631e51bf4db04bdce5
Author: Gary Tully <[email protected]>
AuthorDate: Fri Nov 14 18:07:18 2025 +0000
ARTEMIS-5763 reload config after node manager lock acquisition to ensure
startup has current config
---
.../core/server/impl/ActiveMQServerImpl.java | 126 +++++++++++----------
.../core/server/impl/PrimaryOnlyActivation.java | 6 +
.../artemis/tests/util/ActiveMQTestBase.java | 3 +
.../isolated/amqp/JMSSaslExternalLDAPTest.java | 1 +
.../integration/server/ConfigurationTest.java | 73 ++++++++++++
5 files changed, 148 insertions(+), 61 deletions(-)
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 92dc97ef52..4fa48902dc 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
@@ -4642,10 +4642,10 @@ public class ActiveMQServerImpl implements
ActiveMQServer {
configuration.setAMQPConnectionConfigurations(config.getAMQPConnection());
configuration.setPurgePageFolders(config.isPurgePageFolders());
}
+ configuration.parseProperties(propertiesFileUrl);
+ updateStatus(ServerStatus.CONFIGURATION_COMPONENT,
configuration.getStatus());
configurationReloadDeployed.set(false);
if (isActive()) {
- configuration.parseProperties(propertiesFileUrl);
- updateStatus(ServerStatus.CONFIGURATION_COMPONENT,
configuration.getStatus());
deployReloadableConfigFromConfiguration();
}
}
@@ -4660,90 +4660,94 @@ public class ActiveMQServerImpl implements
ActiveMQServer {
addressSettingsRepository.swap(configuration.getAddressSettings().entrySet());
recoverStoredAddressSettings();
- ActiveMQServerLogger.LOGGER.reloadingConfiguration("diverts");
- // Filter out all active diverts
- final Set<SimpleString> divertsToRemove = postOffice.getAllBindings()
- .filter(binding -> binding instanceof DivertBinding)
- .map(Binding::getUniqueName)
- .collect(Collectors.toSet());
- // Go through the currently configured diverts
- for (DivertConfiguration divertConfig :
configuration.getDivertConfigurations()) {
- // Retain diverts still configured to exist
- divertsToRemove.remove(SimpleString.of(divertConfig.getName()));
- // Deploy newly added diverts, reconfigure existing
- final SimpleString divertName =
SimpleString.of(divertConfig.getName());
- final DivertBinding divertBinding = (DivertBinding)
postOffice.getBinding(divertName);
- if (divertBinding == null) {
- deployDivert(divertConfig);
- } else {
- if ((divertBinding.isExclusive() != divertConfig.isExclusive())
||
-
!divertBinding.getAddress().toString().equals(divertConfig.getAddress())) {
- // Diverts whose exclusivity or address has changed have to
be redeployed.
- // See the Divert interface and look for setters. Absent
setter is a hint that maybe that property is immutable.
- destroyDivert(divertName);
+ if (postOffice.isStarted()) {
+ ActiveMQServerLogger.LOGGER.reloadingConfiguration("diverts");
+
+ // Filter out all active diverts
+ final Set<SimpleString> divertsToRemove =
postOffice.getAllBindings()
+ .filter(binding -> binding instanceof DivertBinding)
+ .map(Binding::getUniqueName)
+ .collect(Collectors.toSet());
+ // Go through the currently configured diverts
+ for (DivertConfiguration divertConfig :
configuration.getDivertConfigurations()) {
+ // Retain diverts still configured to exist
+ divertsToRemove.remove(SimpleString.of(divertConfig.getName()));
+ // Deploy newly added diverts, reconfigure existing
+ final SimpleString divertName =
SimpleString.of(divertConfig.getName());
+ final DivertBinding divertBinding = (DivertBinding)
postOffice.getBinding(divertName);
+ if (divertBinding == null) {
deployDivert(divertConfig);
} else {
- // Diverts with their exclusivity and address unchanged can
be updated directly.
- updateDivert(divertConfig);
+ if ((divertBinding.isExclusive() !=
divertConfig.isExclusive()) ||
+
!divertBinding.getAddress().toString().equals(divertConfig.getAddress())) {
+ // Diverts whose exclusivity or address has changed have
to be redeployed.
+ // See the Divert interface and look for setters. Absent
setter is a hint that maybe that property is immutable.
+ destroyDivert(divertName);
+ deployDivert(divertConfig);
+ } else {
+ // Diverts with their exclusivity and address unchanged
can be updated directly.
+ updateDivert(divertConfig);
+ }
}
}
- }
- // Remove all remaining diverts
- for (final SimpleString divertName : divertsToRemove) {
- try {
- destroyDivert(divertName);
- } catch (Throwable e) {
- logger.warn("Divert {} could not be removed", divertName, e);
+ // Remove all remaining diverts
+ for (final SimpleString divertName : divertsToRemove) {
+ try {
+ destroyDivert(divertName);
+ } catch (Throwable e) {
+ logger.warn("Divert {} could not be removed", divertName, e);
+ }
}
+ recoverStoredDiverts();
}
- recoverStoredDiverts();
ActiveMQServerLogger.LOGGER.reloadingConfiguration("addresses");
undeployAddressesAndQueueNotInConfiguration(configuration);
deployAddressesFromConfiguration(configuration);
deployQueuesFromListQueueConfiguration(configuration.getQueueConfigs(), null);
- ActiveMQServerLogger.LOGGER.reloadingConfiguration("bridges");
+ if (clusterManager.isStarted()) {
+ ActiveMQServerLogger.LOGGER.reloadingConfiguration("bridges");
- for (BridgeConfiguration newBridgeConfig :
configuration.getBridgeConfigurations()) {
+ for (BridgeConfiguration newBridgeConfig :
configuration.getBridgeConfigurations()) {
- String bridgeName = newBridgeConfig.getName();
- newBridgeConfig.setParentName(bridgeName);
+ String bridgeName = newBridgeConfig.getName();
+ newBridgeConfig.setParentName(bridgeName);
- //Look for bridges with matching parentName. Only need first match
in case of concurrent bridges
- Bridge existingBridge =
clusterManager.getBridges().values().stream()
- .filter(bridge ->
bridge.getConfiguration().getParentName().equals(bridgeName))
- .findFirst()
- .orElse(null);
+ //Look for bridges with matching parentName. Only need first
match in case of concurrent bridges
+ Bridge existingBridge =
clusterManager.getBridges().values().stream()
+ .filter(bridge ->
bridge.getConfiguration().getParentName().equals(bridgeName))
+ .findFirst()
+ .orElse(null);
- if (existingBridge != null &&
existingBridge.getConfiguration().isConfigurationManaged() &&
!existingBridge.getConfiguration().equals(newBridgeConfig)) {
- // this is an existing bridge but the config changed so stop
the current bridge and deploy the new one
- destroyBridge(bridgeName);
- deployBridge(newBridgeConfig);
- } else if (existingBridge == null) {
- // this is a new bridge
- deployBridge(newBridgeConfig);
+ if (existingBridge != null &&
existingBridge.getConfiguration().isConfigurationManaged() &&
!existingBridge.getConfiguration().equals(newBridgeConfig)) {
+ // this is an existing bridge but the config changed so stop
the current bridge and deploy the new one
+ destroyBridge(bridgeName);
+ deployBridge(newBridgeConfig);
+ } else if (existingBridge == null) {
+ // this is a new bridge
+ deployBridge(newBridgeConfig);
+ }
}
- }
-
- //Look for already running bridges no longer in configuration, stop
if found
- for (final Bridge existingBridge :
clusterManager.getBridges().values()) {
- BridgeConfiguration existingBridgeConfig =
existingBridge.getConfiguration();
+ //Look for already running bridges no longer in configuration,
stop if found
+ for (final Bridge existingBridge :
clusterManager.getBridges().values()) {
+ BridgeConfiguration existingBridgeConfig =
existingBridge.getConfiguration();
- if (existingBridgeConfig.isConfigurationManaged()) {
- String existingBridgeName =
existingBridgeConfig.getParentName();
+ if (existingBridgeConfig.isConfigurationManaged()) {
+ String existingBridgeName =
existingBridgeConfig.getParentName();
- boolean noLongerConfigured =
configuration.getBridgeConfigurations().stream()
- .noneMatch(bridge ->
bridge.getParentName().equals(existingBridgeName));
+ boolean noLongerConfigured =
configuration.getBridgeConfigurations().stream()
+ .noneMatch(bridge ->
bridge.getParentName().equals(existingBridgeName));
- if (noLongerConfigured) {
- destroyBridge(existingBridgeName);
+ if (noLongerConfigured) {
+ destroyBridge(existingBridgeName);
+ }
}
}
+ recoverStoredBridges();
}
- recoverStoredBridges();
recoverStoredConnectors();
ActiveMQServerLogger.LOGGER.reloadingConfiguration("protocol
services");
diff --git
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PrimaryOnlyActivation.java
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PrimaryOnlyActivation.java
index 0606f60a71..9de817d634 100644
---
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PrimaryOnlyActivation.java
+++
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PrimaryOnlyActivation.java
@@ -67,6 +67,12 @@ public class PrimaryOnlyActivation extends Activation {
@Override
public void run() {
try {
+ if
(activeMQServer.getConfiguration().getConfigurationFileRefreshPeriod() > 0) {
+ // we may have stale config after waiting for a lock for a while
+ if (activeMQServer.getUptimeMillis() >
activeMQServer.getConfiguration().getConfigurationFileRefreshPeriod()) {
+ activeMQServer.reloadConfigurationFile();
+ }
+ }
activeMQServer.initialisePart1(false);
activeMQServer.registerActivateCallback(activeMQServer.getNodeManager().startPrimaryNode());
diff --git
a/tests/artemis-test-support/src/main/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java
b/tests/artemis-test-support/src/main/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java
index e98ab6673d..58ca423847 100644
---
a/tests/artemis-test-support/src/main/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java
+++
b/tests/artemis-test-support/src/main/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java
@@ -468,6 +468,9 @@ public abstract class ActiveMQTestBase extends
ArtemisTestCase {
// When it comes to the testsuite, we don't need any batching, I will
leave some minimal batching to exercise the codebase
configuration.setJournalBufferTimeout_AIO(100).setJournalBufferTimeout_NIO(100);
+ // protect programmatic config before start, a common pattern that is
only good for the current default which is 5s
+ configuration.setConfigurationFileRefreshPeriod(-1);
+
return configuration;
}
diff --git
a/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/JMSSaslExternalLDAPTest.java
b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/JMSSaslExternalLDAPTest.java
index 3cac43d827..6c839c9703 100644
---
a/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/JMSSaslExternalLDAPTest.java
+++
b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/JMSSaslExternalLDAPTest.java
@@ -115,6 +115,7 @@ public class JMSSaslExternalLDAPTest extends
AbstractLdapTestUnit {
ActiveMQJAASSecurityManager securityManager = new
ActiveMQJAASSecurityManager("SaslExternalPlusLdap");
Configuration configuration = new
ConfigurationImpl().setSecurityEnabled(true).addAcceptorConfiguration(new
TransportConfiguration(InVMAcceptorFactory.class.getCanonicalName())).setJournalDirectory(ActiveMQTestBase.getJournalDir(testDir,
0, false)).setBindingsDirectory(ActiveMQTestBase.getBindingsDir(testDir, 0,
false)).setPagingDirectory(ActiveMQTestBase.getPageDir(testDir, 0,
false)).setLargeMessagesDirectory(ActiveMQTestBase.getLargeMessagesDir(testDir,
0, false));
+ configuration.setConfigurationFileRefreshPeriod(-1); // such that config
reload does not whack programmatic config
server = ActiveMQServers.newActiveMQServer(configuration,
ManagementFactory.getPlatformMBeanServer(), securityManager, false);
diff --git
a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ConfigurationTest.java
b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ConfigurationTest.java
index d44827f3c1..7f4dd0c7e3 100644
---
a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ConfigurationTest.java
+++
b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ConfigurationTest.java
@@ -23,6 +23,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.config.CoreQueueConfiguration;
@@ -269,6 +271,77 @@ public class ConfigurationTest extends ActiveMQTestBase {
}
}
+ @Test
+ public void
testPropertiesDirWithFilterConfigReloadOnNewFileAfterGettingJournalLock()
throws Exception {
+
+ File propsFile = new File(getTestDirfile(), "some.custom_props");
+ propsFile.createNewFile();
+
+ Properties properties = new
ConfigurationImpl.InsertionOrderedProperties();
+ properties.put("configurationFileRefreshPeriod", "100");
+ properties.put("persistenceEnabled", "true");
+ properties.put("connectionRouters.joe.localTargetFilter", "LF");
+
+ try (FileOutputStream outStream = new FileOutputStream(propsFile)) {
+ properties.store(outStream, null);
+ }
+ assertTrue(propsFile.exists());
+
+ FileConfiguration fc = new FileConfiguration();
+ ActiveMQJAASSecurityManager sm = new
ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), new
SecurityConfiguration());
+ ActiveMQServer server = addServer(new ActiveMQServerImpl(fc, sm));
+ server.getConfiguration().setBrokerInstance(getTestDirfile());
+
+ server.setProperties(getTestDirfile().getAbsolutePath() +
"/?filter=.*\\.custom_props"); // no xml config
+ server.getConfiguration().setConfigurationFileRefreshPeriod(100);
+ CountDownLatch blockActivation = new CountDownLatch(1);
+ CountDownLatch inActivation = new CountDownLatch(1);
+ try {
+ ((ActiveMQServerImpl) server).setAfterActivationCreated(() -> {
+ try {
+ inActivation.countDown();
+ blockActivation.await(4, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ Thread t = new Thread(() -> {
+ try {
+ server.start();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ t.start();
+
+ inActivation.await();
+
+
TimeUnit.MILLISECONDS.sleep(server.getConfiguration().getConfigurationFileRefreshPeriod()
+ 100);
+
+ // new file while blocked on activation, like waiting for a file lock
release
+ propsFile = new File(getTestDirfile(), "somemore.custom_props");
+ propsFile.createNewFile();
+ properties = new Properties();
+ properties.put("connectionRouters.joe.localTargetFilter", "UPDATED");
+ try (FileOutputStream outStream = new FileOutputStream(propsFile)) {
+ properties.store(outStream, null);
+ }
+
+ // release activation to see if it will reload the new config
+ blockActivation.countDown();
+
+ Wait.assertTrue(() -> {
+ return
"UPDATED".equals(server.getConfiguration().getConnectionRouters().get(0).getLocalTargetFilter());
+ });
+ } finally {
+ try {
+ server.stop();
+ } catch (Exception e) {
+ }
+ }
+ }
+
protected ActiveMQServer getActiveMQServer(String brokerConfig) throws
Exception {
FileConfiguration fc = new FileConfiguration();
FileJMSConfiguration fileConfiguration = new FileJMSConfiguration();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact