ARTEMIS-3627 - support broker.properties for augmenting or supplying additional configuration via nested properties of the internal configuratinimpl bean - elements with a name attribute can be configured in collections, the type inferred by the add singular fluent api

This commit is contained in:
gtully 2022-01-07 17:50:03 +00:00 committed by Gary Tully
parent 9c01f9b983
commit 10d93d9c92
18 changed files with 626 additions and 28 deletions

View File

@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicReference;
import io.airlift.airline.Command;
import io.airlift.airline.Option;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.cli.Artemis;
import org.apache.activemq.artemis.cli.commands.tools.LockAbstract;
@ -47,6 +48,9 @@ public class Run extends LockAbstract {
@Option(name = "--allow-kill", description = "This will allow the server to kill itself. Useful for tests (failover tests for instance)")
boolean allowKill;
@Option(name = "--properties", description = "A file url to a properties file that is applied to the server's internal ConfigurationImpl bean")
String properties;
private static boolean embedded = false;
public static final ReusableLatch latchRunning = new ReusableLatch(0);
@ -112,6 +116,14 @@ public class Run extends LockAbstract {
server.createComponents();
server.getServer().registerActivationFailureListener(exception -> serverActivationFailed.set(exception));
if (properties == null) {
File propertiesFileFromEtc = new File(getBrokerEtc(), ActiveMQDefaultConfiguration.BROKER_PROPERTIES_SYSTEM_PROPERTY_NAME);
if (propertiesFileFromEtc.exists()) {
properties = propertiesFileFromEtc.getAbsolutePath();
}
}
server.getServer().setProperties(properties);
server.start();
server.getServer().addExternalComponent(managementContext, false);
@ -121,7 +133,7 @@ public class Run extends LockAbstract {
for (ComponentDTO componentDTO : broker.components) {
Class clazz = this.getClass().getClassLoader().loadClass(componentDTO.componentClassName);
ExternalComponent component = (ExternalComponent) clazz.newInstance();
ExternalComponent component = (ExternalComponent) clazz.getDeclaredConstructor(null).newInstance();
component.configure(componentDTO, getBrokerInstance(), getBrokerHome());
server.getServer().addExternalComponent(component, true);
assert component.isStarted();
@ -129,6 +141,7 @@ public class Run extends LockAbstract {
} catch (Throwable t) {
t.printStackTrace();
serverActivationFailed.set(t);
latchRunning.countDown();
}
if (serverActivationFailed.get() != null) {

View File

@ -39,6 +39,7 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
@ -1920,6 +1921,39 @@ public class ArtemisTest extends CliTestBase {
}
@Test
public void testRunPropertiesArgumentSetsAcceptorPort() throws Exception {
File instanceFile = new File(temporaryFolder.getRoot(), "testRunPropertiesArgumentSetsAcceptorPort");
setupAuth(instanceFile);
Run.setEmbedded(true);
Artemis.main("create", instanceFile.getAbsolutePath(), "--silent", "--no-fsync", "--no-autotune", "--no-web", "--require-login");
System.setProperty("artemis.instance", instanceFile.getAbsolutePath());
// configure
URL brokerPropertiesFromClasspath = this.getClass().getClassLoader().getResource(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_SYSTEM_PROPERTY_NAME);
Artemis.internalExecute("run", "--properties", new File(brokerPropertiesFromClasspath.toURI()).getAbsolutePath());
// verify
try (ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:61618"); Connection connection = cf.createConnection("admin", "admin");) {
connection.start();
} finally {
stopServer();
}
}
@Test
public void testRunPropertiesDudArgument() throws Exception {
File instanceFile = new File(temporaryFolder.getRoot(), "testRunPropertiesDudArgument");
setupAuth(instanceFile);
Run.setEmbedded(true);
Artemis.main("create", instanceFile.getAbsolutePath(), "--silent", "--no-fsync", "--no-autotune", "--no-web", "--require-login");
System.setProperty("artemis.instance", instanceFile.getAbsolutePath());
// verify error
Object ret = Artemis.internalExecute("run", "--properties", "https://www.apache.org");
assertTrue(ret instanceof IllegalStateException);
}
@Test
public void testVersionCommand() throws Exception {
TestActionContext context = new TestActionContext();

View File

@ -0,0 +1,19 @@
#
# 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.
#
name=ConfiguredViaProperties
acceptorConfigurations.artemis.params.port=61618

View File

@ -557,6 +557,12 @@ public final class ActiveMQDefaultConfiguration {
public static final String DEFAULT_SYSTEM_PROPERTY_PREFIX = "brokerconfig.";
public static final String BROKER_PROPERTIES_SYSTEM_PROPERTY_NAME = "broker.properties";
public static final String BROKER_PROPERTIES_KEY_SURROUND = "\"";
public static final String BROKER_PROPERTIES_KEY_SURROUND_PROPERTY = "key.surround";
public static String DEFAULT_NETWORK_CHECK_LIST = null;
public static String DEFAULT_NETWORK_CHECK_URL_LIST = null;
@ -1587,6 +1593,10 @@ public final class ActiveMQDefaultConfiguration {
return DEFAULT_SYSTEM_PROPERTY_PREFIX;
}
public static String getDefaultBrokerPropertiesKeySurround() {
return BROKER_PROPERTIES_KEY_SURROUND;
}
public static String getDefaultNetworkCheckList() {
return DEFAULT_NETWORK_CHECK_LIST;
}

View File

@ -164,6 +164,10 @@ public class TransportConfiguration implements Serializable {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* Returns the class name of ConnectorFactory being used by this TransportConfiguration
*

View File

@ -20,7 +20,6 @@ import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@ -87,9 +86,7 @@ public interface Configuration {
*/
String getSystemPropertyPrefix();
Configuration parseSystemProperties() throws Exception;
Configuration parseSystemProperties(Properties properties) throws Exception;
Configuration parseProperties(String optionalUrlToPropertiesFile) throws Exception;
boolean isCriticalAnalyzer();

View File

@ -31,16 +31,17 @@ public class AMQPBrokerConnectConfiguration extends BrokerConnectConfiguration {
List<TransportConfiguration> transportConfigurations;
List<AMQPBrokerConnectionElement> connectionElements;
List<AMQPBrokerConnectionElement> connectionElements = new ArrayList<>();
public AMQPBrokerConnectConfiguration() {
super(null, null);
}
public AMQPBrokerConnectConfiguration(String name, String uri) {
super(name, uri);
}
public AMQPBrokerConnectConfiguration addElement(AMQPBrokerConnectionElement amqpBrokerConnectionElement) {
if (connectionElements == null) {
connectionElements = new ArrayList<>();
}
amqpBrokerConnectionElement.setParent(this);
@ -53,6 +54,10 @@ public class AMQPBrokerConnectConfiguration extends BrokerConnectConfiguration {
return this;
}
public AMQPBrokerConnectConfiguration addConnectionElement(AMQPMirrorBrokerConnectionElement amqpBrokerConnectionElement) {
return addElement(amqpBrokerConnectionElement);
}
public List<AMQPBrokerConnectionElement> getConnectionElements() {
return connectionElements;
}

View File

@ -23,6 +23,7 @@ import org.apache.activemq.artemis.core.config.WildcardConfiguration;
import org.apache.activemq.artemis.core.postoffice.impl.AddressImpl;
public class AMQPBrokerConnectionElement implements Serializable {
String name;
SimpleString matchAddress;
SimpleString queueName;
AMQPBrokerConnectionAddressType type;
@ -84,4 +85,14 @@ public class AMQPBrokerConnectionElement implements Serializable {
this.type = type;
return this;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -16,21 +16,27 @@
*/
package org.apache.activemq.artemis.core.config.impl;
import java.beans.PropertyDescriptor;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@ -38,12 +44,14 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.BroadcastGroupConfiguration;
import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration;
import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.api.core.QueueConfiguration;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
@ -65,6 +73,8 @@ import org.apache.activemq.artemis.core.config.WildcardConfiguration;
import org.apache.activemq.artemis.core.config.ha.ReplicaPolicyConfiguration;
import org.apache.activemq.artemis.core.config.ha.ReplicatedPolicyConfiguration;
import org.apache.activemq.artemis.core.config.storage.DatabaseStorageConfiguration;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory;
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.core.server.JournalType;
@ -89,7 +99,14 @@ import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
import org.apache.activemq.artemis.utils.Env;
import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy;
import org.apache.activemq.artemis.utils.uri.BeanSupport;
import org.apache.activemq.artemis.utils.uri.FluentPropertyBeanIntrospectorWithIgnores;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.MappedPropertyDescriptor;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.commons.beanutils.expression.DefaultResolver;
import org.jboss.logging.Logger;
public class ConfigurationImpl implements Configuration, Serializable {
@ -338,6 +355,8 @@ public class ConfigurationImpl implements Configuration, Serializable {
private String systemPropertyPrefix = ActiveMQDefaultConfiguration.getDefaultSystemPropertyPrefix();
private String brokerPropertiesKeySurround = ActiveMQDefaultConfiguration.getDefaultBrokerPropertiesKeySurround();
private String networkCheckList = ActiveMQDefaultConfiguration.getDefaultNetworkCheckList();
private String networkURLList = ActiveMQDefaultConfiguration.getDefaultNetworkCheckURLList();
@ -429,31 +448,73 @@ public class ConfigurationImpl implements Configuration, Serializable {
return systemPropertyPrefix;
}
@Override
public Configuration parseSystemProperties() throws Exception {
parseSystemProperties(System.getProperties());
return this;
public String getBrokerPropertiesKeySurround() {
return brokerPropertiesKeySurround;
}
public void setBrokerPropertiesKeySurround(String brokerPropertiesKeySurround) {
this.brokerPropertiesKeySurround = brokerPropertiesKeySurround;
}
@Override
public Configuration parseSystemProperties(Properties properties) throws Exception {
public Configuration parseProperties(String fileUrlToProperties) throws Exception {
// system property overrides
fileUrlToProperties = System.getProperty(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_SYSTEM_PROPERTY_NAME, fileUrlToProperties);
if (fileUrlToProperties != null) {
Properties brokerProperties = new Properties();
try (FileInputStream fileInputStream = new FileInputStream(fileUrlToProperties); BufferedInputStream reader = new BufferedInputStream(fileInputStream)) {
brokerProperties.load(reader);
parsePrefixedProperties(brokerProperties, null);
}
}
parsePrefixedProperties(System.getProperties(), systemPropertyPrefix);
return this;
}
public void parsePrefixedProperties(Properties properties, String prefix) throws Exception {
Map<String, Object> beanProperties = new HashMap<>();
synchronized (properties) {
String key = null;
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
if (entry.getKey().toString().startsWith(systemPropertyPrefix)) {
String key = entry.getKey().toString().substring(systemPropertyPrefix.length());
logger.debug("Setting up config, " + key + "=" + entry.getValue());
beanProperties.put(key, entry.getValue());
key = entry.getKey().toString();
if (prefix != null) {
if (!key.startsWith(prefix)) {
continue;
}
key = entry.getKey().toString().substring(prefix.length());
}
logger.debug("Setting up config, " + key + "=" + entry.getValue());
beanProperties.put(key, entry.getValue());
}
}
if (!beanProperties.isEmpty()) {
BeanSupport.setData(this, beanProperties);
populateWithProperties(beanProperties);
}
}
return this;
public void populateWithProperties(Map<String, Object> beanProperties) throws InvocationTargetException, IllegalAccessException {
BeanUtilsBean beanUtils = new BeanUtilsBean(new ConvertUtilsBean(), new CollectionAutoFillPropertiesUtil());
// nested property keys delimited by . and enclosed by '"' if they key's themselves contain dots
beanUtils.getPropertyUtils().setResolver(new SurroundResolver(getBrokerPropertiesKeySurround(beanProperties)));
beanUtils.getConvertUtils().register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
return (T) SimpleString.toSimpleString(value.toString());
}
}, SimpleString.class);
beanUtils.getPropertyUtils().addBeanIntrospector(new FluentPropertyBeanIntrospectorWithIgnores());
beanUtils.populate(this, beanProperties);
}
private String getBrokerPropertiesKeySurround(Map<String, Object> propertiesToApply) {
if (propertiesToApply.containsKey(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_KEY_SURROUND_PROPERTY)) {
return String.valueOf(propertiesToApply.get(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_KEY_SURROUND_PROPERTY));
} else {
return System.getProperty(getSystemPropertyPrefix() + ActiveMQDefaultConfiguration.BROKER_PROPERTIES_KEY_SURROUND_PROPERTY, getBrokerPropertiesKeySurround());
}
}
@Override
@ -698,6 +759,11 @@ public class ConfigurationImpl implements Configuration, Serializable {
return this;
}
public ConfigurationImpl addConnectorConfiguration(final TransportConfiguration info) {
connectorConfigs.put(info.getName(), info);
return this;
}
@Override
public ConfigurationImpl addConnectorConfiguration(final String name, final String uri) throws Exception {
@ -795,6 +861,10 @@ public class ConfigurationImpl implements Configuration, Serializable {
return this.amqpBrokerConnectConfigurations;
}
public List<AMQPBrokerConnectConfiguration> getAMQPConnections() {
return this.amqpBrokerConnectConfigurations;
}
@Override
public ConfigurationImpl clearClusterConfigurations() {
clusterConfigurations.clear();
@ -1555,6 +1625,10 @@ public class ConfigurationImpl implements Configuration, Serializable {
return this;
}
public ConfigurationImpl addResourceLimitSetting(ResourceLimitSettings resourceLimitSettings) {
return this.addResourceLimitSettings(resourceLimitSettings);
}
@Override
public Map<String, Set<Role>> getSecurityRoles() {
for (SecuritySettingPlugin securitySettingPlugin : securitySettingPlugins) {
@ -2603,4 +2677,190 @@ public class ConfigurationImpl implements Configuration, Serializable {
return this;
}
// extend property utils with ability to auto-fill and locate from collections
// collection entries are identified by the name() property
private static class CollectionAutoFillPropertiesUtil extends PropertyUtilsBean {
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[]{};
final Stack<Pair<String, Object>> collections = new Stack<>();
@Override
public void setProperty(final Object bean, final String name, final Object value) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// any set will invalidate our collections stack
if (!collections.isEmpty()) {
Pair<String, Object> collectionInfo = collections.pop();
}
super.setProperty(bean, name, value);
}
// need to track collections such that we can locate or create entries on demand
@Override
public Object getProperty(final Object bean,
final String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
if (!collections.isEmpty()) {
final String key = getResolver().getProperty(name);
Pair<String, Object> collectionInfo = collections.pop();
if (bean instanceof Map) {
Map map = (Map) bean;
if (!map.containsKey(key)) {
map.put(key, newNamedInstanceForCollection(collectionInfo.getA(), collectionInfo.getB(), key));
}
return map.get(key);
} else { // collection
// locate on name property
for (Object candidate : (Collection) bean) {
if (key.equals(getProperty(candidate, "name"))) {
return candidate;
}
}
// or create it
Object created = newNamedInstanceForCollection(collectionInfo.getA(), collectionInfo.getB(), key);
((Collection) bean).add(created);
return created;
}
}
Object resolved = getNestedProperty(bean, name);
if (resolved instanceof Collection || resolved instanceof Map) {
collections.push(new Pair<String, Object>(name, bean));
}
return resolved;
}
// allow finding beans in collections via name() such that a mapped key (key)
// can be used to access and *not* auto create entries
@Override
public Object getMappedProperty(final Object bean,
final String name, final String key)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
if (bean == null) {
throw new IllegalArgumentException("No bean specified");
}
if (name == null) {
throw new IllegalArgumentException("No name specified for bean class '" +
bean.getClass() + "'");
}
if (key == null) {
throw new IllegalArgumentException("No key specified for property '" +
name + "' on bean class " + bean.getClass() + "'");
}
Object result = null;
final PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
if (descriptor == null) {
throw new NoSuchMethodException("Unknown property '" +
name + "'+ on bean class '" + bean.getClass() + "'");
}
if (descriptor instanceof MappedPropertyDescriptor) {
// Call the keyed getter method if there is one
Method readMethod = ((MappedPropertyDescriptor) descriptor).
getMappedReadMethod();
readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
if (readMethod != null) {
final Object[] keyArray = new Object[1];
keyArray[0] = key;
result = readMethod.invoke(bean, keyArray);
} else {
throw new NoSuchMethodException("Property '" + name +
"' has no mapped getter method on bean class '" +
bean.getClass() + "'");
}
} else {
final Method readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), descriptor.getReadMethod());
if (readMethod != null) {
final Object invokeResult = readMethod.invoke(bean, EMPTY_OBJECT_ARRAY);
if (invokeResult instanceof Map) {
result = ((Map<?, ?>)invokeResult).get(key);
} else if (invokeResult instanceof Collection) {
// locate on name property
for (Object candidate : (Collection) invokeResult) {
if (key.equals(getProperty(candidate, "name"))) {
return candidate;
}
}
}
} else {
throw new NoSuchMethodException("Property '" + name +
"' has no mapped getter method on bean class '" +
bean.getClass() + "'");
}
}
return result;
}
private Object newNamedInstanceForCollection(String collectionPropertyName, Object hostingBean, String name) {
// find the add X and init an instance of the type with name=name
// expect an add... without the plural
String addPropertyName = "add" + Character.toUpperCase(collectionPropertyName.charAt(0)) + collectionPropertyName.substring(1, collectionPropertyName.length() - 1);
// we don't know the type, infer from add method add(X x) or add(String key, X x)
final Method[] methods = hostingBean.getClass().getMethods();
for (Method candidate : methods) {
if (candidate.getName().equals(addPropertyName) &&
(candidate.getParameterCount() == 1 ||
(candidate.getParameterCount() == 2
// has a String key
&& String.class.equals(candidate.getParameterTypes()[0])
// but not initialised from a String form (eg: uri)
&& !String.class.equals(candidate.getParameterTypes()[1])))) {
// create one and initialise with name
try {
Object instance = candidate.getParameterTypes()[candidate.getParameterCount() - 1].getDeclaredConstructor().newInstance(null);
try {
setProperty(instance, "name", name);
} catch (NoSuchMethodException okIgnore) {
}
// this is always going to be a little hacky b/c our config is not natively property friendly
if (instance instanceof TransportConfiguration) {
setProperty(instance, "factoryClassName", "invm".equals(name) ? InVMConnectorFactory.class.getName() : NettyConnectorFactory.class.getName());
}
return instance;
} catch (Exception e) {
logger.debug("Failed to add entry for " + name + " with method: " + candidate, e);
throw new IllegalArgumentException("failed to add entry for collection key " + name, e);
}
}
}
throw new IllegalArgumentException("failed to locate add method for collection property " + addPropertyName);
}
}
private static class SurroundResolver extends DefaultResolver {
final String surroundString;
SurroundResolver(String surroundString) {
this.surroundString = surroundString;
}
@Override
public String next(String expression) {
String result = super.next(expression);
if (result != null) {
if (result.startsWith(surroundString)) {
// we need to recompute to properly terminate this SURROUND
result = expression.substring(expression.indexOf(surroundString));
return result.substring(0, result.indexOf(surroundString, surroundString.length()) + surroundString.length());
}
}
return result;
}
@Override
public String getProperty(final String expression) {
if (expression.startsWith(surroundString) && expression.endsWith(surroundString)) {
return expression.substring(surroundString.length(), expression.length() - surroundString.length());
}
return super.getProperty(expression);
}
}
}

View File

@ -57,8 +57,6 @@ public final class FileConfiguration extends ConfigurationImpl implements Deploy
setConfigurationUrl(url);
parseSystemProperties();
parsed = true;
}

View File

@ -2052,7 +2052,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
String uri = e.getAttribute("uri");
int retryInterval = getAttributeInteger(e, "retry-interval", 5000, Validators.GT_ZERO);
int reconnectAttemps = getAttributeInteger(e, "reconnect-attempts", -1, Validators.MINUS_ONE_OR_GT_ZERO);
int reconnectAttempts = getAttributeInteger(e, "reconnect-attempts", -1, Validators.MINUS_ONE_OR_GT_ZERO);
String user = getAttributeValue(e, "user");
String password = getAttributeValue(e, "password");
boolean autoStart = getBooleanAttribute(e, "auto-start", true);
@ -2061,7 +2061,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
AMQPBrokerConnectConfiguration config = new AMQPBrokerConnectConfiguration(name, uri);
config.parseURI();
config.setRetryInterval(retryInterval).setReconnectAttempts(reconnectAttemps).setUser(user).setPassword(password).setAutostart(autoStart);
config.setRetryInterval(retryInterval).setReconnectAttempts(reconnectAttempts).setUser(user).setPassword(password).setAutostart(autoStart);
mainConfig.addAMQPConnection(config);

View File

@ -961,4 +961,7 @@ public interface ActiveMQServer extends ServiceComponent {
BrokerBalancerManager getBalancerManager();
String validateUser(String username, String password, RemotingConnection connection, String securityDomain) throws Exception;
default void setProperties(String fileUrltoBrokerProperties) {
}
}

View File

@ -17,8 +17,11 @@
package org.apache.activemq.artemis.core.server.embedded;
import javax.management.MBeanServer;
import java.io.File;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.FileDeploymentManager;
import org.apache.activemq.artemis.core.config.impl.FileConfiguration;
@ -140,6 +143,11 @@ public class EmbeddedActiveMQ {
} else {
activeMQServer = new ActiveMQServerImpl(configuration, mbeanServer, securityManager);
}
URL brokerPropertiesFromClasspath = this.getClass().getClassLoader().getResource(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_SYSTEM_PROPERTY_NAME);
if (brokerPropertiesFromClasspath != null) {
activeMQServer.setProperties(new File(brokerPropertiesFromClasspath.toURI()).getAbsolutePath());
}
}
public EmbeddedActiveMQ stop() throws Exception {

View File

@ -369,6 +369,8 @@ public class ActiveMQServerImpl implements ActiveMQServer {
private volatile FederationManager federationManager;
private String propertiesFileUrl;
private final ActiveMQComponent networkCheckMonitor = new ActiveMQComponent() {
@Override
public void start() throws Exception {
@ -593,13 +595,18 @@ public class ActiveMQServerImpl implements ActiveMQServer {
return this.analyzer;
}
@Override
public void setProperties(String fileUrltoBrokerProperties) {
propertiesFileUrl = fileUrltoBrokerProperties;
}
private void internalStart() throws Exception {
if (state != SERVER_STATE.STOPPED) {
logger.debug("Server already started!");
return;
}
configuration.parseSystemProperties();
configuration.parseProperties(propertiesFileUrl);
initializeExecutorServices();
@ -4323,6 +4330,7 @@ public class ActiveMQServerImpl implements ActiveMQServer {
configuration.setQueueConfigs(config.getQueueConfigs());
configurationReloadDeployed.set(false);
if (isActive()) {
configuration.parseProperties(propertiesFileUrl);
deployReloadableConfigFromConfiguration();
}
}

View File

@ -48,6 +48,10 @@ public class ResourceLimitSettings implements Serializable, EncodingSupport {
// SimpleString queueNameRegex = null;
public void setName(String name) {
setMatch(SimpleString.toSimpleString(name));
}
public SimpleString getMatch() {
return match != null ? match : DEFAULT_MATCH;
}

View File

@ -24,7 +24,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.activemq.artemis.ArtemisConstants;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.amqpBrokerConnectivity.AMQPBrokerConnectConfiguration;
import org.apache.activemq.artemis.core.config.amqpBrokerConnectivity.AMQPBrokerConnectionAddressType;
import org.apache.activemq.artemis.core.config.amqpBrokerConnectivity.AMQPMirrorBrokerConnectionElement;
import org.apache.activemq.artemis.core.config.ha.LiveOnlyPolicyConfiguration;
import org.apache.activemq.artemis.core.server.JournalType;
import org.apache.activemq.artemis.core.server.plugin.impl.LoggingActiveMQServerPlugin;
@ -584,12 +588,127 @@ public class ConfigurationImplTest extends ActiveMQTestBase {
properties.put(configuration.getSystemPropertyPrefix() + "fileDeployerScanPeriod", "1234");
properties.put(configuration.getSystemPropertyPrefix() + "globalMaxSize", "4321");
configuration.parseSystemProperties(properties);
configuration.parsePrefixedProperties(properties, configuration.getSystemPropertyPrefix());
Assert.assertEquals(1234, configuration.getFileDeployerScanPeriod());
Assert.assertEquals(4321, configuration.getGlobalMaxSize());
}
@Test
public void testSetNestedPropertyOnCollections() throws Throwable {
ConfigurationImpl configuration = new ConfigurationImpl();
Properties properties = new Properties();
properties.put("balancerConfigurations.joe.localTargetFilter", "LF");
properties.put("balancerConfigurations(joe).targetKeyFilter", "TF");
properties.put("acceptorConfigurations.tcp.params.HOST", "LOCALHOST");
properties.put("acceptorConfigurations.tcp.params.PORT", "61616");
properties.put("acceptorConfigurations.invm.params.ID", "0");
// <amqp-connection uri="tcp://HOST:PORT" name="other-server" retry-interval="100" reconnect-attempts="-1" user="john" password="doe">
properties.put("AMQPConnections.other-server.uri", "tcp://HOST:PORT");
properties.put("AMQPConnections.other-server.retryInterval", "100");
properties.put("AMQPConnections.other-server.reconnectAttempts", "100");
properties.put("AMQPConnections.other-server.user", "john");
properties.put("AMQPConnections.other-server.password", "doe");
// <amqp-connection uri="tcp://brokerB:5672" name="brokerB"> <mirror/> </amqp-connection>
properties.put("AMQPConnections.brokerB.uri", "tcp://brokerB:5672");
properties.put("AMQPConnections.brokerB.type", AMQPBrokerConnectionAddressType.MIRROR.toString());
properties.put("AMQPConnections.brokerB.connectionElements.mirror.mirrorSNF", "mirrorSNFQueue");
properties.put("resourceLimitSettings.joe.maxConnections", "100");
configuration.parsePrefixedProperties(properties, null);
Assert.assertEquals(1, configuration.getBalancerConfigurations().size());
Assert.assertEquals("LF", configuration.getBalancerConfigurations().get(0).getLocalTargetFilter());
Assert.assertEquals("TF", configuration.getBalancerConfigurations().get(0).getTargetKeyFilter());
Assert.assertEquals(2, configuration.getAcceptorConfigurations().size());
for (TransportConfiguration acceptor : configuration.getAcceptorConfigurations()) {
if ("tcp".equals(acceptor.getName())) {
Assert.assertEquals("61616", acceptor.getParams().get("PORT"));
}
if ("invm".equals(acceptor.getName())) {
Assert.assertEquals("0", acceptor.getParams().get("ID"));
}
}
Assert.assertEquals(2, configuration.getAMQPConnection().size());
for (AMQPBrokerConnectConfiguration amqpBrokerConnectConfiguration : configuration.getAMQPConnection()) {
if ("brokerB".equals(amqpBrokerConnectConfiguration.getName())) {
Assert.assertEquals(AMQPBrokerConnectionAddressType.MIRROR.toString(), amqpBrokerConnectConfiguration.getConnectionElements().get(0).getType().toString());
Assert.assertEquals("mirrorSNFQueue", ((AMQPMirrorBrokerConnectionElement)amqpBrokerConnectConfiguration.getConnectionElements().get(0)).getMirrorSNF().toString());
} else if ("other-server".equals(amqpBrokerConnectConfiguration.getName())) {
Assert.assertEquals(100, amqpBrokerConnectConfiguration.getReconnectAttempts());
} else {
fail("unexpected amqp broker connection configuration: " + amqpBrokerConnectConfiguration.getName());
}
}
Assert.assertEquals(100, configuration.getResourceLimitSettings().get("joe").getMaxConnections());
}
@Test
public void testSetNestedPropertyOnExistingCollectionEntryViaMappedNotation() throws Throwable {
ConfigurationImpl configuration = new ConfigurationImpl();
Properties properties = new Properties();
properties.put("balancerConfigurations.joe.localTargetFilter", "LF");
// does not exist, ignored
properties.put("balancerConfigurations(bob).targetKeyFilter", "TF");
properties.put("balancerConfigurations(joe).targetKeyFilter", "TF");
configuration.parsePrefixedProperties(properties, null);
Assert.assertEquals(1, configuration.getBalancerConfigurations().size());
Assert.assertEquals("LF", configuration.getBalancerConfigurations().get(0).getLocalTargetFilter());
Assert.assertEquals("TF", configuration.getBalancerConfigurations().get(0).getTargetKeyFilter());
}
@Test
public void testAddressSettingsViaProperties() throws Throwable {
ConfigurationImpl configuration = new ConfigurationImpl();
Properties properties = new Properties();
properties.put("addressesSettings.#.expiryAddress", "sharedExpiry");
properties.put("addressesSettings.NeedToTrackExpired.expiryAddress", "important");
properties.put("addressesSettings.\"Name.With.Dots\".expiryAddress", "moreImportant");
configuration.parsePrefixedProperties(properties, null);
Assert.assertEquals(3, configuration.getAddressesSettings().size());
Assert.assertEquals(SimpleString.toSimpleString("sharedExpiry"), configuration.getAddressesSettings().get("#").getExpiryAddress());
Assert.assertEquals(SimpleString.toSimpleString("important"), configuration.getAddressesSettings().get("NeedToTrackExpired").getExpiryAddress());
Assert.assertEquals(SimpleString.toSimpleString("moreImportant"), configuration.getAddressesSettings().get("Name.With.Dots").getExpiryAddress());
}
@Test
public void testNameWithDotsSurroundWithDollarDollar() throws Throwable {
ConfigurationImpl configuration = new ConfigurationImpl();
Properties properties = new Properties();
// overrides the default surrounding string of '"' with '$$' using a property
properties.put(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_KEY_SURROUND_PROPERTY, "$$");
properties.put("addressesSettings.#.expiryAddress", "sharedExpiry");
properties.put("addressesSettings.NeedToTrackExpired.expiryAddress", "important");
properties.put("addressesSettings.$$Name.With.Dots$$.expiryAddress", "moreImportant");
configuration.parsePrefixedProperties(properties, null);
Assert.assertEquals(3, configuration.getAddressesSettings().size());
Assert.assertEquals(SimpleString.toSimpleString("sharedExpiry"), configuration.getAddressesSettings().get("#").getExpiryAddress());
Assert.assertEquals(SimpleString.toSimpleString("important"), configuration.getAddressesSettings().get("NeedToTrackExpired").getExpiryAddress());
Assert.assertEquals(SimpleString.toSimpleString("moreImportant"), configuration.getAddressesSettings().get("Name.With.Dots").getExpiryAddress());
}
/**
* To test ARTEMIS-926
* @throws Throwable
@ -632,7 +751,7 @@ public class ConfigurationImplTest extends ActiveMQTestBase {
properties.put(configuration.getSystemPropertyPrefix() + "fileDeployerScanPeriod", "1234");
properties.put(configuration.getSystemPropertyPrefix() + "globalMaxSize", "4321");
configuration.parseSystemProperties(properties);
configuration.parsePrefixedProperties(properties, configuration.getSystemPropertyPrefix());
} finally {

View File

@ -0,0 +1,87 @@
/*
* 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.server.config;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.Test;
public class JMSServerPropertyConfigTest extends ActiveMQTestBase {
@Test
public void testConfigViaBrokerPropertiesSystemProperty() throws Exception {
EmbeddedActiveMQ server = new EmbeddedActiveMQ();
ConfigurationImpl configuration = new ConfigurationImpl();
configuration.setJournalDirectory(new File(getTestDir(), "./journal").getAbsolutePath()).
setPagingDirectory(new File(getTestDir(), "./paging").getAbsolutePath()).
setLargeMessagesDirectory(new File(getTestDir(), "./largemessages").getAbsolutePath()).
setBindingsDirectory(new File(getTestDir(), "./bindings").getAbsolutePath()).setPersistenceEnabled(true);
File bindingsDir = new File(configuration.getBindingsDirectory());
bindingsDir.mkdirs();
File propertiesInBindingsDir = new File(bindingsDir, ActiveMQDefaultConfiguration.BROKER_PROPERTIES_SYSTEM_PROPERTY_NAME);
try (PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(propertiesInBindingsDir)))) {
// use the same name property as from the classpath broker.properties to verify precedence of system prop
out.println("name=nameFromCopiedPropertiesRefViaSystemProp");
}
System.setProperty(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_SYSTEM_PROPERTY_NAME, propertiesInBindingsDir.getAbsolutePath());
try {
server.setConfiguration(configuration);
server.start();
assertEquals("nameFromCopiedPropertiesRefViaSystemProp", server.getActiveMQServer().getConfiguration().getName());
} finally {
System.getProperties().remove(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_SYSTEM_PROPERTY_NAME);
server.stop();
}
}
@Test
public void testConfigViaBrokerPropertiesFromClasspath() throws Exception {
EmbeddedActiveMQ server = new EmbeddedActiveMQ();
ConfigurationImpl configuration = new ConfigurationImpl();
configuration.setJournalDirectory(new File(getTestDir(), "./journal").getAbsolutePath()).
setPagingDirectory(new File(getTestDir(), "./paging").getAbsolutePath()).
setLargeMessagesDirectory(new File(getTestDir(), "./largemessages").getAbsolutePath()).
setBindingsDirectory(new File(getTestDir(), "./bindings").getAbsolutePath()).setPersistenceEnabled(true);
try {
server.setConfiguration(configuration);
server.start();
assertEquals("ConfiguredViaProperties", server.getActiveMQServer().getConfiguration().getName());
} finally {
server.stop();
}
}
}

View File

@ -0,0 +1,18 @@
#
# 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.
#
name=ConfiguredViaProperties