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


Reply via email to