This is an automated email from the ASF dual-hosted git repository.
gtully 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 28bd3c2e70 ARTEMIS-5746 support a filter query parameter on the dir
path for broker properties to allow a custom file name filter to be used
28bd3c2e70 is described below
commit 28bd3c2e709be0afde232094ba1d7a7640fd1558
Author: Gary Tully <[email protected]>
AuthorDate: Thu Nov 6 17:04:37 2025 +0000
ARTEMIS-5746 support a filter query parameter on the dir path for broker
properties to allow a custom file name filter to be used
---
.../activemq/artemis/utils/uri/URISupport.java | 4 ++
.../core/config/impl/ConfigurationImpl.java | 30 ++++++++---
.../core/server/impl/ActiveMQServerImpl.java | 4 ++
.../core/config/impl/ConfigurationImplTest.java | 21 ++++++++
docs/user-manual/configuration-index.adoc | 9 +++-
.../integration/server/ConfigurationTest.java | 58 ++++++++++++++++++++++
6 files changed, 116 insertions(+), 10 deletions(-)
diff --git
a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/uri/URISupport.java
b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/uri/URISupport.java
index e9edae5031..48f1366e3c 100644
---
a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/uri/URISupport.java
+++
b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/uri/URISupport.java
@@ -132,6 +132,10 @@ public class URISupport {
return rc;
}
+ public static String stripQuery(String url) {
+ return url.substring(0, url.indexOf('?'));
+ }
+
public static boolean containsQuery(String uri) {
return uri.contains("?");
}
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 91c9f22302..a81dda1a4d 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
@@ -60,6 +60,7 @@ import java.util.Stack;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.Adler32;
@@ -145,6 +146,7 @@ import
org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy;
import org.apache.activemq.artemis.utils.sm.SecurityManagerShim;
import org.apache.activemq.artemis.utils.uri.BeanSupport;
import
org.apache.activemq.artemis.utils.uri.FluentPropertyBeanIntrospectorWithIgnores;
+import org.apache.activemq.artemis.utils.uri.URISupport;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.Converter;
@@ -169,6 +171,8 @@ public class ConfigurationImpl implements Configuration,
Serializable {
public static final String REDACTED = "**redacted**";
+ private static final String FILTER_QUERY_PARAMETER_KEY = "filter";
+
private static final long serialVersionUID = 4077088945050267843L;
private String name = "localhost";
@@ -480,6 +484,7 @@ public class ConfigurationImpl implements Configuration,
Serializable {
private File artemisInstance;
private transient JsonObject jsonStatus =
JsonLoader.createObjectBuilder().build();
private final Set<String> keysToRedact = new HashSet<>();
+ private static final Pattern defaultPropertiesFileNamePattern =
Pattern.compile(".*\\.(json|properties)");
private JsonObject getJsonStatus() {
if (jsonStatus == null) {
@@ -567,16 +572,25 @@ public class ConfigurationImpl implements Configuration,
Serializable {
}
@Override
- public Configuration parseProperties(String fileUrlToProperties) throws
Exception {
+ public Configuration parseProperties(String pathsToProperties) throws
Exception {
// system property overrides location of file(s)
- fileUrlToProperties = resolvePropertiesSources(fileUrlToProperties);
- if (fileUrlToProperties != null) {
- for (String fileUrl : fileUrlToProperties.split(",")) {
- if (fileUrl.endsWith("/")) {
+ pathsToProperties = resolvePropertiesSources(pathsToProperties);
+ if (pathsToProperties != null) {
+ for (String path : pathsToProperties.split(",")) {
+ Pattern patternToUse = defaultPropertiesFileNamePattern;
+ if (URISupport.containsQuery(path)) {
+ Map<String, String> parameters = URISupport.parseQuery(path);
+ if (parameters.containsKey(FILTER_QUERY_PARAMETER_KEY)) {
+ patternToUse =
Pattern.compile(parameters.get(FILTER_QUERY_PARAMETER_KEY));
+ }
+ path = URISupport.stripQuery(path);
+ }
+ if (path.endsWith("/")) {
// treat as a directory and parse every property file in
alphabetical order
- File dir = new File(fileUrl);
+ File dir = new File(path);
if (dir.exists()) {
- String[] files = dir.list((file, s) -> s.endsWith(".json")
|| s.endsWith(".properties"));
+ final Pattern forLambda = patternToUse;
+ String[] files = dir.list((file, s) ->
forLambda.matcher(s).matches());
if (files != null && files.length > 0) {
Arrays.sort(files);
for (String fileName : files) {
@@ -585,7 +599,7 @@ public class ConfigurationImpl implements Configuration,
Serializable {
}
}
} else {
- parseFileProperties(new File(fileUrl));
+ parseFileProperties(new File(path));
}
}
}
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 3a3c04dcd2..273dc5d606 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
@@ -224,6 +224,7 @@ import
org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy;
import org.apache.activemq.artemis.utils.critical.CriticalComponent;
import org.apache.activemq.artemis.utils.critical.EmptyCriticalAnalyzer;
import org.apache.activemq.artemis.utils.sm.SecurityManagerShim;
+import org.apache.activemq.artemis.utils.uri.URISupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -3400,6 +3401,9 @@ public class ActiveMQServerImpl implements ActiveMQServer
{
String propsLocations =
configuration.resolvePropertiesSources(propertiesFileUrl);
if (propsLocations != null) {
for (String fileUrl : propsLocations.split(",")) {
+ if (URISupport.containsQuery(fileUrl)) {
+ fileUrl = URISupport.stripQuery(fileUrl);
+ }
reloadManager.addCallback(new File(fileUrl).toURI().toURL(),
fullConfigReloadCallback);
}
}
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 d8a5d1d03c..6fc6996a77 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
@@ -2770,6 +2770,27 @@ public class ConfigurationImplTest extends
AbstractConfigurationTestBase {
assertTrue(names.isEmpty(), "all names applied");
}
+ @Test
+ public void testRegxPropertiesFilesInDir() throws Exception {
+
+ File tmpFile = File.createTempFile("a_stuff", ".properties",
temporaryFolder);
+ Properties properties = new Properties();
+ properties.put("name", "a");
+ properties.store(new FileOutputStream(tmpFile), null);
+
+ tmpFile = File.createTempFile("b_stuff", ".0-properties",
temporaryFolder);
+ properties = new Properties();
+ properties.put("name", "0");
+ properties.store(new FileOutputStream(tmpFile), null);
+
+ ConfigurationImpl configuration = new ConfigurationImpl();
+ configuration.parseProperties(temporaryFolder + "/");
+ assertEquals("a", configuration.getName());
+
+ configuration.parseProperties(temporaryFolder +
"/?filter=.*\\.(json|properties|0-properties)");
+ assertEquals("0", configuration.getName());
+ }
+
@Test
public void testNameWithDotsSurroundWithDollarDollar() throws Throwable {
ConfigurationImpl configuration = new ConfigurationImpl();
diff --git a/docs/user-manual/configuration-index.adoc
b/docs/user-manual/configuration-index.adoc
index ed70eca242..517f1acb3f 100644
--- a/docs/user-manual/configuration-index.adoc
+++ b/docs/user-manual/configuration-index.adoc
@@ -117,9 +117,14 @@ acceptorConfigurations.tcp.params.port=61616
==== Usage
-The `artemis run` command script supports `--properties <properties file
url>`, where a properties file can be configured.
+The `artemis run` command script supports `--properties <path to properties
files comma list>` where properties file paths can be configured.
+If a properties path string ends in a '/' it represents a directory. Any file
name matching the regex `.*\.(json|properties)` in that directory is treated as
broker configuration.
-There is a related xref:management.adoc#server-management[server management
operation] `exportConfigAsProperties` that will export the current
configuration as broker properties. Use this to get a better understanding of
how properties apply to the broker configuration and how they map from xml.
+It is possible to override the default regex filter pattern with a `filter`
query parameter on the directory path, e.g: `--properties
'/home/x/broker_props/?filter=.*\.custom_props'`.
+
+==== Broker configuration in properties format
+
+The xref:management.adoc#server-management[server management operation]
`exportConfigAsProperties` will export the current configuration of a broker as
broker properties. Use this to get a better understanding of how properties
apply to the broker configuration and how they map from xml.
==== Attribute Names with Dots
Dots are significant in property keys because they identify the nesting level.
If attribute names contain dots those need to be quoted. The quote string is
specified via the property `key.surround` and has a default value of the double
quote character: `"`.
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 c34bc35148..d44827f3c1 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
@@ -211,6 +211,64 @@ public class ConfigurationTest extends ActiveMQTestBase {
}
}
+ @Test
+ public void testPropertiesDirWithFilterConfigReloadOnNewFile() throws
Exception {
+
+ File propsFile = new File(getTestDirfile(), "some.custom_props");
+ propsFile.createNewFile();
+
+
+ Properties properties = new
ConfigurationImpl.InsertionOrderedProperties();
+ properties.put("configurationFileRefreshPeriod", "100");
+ properties.put("persistenceEnabled", "false");
+ 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.setProperties(getTestDirfile().getAbsolutePath() +
"/?filter=.*\\.custom_props"); // no xml config
+ try {
+
+ server.start();
+
+ assertEquals(1,
server.getConfiguration().getConnectionRouters().size());
+ assertEquals("LF",
server.getConfiguration().getConnectionRouters().get(0).getLocalTargetFilter());
+
+ // verify ignored file, alphabetically after that will get ignored
due to the filter non match
+ properties = new Properties();
+ properties.put("connectionRouters.joe.localTargetFilter", "LF");
+ propsFile = new File(getTestDirfile(), "v_somemore.properties");
+ propsFile.createNewFile();
+ try (FileOutputStream outStream = new FileOutputStream(propsFile)) {
+ properties.store(outStream, null);
+ }
+
+ // verify update via new file
+ propsFile = new File(getTestDirfile(), "u_somemore.custom_props");
+ propsFile.createNewFile();
+ properties = new Properties();
+ properties.put("connectionRouters.joe.localTargetFilter", "UPDATED");
+ try (FileOutputStream outStream = new FileOutputStream(propsFile)) {
+ properties.store(outStream, null);
+ }
+
+ 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