ARTEMIS-3757 - allow system and env var substution of properties config, respect order of file loaded properties and add generic enum converter
This commit is contained in:
parent
4ff5d95b2c
commit
a739b9f068
|
@ -17,23 +17,6 @@
|
|||
|
||||
package org.apache.activemq.artemis.utils.critical;
|
||||
|
||||
import org.apache.activemq.artemis.utils.uri.BeanSupport;
|
||||
import org.apache.commons.beanutils.Converter;
|
||||
|
||||
public enum CriticalAnalyzerPolicy {
|
||||
HALT, SHUTDOWN, LOG;
|
||||
|
||||
static {
|
||||
// for URI support on ClusterConnection
|
||||
BeanSupport.registerConverter(new CriticalAnalyzerPolicyConverter(), CriticalAnalyzerPolicy.class);
|
||||
}
|
||||
|
||||
static class CriticalAnalyzerPolicyConverter implements Converter {
|
||||
|
||||
@Override
|
||||
public <T> T convert(Class<T> type, Object value) {
|
||||
return type.cast(CriticalAnalyzerPolicy.valueOf(value.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -30,6 +30,7 @@ import java.util.Properties;
|
|||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.beanutils.BeanUtilsBean;
|
||||
import org.apache.commons.beanutils.ConversionException;
|
||||
import org.apache.commons.beanutils.Converter;
|
||||
|
||||
public class BeanSupport {
|
||||
|
@ -40,6 +41,25 @@ public class BeanSupport {
|
|||
static {
|
||||
// This is to customize the BeanUtils to use Fluent Properties as well
|
||||
beanUtils.getPropertyUtils().addBeanIntrospector(new FluentPropertyBeanIntrospectorWithIgnores());
|
||||
|
||||
// generic converter from String to enum type via valueOf with no default
|
||||
registerConverter(new Converter() {
|
||||
@Override
|
||||
public <T> T convert(Class<T> type, Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (String.class.equals(type) || Object.class.equals(type)) {
|
||||
return type.cast(value.toString());
|
||||
}
|
||||
if (type.isEnum()) {
|
||||
return (T) Enum.valueOf((Class<Enum>)type, value.toString());
|
||||
}
|
||||
throw new ConversionException("Can't convert value '" + value
|
||||
+ "' to type " + type);
|
||||
}
|
||||
}, String.class);
|
||||
}
|
||||
|
||||
public static void registerConverter(Converter converter, Class type) {
|
||||
|
|
|
@ -52,6 +52,12 @@ public final class XMLUtil {
|
|||
// Utility class
|
||||
}
|
||||
|
||||
public static String CONSIDER_OS_ENV_PROP = "org.apache.activemq.artemis.utils.considerOsEnv";
|
||||
private static final boolean considerOsEnv;
|
||||
static {
|
||||
considerOsEnv = Boolean.parseBoolean(System.getProperty(CONSIDER_OS_ENV_PROP, "true"));
|
||||
}
|
||||
|
||||
public static Element streamToElement(InputStream inputStream) throws Exception {
|
||||
try (Reader reader = new InputStreamReader(inputStream)) {
|
||||
return XMLUtil.readerToElement(reader);
|
||||
|
@ -271,7 +277,14 @@ public final class XMLUtil {
|
|||
val = parts[1].trim();
|
||||
}
|
||||
|
||||
String sysProp = System.getProperty(prop, val);
|
||||
String sysProp = System.getProperty(prop);
|
||||
if (sysProp == null && considerOsEnv) {
|
||||
sysProp = System.getenv(prop);
|
||||
}
|
||||
if (sysProp == null) {
|
||||
sysProp = val;
|
||||
}
|
||||
// interesting choice to replace with val == "" with no match!
|
||||
logger.debug("replacing " + subString + " with " + sysProp);
|
||||
xml = xml.replace(subString, sysProp);
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
|
|||
import org.apache.activemq.artemis.utils.ByteUtil;
|
||||
import org.apache.activemq.artemis.utils.Env;
|
||||
import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
|
||||
import org.apache.activemq.artemis.utils.XMLUtil;
|
||||
import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy;
|
||||
import org.apache.activemq.artemis.utils.uri.BeanSupport;
|
||||
import org.apache.commons.beanutils.BeanUtilsBean;
|
||||
|
@ -464,13 +465,27 @@ public class ConfigurationImpl implements Configuration, Serializable {
|
|||
|
||||
@Override
|
||||
public Configuration parseProperties(String fileUrlToProperties) throws Exception {
|
||||
// system property overrides
|
||||
// system property overrides location of file(s)
|
||||
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);
|
||||
for (String fileUrl : fileUrlToProperties.split(",")) {
|
||||
Properties brokerProperties = new Properties() {
|
||||
final LinkedHashMap<Object, Object> orderedMap = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public Object put(Object key, Object value) {
|
||||
return orderedMap.put(key.toString(), value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||
return orderedMap.entrySet();
|
||||
}
|
||||
};
|
||||
try (FileInputStream fileInputStream = new FileInputStream(fileUrl); BufferedInputStream reader = new BufferedInputStream(fileInputStream)) {
|
||||
brokerProperties.load(reader);
|
||||
parsePrefixedProperties(brokerProperties, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
parsePrefixedProperties(System.getProperties(), systemPropertyPrefix);
|
||||
|
@ -478,7 +493,7 @@ public class ConfigurationImpl implements Configuration, Serializable {
|
|||
}
|
||||
|
||||
public void parsePrefixedProperties(Properties properties, String prefix) throws Exception {
|
||||
Map<String, Object> beanProperties = new HashMap<>();
|
||||
Map<String, Object> beanProperties = new LinkedHashMap<>();
|
||||
|
||||
synchronized (properties) {
|
||||
String key = null;
|
||||
|
@ -490,8 +505,10 @@ public class ConfigurationImpl implements Configuration, Serializable {
|
|||
}
|
||||
key = entry.getKey().toString().substring(prefix.length());
|
||||
}
|
||||
logger.debug("Setting up config, " + key + "=" + entry.getValue());
|
||||
beanProperties.put(key, entry.getValue());
|
||||
String value = XMLUtil.replaceSystemPropsInString(entry.getValue().toString());
|
||||
key = XMLUtil.replaceSystemPropsInString(key);
|
||||
logger.debug("Property config, " + key + "=" + value);
|
||||
beanProperties.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ public class ClusterConnectionControlImpl extends AbstractControl implements Clu
|
|||
}
|
||||
clearIO();
|
||||
try {
|
||||
return configuration.getMessageLoadBalancingType().getType();
|
||||
return configuration.getMessageLoadBalancingType().toString();
|
||||
} finally {
|
||||
blockOnIO();
|
||||
}
|
||||
|
|
|
@ -16,46 +16,6 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.core.server.cluster.impl;
|
||||
|
||||
import org.apache.activemq.artemis.utils.uri.BeanSupport;
|
||||
import org.apache.commons.beanutils.Converter;
|
||||
|
||||
public enum MessageLoadBalancingType {
|
||||
OFF("OFF"), STRICT("STRICT"), ON_DEMAND("ON_DEMAND"), OFF_WITH_REDISTRIBUTION("OFF_WITH_REDISTRIBUTION");
|
||||
|
||||
static {
|
||||
// for URI support on ClusterConnection
|
||||
BeanSupport.registerConverter(new MessageLoadBalancingTypeConverter(), MessageLoadBalancingType.class);
|
||||
}
|
||||
|
||||
static class MessageLoadBalancingTypeConverter implements Converter {
|
||||
|
||||
@Override
|
||||
public <T> T convert(Class<T> type, Object value) {
|
||||
return type.cast(MessageLoadBalancingType.getType(value.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
private String type;
|
||||
|
||||
MessageLoadBalancingType(final String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public static MessageLoadBalancingType getType(String string) {
|
||||
if (string.equals(OFF.getType())) {
|
||||
return MessageLoadBalancingType.OFF;
|
||||
} else if (string.equals(STRICT.getType())) {
|
||||
return MessageLoadBalancingType.STRICT;
|
||||
} else if (string.equals(ON_DEMAND.getType())) {
|
||||
return MessageLoadBalancingType.ON_DEMAND;
|
||||
} else if (string.equals(OFF_WITH_REDISTRIBUTION.getType())) {
|
||||
return MessageLoadBalancingType.OFF_WITH_REDISTRIBUTION;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
OFF, STRICT, ON_DEMAND, OFF_WITH_REDISTRIBUTION;
|
||||
}
|
||||
|
|
|
@ -17,9 +17,15 @@
|
|||
package org.apache.activemq.artemis.core.config.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.activemq.artemis.ArtemisConstants;
|
||||
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
|
||||
|
@ -31,9 +37,13 @@ import org.apache.activemq.artemis.core.config.amqpBrokerConnectivity.AMQPBroker
|
|||
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.cluster.impl.MessageLoadBalancingType;
|
||||
import org.apache.activemq.artemis.core.server.plugin.impl.LoggingActiveMQServerPlugin;
|
||||
import org.apache.activemq.artemis.core.server.routing.KeyType;
|
||||
import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
|
||||
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
|
||||
import org.apache.activemq.artemis.utils.RandomUtil;
|
||||
import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
@ -601,6 +611,7 @@ public class ConfigurationImplTest extends ActiveMQTestBase {
|
|||
Properties properties = new Properties();
|
||||
properties.put("connectionRouters.joe.localTargetFilter", "LF");
|
||||
properties.put("connectionRouters.joe.keyFilter", "TF");
|
||||
properties.put("connectionRouters.joe.keyType", "SOURCE_IP");
|
||||
|
||||
properties.put("acceptorConfigurations.tcp.params.HOST", "LOCALHOST");
|
||||
properties.put("acceptorConfigurations.tcp.params.PORT", "61616");
|
||||
|
@ -626,6 +637,7 @@ public class ConfigurationImplTest extends ActiveMQTestBase {
|
|||
Assert.assertEquals(1, configuration.getConnectionRouters().size());
|
||||
Assert.assertEquals("LF", configuration.getConnectionRouters().get(0).getLocalTargetFilter());
|
||||
Assert.assertEquals("TF", configuration.getConnectionRouters().get(0).getKeyFilter());
|
||||
Assert.assertEquals(KeyType.SOURCE_IP, configuration.getConnectionRouters().get(0).getKeyType());
|
||||
|
||||
Assert.assertEquals(2, configuration.getAcceptorConfigurations().size());
|
||||
|
||||
|
@ -708,6 +720,149 @@ public class ConfigurationImplTest extends ActiveMQTestBase {
|
|||
Assert.assertEquals(25 * 1024, configuration.getGlobalMaxSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemPropValueReplaced() throws Exception {
|
||||
ConfigurationImpl configuration = new ConfigurationImpl();
|
||||
Properties properties = new Properties();
|
||||
final String homeFromDefault = "default-home";
|
||||
final String homeFromEnv = System.getenv("HOME");
|
||||
properties.put("name", "${HOME:" + homeFromDefault + "}");
|
||||
configuration.parsePrefixedProperties(properties, null);
|
||||
if (homeFromEnv != null) {
|
||||
Assert.assertEquals(homeFromEnv, configuration.getName());
|
||||
} else {
|
||||
// if $HOME is not set for some platform
|
||||
Assert.assertEquals(homeFromDefault, configuration.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemPropValueNoMatch() throws Exception {
|
||||
ConfigurationImpl configuration = new ConfigurationImpl();
|
||||
Properties properties = new Properties();
|
||||
properties.put("name", "vv-${SOME_RANDOM_VV}");
|
||||
configuration.parsePrefixedProperties(properties, null);
|
||||
Assert.assertEquals("vv-", configuration.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemPropValueNonExistWithDefault() throws Exception {
|
||||
ConfigurationImpl configuration = new ConfigurationImpl();
|
||||
Properties properties = new Properties();
|
||||
properties.put("name", "vv-${SOME_RANDOM_VV:y}");
|
||||
configuration.parsePrefixedProperties(properties, null);
|
||||
Assert.assertEquals("vv-y", configuration.getName());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSystemPropKeyReplacement() throws Exception {
|
||||
ConfigurationImpl configuration = new ConfigurationImpl();
|
||||
Properties properties = new Properties();
|
||||
|
||||
final String newKeyName = RandomUtil.randomString();
|
||||
final String valueFromSysProp = "VV";
|
||||
System.setProperty(newKeyName, valueFromSysProp);
|
||||
|
||||
try {
|
||||
properties.put("connectorConfigurations.KEY-${" + newKeyName + "}.name", "y");
|
||||
configuration.parsePrefixedProperties(properties, null);
|
||||
Assert.assertNotNull("configured new key from prop", configuration.connectorConfigs.get("KEY-" + valueFromSysProp));
|
||||
Assert.assertEquals("y", configuration.connectorConfigs.get("KEY-" + valueFromSysProp).getName());
|
||||
} finally {
|
||||
System.clearProperty(newKeyName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnumConversion() throws Exception {
|
||||
ConfigurationImpl configuration = new ConfigurationImpl();
|
||||
Properties properties = new Properties();
|
||||
properties.put("clusterConfiguration.cc.name", "cc");
|
||||
properties.put("clusterConfigurations.cc.messageLoadBalancingType", "OFF_WITH_REDISTRIBUTION");
|
||||
properties.put("criticalAnalyzerPolicy", "SHUTDOWN");
|
||||
|
||||
configuration.parsePrefixedProperties(properties, null);
|
||||
|
||||
Assert.assertEquals("cc", configuration.getClusterConfigurations().get(0).getName());
|
||||
Assert.assertEquals(MessageLoadBalancingType.OFF_WITH_REDISTRIBUTION, configuration.getClusterConfigurations().get(0).getMessageLoadBalancingType());
|
||||
Assert.assertEquals(CriticalAnalyzerPolicy.SHUTDOWN, configuration.getCriticalAnalyzerPolicy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertiesReaderRespectsOrderFromFile() throws Exception {
|
||||
|
||||
File tmpFile = File.createTempFile("ordered-props-test", "");
|
||||
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
|
||||
PrintWriter printWriter = new PrintWriter(fileOutputStream);
|
||||
|
||||
LinkedList<String> insertionOrderedKeys = new LinkedList<>();
|
||||
char ascii = 'a';
|
||||
for (int i = 0; i < 26; i++, ascii++) {
|
||||
printWriter.println("resourceLimitSettings." + i + ".maxConnections=100");
|
||||
insertionOrderedKeys.addLast(String.valueOf(i));
|
||||
|
||||
printWriter.println("resourceLimitSettings." + ascii + ".maxConnections=100");
|
||||
insertionOrderedKeys.addLast(String.valueOf(ascii));
|
||||
}
|
||||
printWriter.flush();
|
||||
fileOutputStream.flush();
|
||||
fileOutputStream.close();
|
||||
|
||||
final AtomicReference<String> errorAt = new AtomicReference<>();
|
||||
ConfigurationImpl configuration = new ConfigurationImpl();
|
||||
configuration.setResourceLimitSettings(new HashMap<String, ResourceLimitSettings>() {
|
||||
@Override
|
||||
public ResourceLimitSettings put(String key, ResourceLimitSettings value) {
|
||||
if (!(key.equals(insertionOrderedKeys.remove()))) {
|
||||
errorAt.set(key);
|
||||
fail("Expected to see props applied in insertion order!, errorAt:" + errorAt.get());
|
||||
}
|
||||
return super.put(key, value);
|
||||
}
|
||||
});
|
||||
configuration.parseProperties(tmpFile.getAbsolutePath());
|
||||
assertNull("no errors in insertion order, errorAt:" + errorAt.get(), errorAt.get());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testPropertiesFiles() throws Exception {
|
||||
|
||||
LinkedList<String> files = new LinkedList<>();
|
||||
LinkedList<String> names = new LinkedList<>();
|
||||
names.addLast("one");
|
||||
names.addLast("two");
|
||||
|
||||
for (String suffix : names) {
|
||||
File tmpFile = File.createTempFile("props-test", suffix);
|
||||
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
|
||||
PrintWriter printWriter = new PrintWriter(fileOutputStream);
|
||||
|
||||
printWriter.println("name=" + suffix);
|
||||
|
||||
printWriter.flush();
|
||||
fileOutputStream.flush();
|
||||
fileOutputStream.close();
|
||||
|
||||
files.addLast(tmpFile.getAbsolutePath());
|
||||
}
|
||||
final AtomicReference<String> errorAt = new AtomicReference<>();
|
||||
ConfigurationImpl configuration = new ConfigurationImpl() {
|
||||
@Override
|
||||
public ConfigurationImpl setName(String name) {
|
||||
if (!(name.equals(names.remove()))) {
|
||||
fail("Expected names from files in order");
|
||||
}
|
||||
return super.setName(name);
|
||||
}
|
||||
};
|
||||
configuration.parseProperties(files.stream().collect(Collectors.joining(",")));
|
||||
assertEquals("second won", "two", configuration.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNameWithDotsSurroundWithDollarDollar() throws Throwable {
|
||||
ConfigurationImpl configuration = new ConfigurationImpl();
|
||||
|
|
|
@ -75,7 +75,7 @@ public class ClusterConnectionControlTest extends ManagementTestBase {
|
|||
Assert.assertEquals(clusterConnectionConfig1.getDiscoveryGroupName(), clusterConnectionControl.getDiscoveryGroupName());
|
||||
Assert.assertEquals(clusterConnectionConfig1.getRetryInterval(), clusterConnectionControl.getRetryInterval());
|
||||
Assert.assertEquals(clusterConnectionConfig1.isDuplicateDetection(), clusterConnectionControl.isDuplicateDetection());
|
||||
Assert.assertEquals(clusterConnectionConfig1.getMessageLoadBalancingType().getType(), clusterConnectionControl.getMessageLoadBalancingType());
|
||||
Assert.assertEquals(clusterConnectionConfig1.getMessageLoadBalancingType().toString(), clusterConnectionControl.getMessageLoadBalancingType());
|
||||
Assert.assertEquals(clusterConnectionConfig1.getMaxHops(), clusterConnectionControl.getMaxHops());
|
||||
Assert.assertEquals(0L, clusterConnectionControl.getMessagesPendingAcknowledgement());
|
||||
Assert.assertEquals(0L, clusterConnectionControl.getMessagesAcknowledged());
|
||||
|
@ -111,7 +111,7 @@ public class ClusterConnectionControlTest extends ManagementTestBase {
|
|||
Assert.assertEquals(clusterConnectionConfig2.getDiscoveryGroupName(), clusterConnectionControl.getDiscoveryGroupName());
|
||||
Assert.assertEquals(clusterConnectionConfig2.getRetryInterval(), clusterConnectionControl.getRetryInterval());
|
||||
Assert.assertEquals(clusterConnectionConfig2.isDuplicateDetection(), clusterConnectionControl.isDuplicateDetection());
|
||||
Assert.assertEquals(clusterConnectionConfig2.getMessageLoadBalancingType().getType(), clusterConnectionControl.getMessageLoadBalancingType());
|
||||
Assert.assertEquals(clusterConnectionConfig2.getMessageLoadBalancingType().toString(), clusterConnectionControl.getMessageLoadBalancingType());
|
||||
Assert.assertEquals(clusterConnectionConfig2.getMaxHops(), clusterConnectionControl.getMaxHops());
|
||||
|
||||
Object[] connectorPairs = clusterConnectionControl.getStaticConnectors();
|
||||
|
|
Loading…
Reference in New Issue