-
Notifications
You must be signed in to change notification settings - Fork 947
ARTEMIS-5871 restrict reload of properties config to the set of reloa… #6205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -149,6 +149,25 @@ public static void validateConfiguration(Configuration configuration) { | |
| compareTTLWithCheckPeriod(configuration); | ||
| } | ||
|
|
||
| public static void throwIfReloadableConfigProvidedWithoutFileAndBrokerPropertiesUrlNonNullAndReload(Configuration configuration, String propertiesFileUrl) { | ||
| if (configuration.getConfigurationUrl() == null && configuration.getConfigurationFileRefreshPeriod() > 0 && configuration.resolvePropertiesSources(propertiesFileUrl) != null) { | ||
| // if any non xml (programmatic) provided config is reloadable, on the first properties source reload it will whack that config as the reload has the source of truth for reloadable attributes | ||
| if (!configuration.getSecurityRoles().isEmpty() || | ||
| !configuration.getAddressSettings().isEmpty() || | ||
| !configuration.getDivertConfigurations().isEmpty() || | ||
| !configuration.getAddressConfigurations().isEmpty() || | ||
| !configuration.getQueueConfigs().isEmpty() || | ||
| !configuration.getBridgeConfigurations().isEmpty() || | ||
| !configuration.getConnectorConfigurations().isEmpty() || | ||
| !configuration.getAcceptorConfigurations().isEmpty() || | ||
| !configuration.getAMQPConnection().isEmpty() || | ||
| !configuration.getConnectionRouters().isEmpty()) { | ||
|
|
||
| throw new IllegalArgumentException(String.format("a properties source (%s) is illegal, programmatic config contains reloadable elements and configurationFileRefreshPeriod > 0; your programmatic config will be replaced on reload of the properties source", propertiesFileUrl)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels like IllegalState might be more appropriate since this is used in the setProperties(..) method and the very same properties path argument could be valid depending on the existing config/state. |
||
| } | ||
| } | ||
| } | ||
|
|
||
| public static List<TransportConfiguration> parseAcceptorURI(String name, String uri) { | ||
| try { | ||
| // remove all whitespace | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -230,6 +230,7 @@ | |
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import static java.util.stream.Collectors.groupingBy; | ||
| import static org.apache.activemq.artemis.core.config.ConfigurationUtils.throwIfReloadableConfigProvidedWithoutFileAndBrokerPropertiesUrlNonNullAndReload; | ||
| import static org.apache.activemq.artemis.utils.collections.IterableStream.iterableOf; | ||
|
|
||
| /** | ||
|
|
@@ -681,6 +682,7 @@ public CriticalAnalyzer getCriticalAnalyzer() { | |
| @Override | ||
| public void setProperties(String fileUrltoBrokerProperties) { | ||
| propertiesFileUrl = fileUrltoBrokerProperties; | ||
| throwIfReloadableConfigProvidedWithoutFileAndBrokerPropertiesUrlNonNullAndReload(configuration, propertiesFileUrl); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The check should be before the path is set, otherwise it is ineffective and acts as if this is a perfectly valid argument (as the later test shows). The exception should be documented. |
||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -4682,22 +4684,28 @@ public void reloadConfigurationFile() throws Exception { | |
| } | ||
|
|
||
| private void reloadConfigurationFile(URL xmlConfigUri) throws Exception { | ||
| Configuration config = new ConfigurationImpl(); | ||
| if (xmlConfigUri != null) { | ||
| Configuration config = new FileConfigurationParser().parseMainConfig(xmlConfigUri.openStream()); | ||
| config = new FileConfigurationParser().parseMainConfig(xmlConfigUri.openStream()); | ||
| LegacyJMSConfiguration legacyJMSConfiguration = new LegacyJMSConfiguration(config); | ||
| legacyJMSConfiguration.parseConfiguration(xmlConfigUri.openStream()); | ||
| configuration.setSecurityRoles(config.getSecurityRoles()); | ||
| configuration.setAddressSettings(config.getAddressSettings()); | ||
| configuration.setDivertConfigurations(config.getDivertConfigurations()); | ||
| configuration.setAddressConfigurations(config.getAddressConfigurations()); | ||
| configuration.setQueueConfigs(config.getQueueConfigs()); | ||
| configuration.setBridgeConfigurations(config.getBridgeConfigurations()); | ||
| configuration.setConnectorConfigurations(config.getConnectorConfigurations()); | ||
| configuration.setAcceptorConfigurations(config.getAcceptorConfigurations()); | ||
| configuration.setAMQPConnectionConfigurations(config.getAMQPConnection()); | ||
| configuration.setPurgePageFolders(config.isPurgePageFolders()); | ||
| } | ||
| configuration.parseProperties(propertiesFileUrl); | ||
| config.parseProperties(propertiesFileUrl); | ||
| configuration.setStatus(config.getStatus()); | ||
|
|
||
gtully marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| configuration.setSecurityRoles(config.getSecurityRoles()); | ||
| configuration.setAddressSettings(config.getAddressSettings()); | ||
| configuration.setDivertConfigurations(config.getDivertConfigurations()); | ||
| configuration.setAddressConfigurations(config.getAddressConfigurations()); | ||
| configuration.setQueueConfigs(config.getQueueConfigs()); | ||
| configuration.setBridgeConfigurations(config.getBridgeConfigurations()); | ||
| configuration.setConnectorConfigurations(config.getConnectorConfigurations()); | ||
| configuration.setAcceptorConfigurations(config.getAcceptorConfigurations()); | ||
| configuration.setAMQPConnectionConfigurations(config.getAMQPConnection()); | ||
| configuration.setPurgePageFolders(config.isPurgePageFolders()); | ||
| configuration.setConnectionRouters(config.getConnectionRouters()); // needs reload logic | ||
| configuration.setJaasConfigs(config.getJaasConfigs()); | ||
|
Comment on lines
+4706
to
+4707
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doenst properly align with the checks done previously (see earlier comment). It also adds [XML] reload (though without logic?) for bits that were not covered before, which doesnt seem suited to the Jira description and feels somewhat separate. |
||
|
|
||
| updateStatus(ServerStatus.CONFIGURATION_COMPONENT, configuration.getStatus()); | ||
| configurationReloadDeployed.set(false); | ||
| if (isActive()) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -427,6 +427,7 @@ private void prepareNodesAndStartCombinedHeadTail() throws Exception { | |
| .setAutoDeleteQueues(false).setAutoDeleteAddresses(false); // so slow consumer can kick in! | ||
|
|
||
| Configuration baseConfig = new ConfigurationImpl(); | ||
| baseConfig.setConfigurationFileRefreshPeriod(-1); // the classpath has broker.properties we don't want to reload | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then it probably shouldnt since that would mean we dont actually know the current config of the broker during most of the tests. |
||
| baseConfig.getAddressSettings().put(qName, blockingQueue); | ||
|
|
||
| ConnectionRouterConfiguration connectionRouterConfiguration = new ConnectionRouterConfiguration(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,12 +16,9 @@ | |
| */ | ||
| package org.apache.activemq.artemis.tests.integration.server; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| import java.io.File; | ||
| import java.io.FileOutputStream; | ||
| import java.io.StringReader; | ||
| import java.util.Properties; | ||
| import java.util.concurrent.CountDownLatch; | ||
| import java.util.concurrent.TimeUnit; | ||
|
|
@@ -36,13 +33,21 @@ | |
| 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.json.JsonObject; | ||
| 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.apache.activemq.artemis.utils.JsonLoader; | ||
| import org.apache.activemq.artemis.utils.RandomUtil; | ||
| import org.apache.activemq.artemis.tests.util.Wait; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertNotEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| public class ConfigurationTest extends ActiveMQTestBase { | ||
|
|
||
| @Test | ||
|
|
@@ -183,11 +188,15 @@ public void testPropertiesOnlyConfigReload() throws Exception { | |
| properties.put("configurationFileRefreshPeriod", "100"); | ||
| properties.put("persistenceEnabled", "false"); | ||
| properties.put("connectionRouters.joe.localTargetFilter", "LF"); | ||
| properties.put("acceptorConfigurations.tcp.factoryClassName", NETTY_ACCEPTOR_FACTORY); | ||
| properties.put("acceptorConfigurations.tcp.params.HOST", "LOCALHOST"); | ||
| properties.put("acceptorConfigurations.tcp.params.PORT", "61616"); | ||
|
|
||
| try (FileOutputStream outStream = new FileOutputStream(propsFile)) { | ||
| properties.store(outStream, null); | ||
| } | ||
| assertTrue(propsFile.exists()); | ||
| properties.clear(); | ||
|
|
||
| FileConfiguration fc = new FileConfiguration(); | ||
| ActiveMQJAASSecurityManager sm = new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), new SecurityConfiguration()); | ||
|
|
@@ -199,12 +208,13 @@ public void testPropertiesOnlyConfigReload() throws Exception { | |
|
|
||
| assertEquals(1, server.getConfiguration().getConnectionRouters().size()); | ||
| assertEquals("LF", server.getConfiguration().getConnectionRouters().get(0).getLocalTargetFilter()); | ||
|
|
||
| properties.put("persistenceEnabled", "false"); | ||
| properties.put("configurationFileRefreshPeriod", "100"); | ||
|
|
||
| assertEquals(1, server.getActiveMQServerControl().getAcceptors().length); | ||
| // verify update | ||
| properties.put("configurationFileRefreshPeriod", "100"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like either some comment needed to explain better, or the properties clear should be here, or better a fresh set of properties used. Could probably do with a comment either way to describe the changes such this is both 'update' and the later 'remove' changes. |
||
| properties.put("persistenceEnabled", "false"); | ||
| properties.put("connectionRouters.joe.localTargetFilter", "UPDATED"); | ||
|
|
||
| String startedStatus = server.getStatus(); | ||
| try (FileOutputStream outStream = new FileOutputStream(propsFile)) { | ||
| properties.store(outStream, null); | ||
| } | ||
|
|
@@ -213,6 +223,20 @@ public void testPropertiesOnlyConfigReload() throws Exception { | |
| return "UPDATED".equals(server.getConfiguration().getConnectionRouters().get(0).getLocalTargetFilter()); | ||
| }); | ||
|
|
||
| // verify remove | ||
| assertEquals(0, server.getActiveMQServerControl().getAcceptors().length); | ||
|
|
||
| // verify status json reflects update | ||
| String updatedStatus = server.getStatus(); | ||
| assertNotEquals(startedStatus, updatedStatus); | ||
| assertTrue(startedStatus.contains(propsFile.getName())); | ||
| assertTrue(updatedStatus.contains(propsFile.getName())); | ||
| JsonObject jsonStarted = JsonLoader.readObject(new StringReader(startedStatus)); | ||
| JsonObject jsonUpdated = JsonLoader.readObject(new StringReader(updatedStatus)); | ||
| String alder32Used = jsonStarted.getJsonObject("configuration").getJsonObject("properties").getJsonObject(propsFile.getName()).getString("fileAlder32"); | ||
| String alder32Updated = jsonUpdated.getJsonObject("configuration").getJsonObject("properties").getJsonObject(propsFile.getName()).getString("fileAlder32"); | ||
| assertNotEquals(alder32Used, alder32Updated); | ||
|
|
||
| } finally { | ||
| try { | ||
| server.stop(); | ||
|
|
@@ -350,6 +374,55 @@ public void testPropertiesDirWithFilterConfigReloadOnNewFileAfterGettingJournalL | |
| } | ||
| } | ||
|
|
||
| @Test | ||
| public void testPropertiesAndProgrammaticReloadableConfigArg() throws Exception { | ||
|
|
||
| File propsFile = new File(getTestDirfile(), "somemore.props"); | ||
| propsFile.createNewFile(); | ||
|
|
||
|
|
||
| Properties properties = new ConfigurationImpl.InsertionOrderedProperties(); | ||
| properties.put("configurationFileRefreshPeriod", "100"); | ||
| properties.put("persistenceEnabled", "false"); | ||
| properties.put("acceptorConfigurations.reloadable.factoryClassName", NETTY_ACCEPTOR_FACTORY); | ||
| properties.put("acceptorConfigurations.reloadable.params.HOST", "LOCALHOST"); | ||
| properties.put("acceptorConfigurations.reloadable.params.PORT", "61616"); | ||
|
|
||
| try (FileOutputStream outStream = new FileOutputStream(propsFile)) { | ||
| properties.store(outStream, null); | ||
| } | ||
| assertTrue(propsFile.exists()); | ||
|
|
||
| ConfigurationImpl programmatic = new ConfigurationImpl(); | ||
| programmatic.addAcceptorConfiguration("tcp", "tcp://localhost:62618"); | ||
| ActiveMQJAASSecurityManager sm = new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), new SecurityConfiguration()); | ||
| ActiveMQServer server = addServer(new ActiveMQServerImpl(programmatic, sm)); | ||
|
|
||
| assertThrows(IllegalArgumentException.class, () -> { | ||
| server.setProperties(propsFile.getAbsolutePath()); | ||
| }); | ||
|
Comment on lines
+401
to
+403
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesnt really make sense. You explicily check it throws IAE (again ISE seems more in line), but then the test proceeds and acts as if this was a perfectly valid argument and the IAE didnt happen, and explicitly checks the 'new properties get reloaded and tosses away all original configuration' thing that didnt used to happen. |
||
| try { | ||
|
|
||
| server.start(); | ||
|
|
||
| assertEquals(2, server.getActiveMQServerControl().getAcceptors().length); | ||
|
|
||
| // no change, just a touch | ||
| try (FileOutputStream outStream = new FileOutputStream(propsFile)) { | ||
| properties.store(outStream, null); | ||
| } | ||
|
|
||
| // reloaded properties are the source of truth | ||
| Wait.assertEquals(1, ()-> server.getActiveMQServerControl().getAcceptors().length); | ||
|
|
||
| } finally { | ||
| try { | ||
| server.stop(); | ||
| } catch (Exception e) { | ||
| } | ||
| } | ||
| } | ||
|
|
||
| protected ActiveMQServer getActiveMQServer(String brokerConfig) throws Exception { | ||
| FileConfiguration fc = new FileConfiguration(); | ||
| FileJMSConfiguration fileConfiguration = new FileJMSConfiguration(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If not the whole method, then at least this bit of it seems like it should maybe be in the Configuration so its less likely to go stale from not being updated when the Configuration is.
E.g this is actually incorrect here already, because it does not match what you do later in the config reload bits below (which points to some missing testing).