ARTEMIS-4256 - support removal of configuration via properties

This commit is contained in:
Gary Tully 2023-05-04 15:50:40 +01:00
parent cf3afc4096
commit c5d872575e
4 changed files with 124 additions and 3 deletions

View File

@ -569,8 +569,12 @@ public final class ActiveMQDefaultConfiguration {
public static final String BROKER_PROPERTIES_KEY_SURROUND = "\"";
public static final String BROKER_PROPERTIES_REMOVE_VALUE = "-";
public static final String BROKER_PROPERTIES_KEY_SURROUND_PROPERTY = "key.surround";
public static final String BROKER_PROPERTIES_REMOVE_VALUE_PROPERTY = "remove.value";
public static String DEFAULT_NETWORK_CHECK_LIST = null;
public static String DEFAULT_NETWORK_CHECK_URL_LIST = null;
@ -1625,6 +1629,10 @@ public final class ActiveMQDefaultConfiguration {
return BROKER_PROPERTIES_KEY_SURROUND;
}
public static String getDefaultBrokerPropertiesRemoveValue() {
return BROKER_PROPERTIES_REMOVE_VALUE;
}
public static String getDefaultNetworkCheckList() {
return DEFAULT_NETWORK_CHECK_LIST;
}

View File

@ -42,6 +42,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -379,6 +380,8 @@ public class ConfigurationImpl implements Configuration, Serializable {
private String brokerPropertiesKeySurround = ActiveMQDefaultConfiguration.getDefaultBrokerPropertiesKeySurround();
private String brokerPropertiesRemoveValue = ActiveMQDefaultConfiguration.getDefaultBrokerPropertiesRemoveValue();
private String networkCheckList = ActiveMQDefaultConfiguration.getDefaultNetworkCheckList();
private String networkURLList = ActiveMQDefaultConfiguration.getDefaultNetworkCheckURLList();
@ -502,6 +505,14 @@ public class ConfigurationImpl implements Configuration, Serializable {
this.brokerPropertiesKeySurround = brokerPropertiesKeySurround;
}
public String getBrokerPropertiesRemoveValue() {
return brokerPropertiesRemoveValue;
}
public void setBrokerPropertiesRemoveValue(String brokerPropertiesRemoveValue) {
this.brokerPropertiesRemoveValue = brokerPropertiesRemoveValue;
}
@Override
public Configuration parseProperties(String fileUrlToProperties) throws Exception {
// system property overrides location of file(s)
@ -588,7 +599,7 @@ public class ConfigurationImpl implements Configuration, Serializable {
}
public void populateWithProperties(final Object target, final String propsId, Map<String, Object> beanProperties) throws InvocationTargetException, IllegalAccessException {
CollectionAutoFillPropertiesUtil autoFillCollections = new CollectionAutoFillPropertiesUtil();
CollectionAutoFillPropertiesUtil autoFillCollections = new CollectionAutoFillPropertiesUtil(getBrokerPropertiesRemoveValue(beanProperties));
BeanUtilsBean beanUtils = new BeanUtilsBean(new ConvertUtilsBean(), autoFillCollections) {
// override to treat missing properties as errors, not skip as the default impl does
@Override
@ -613,8 +624,38 @@ public class ConfigurationImpl implements Configuration, Serializable {
}
logger.trace("resolved target, bean: {}, name: {}", target.getClass(), name);
// Declare local variables we will require
final String propName = resolver.getProperty(name); // Simple name of target property
if (autoFillCollections.isRemoveValue(value)) {
logger.trace("removing from target, bean: {}, name: {}", target.getClass(), name);
// we may do a further get but no longer want to reference our nested collection stack
if (!autoFillCollections.collections.isEmpty()) {
autoFillCollections.collections.pop();
}
if (target instanceof Map) {
Map targetMap = (Map) target;
Iterator<Map.Entry<String, Object>> i = targetMap.entrySet().iterator();
while (i.hasNext()) {
String key = i.next().getKey();
if (propName.equals(key)) {
i.remove();
break;
}
}
} else if (target instanceof Collection) {
try {
autoFillCollections.removeByNameProperty(propName, (Collection) target);
} catch (NoSuchMethodException e) {
throw new InvocationTargetException(e, "Can only remove named entries from collections or maps" + name + ", on: " + target);
}
} else {
throw new InvocationTargetException(null, "Can only remove entries from collections or maps" + name + ", on: " + target);
}
logger.trace("removed from target, bean: {}, name: {}", target.getClass(), name);
return;
}
Class<?> type = null; // Java type of target property
final int index = resolver.getIndex(name); // Indexed subscript value (if any)
final String key = resolver.getKey(name); // Mapped key value (if any)
@ -814,12 +855,20 @@ public class ConfigurationImpl implements Configuration, Serializable {
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));
return String.valueOf(propertiesToApply.remove(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_KEY_SURROUND_PROPERTY));
} else {
return System.getProperty(getSystemPropertyPrefix() + ActiveMQDefaultConfiguration.BROKER_PROPERTIES_KEY_SURROUND_PROPERTY, getBrokerPropertiesKeySurround());
}
}
private String getBrokerPropertiesRemoveValue(Map<String, Object> propertiesToApply) {
if (propertiesToApply.containsKey(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_REMOVE_VALUE_PROPERTY)) {
return String.valueOf(propertiesToApply.remove(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_REMOVE_VALUE_PROPERTY));
} else {
return System.getProperty(getSystemPropertyPrefix() + ActiveMQDefaultConfiguration.BROKER_PROPERTIES_REMOVE_VALUE_PROPERTY, getBrokerPropertiesRemoveValue());
}
}
@Override
public boolean isClustered() {
return !getClusterConfigurations().isEmpty();
@ -3077,8 +3126,13 @@ public class ConfigurationImpl implements Configuration, Serializable {
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[]{};
final Stack<Pair<String, Object>> collections = new Stack<>();
final String removeValue;
private BeanUtilsBean beanUtilsBean;
CollectionAutoFillPropertiesUtil(String brokerPropertiesRemoveValue) {
this.removeValue = brokerPropertiesRemoveValue;
}
@Override
public void setProperty(final Object bean, final String name, final Object value) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// any set will invalidate our collections stack
@ -3144,6 +3198,18 @@ public class ConfigurationImpl implements Configuration, Serializable {
return null;
}
private Object removeByNameProperty(String key, Collection collection) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// locate on name property, may be a SimpleString
for (Object candidate : collection) {
Object candidateName = getProperty(candidate, "name");
if (candidateName != null && key.equals(candidateName.toString())) {
collection.remove(candidate);
break;
}
}
return null;
}
// allow finding beans in collections via name() such that a mapped key (key)
// can be used to access and *not* auto create entries
@Override
@ -3257,6 +3323,10 @@ public class ConfigurationImpl implements Configuration, Serializable {
// we want type conversion
this.beanUtilsBean = beanUtilsBean;
}
public boolean isRemoveValue(Object value) {
return removeValue != null && removeValue.equals(value);
}
}
private static class SurroundResolver extends DefaultResolver {

View File

@ -967,6 +967,46 @@ public class ConfigurationImplTest extends ActiveMQTestBase {
Assert.assertEquals(false, configuration.getAddressConfigurations().get(0).getQueueConfigs().get(0).isDurable());
}
@Test
public void testAddressRemovalViaProperties() throws Throwable {
ConfigurationImpl configuration = new ConfigurationImpl();
Properties properties = new Properties();
properties.put("addressConfigurations.\"LB.TEST\".queueConfigs.\"LB.TEST\".routingType", "ANYCAST");
configuration.parsePrefixedProperties(properties, null);
Assert.assertEquals(1, configuration.getAddressConfigurations().size());
Assert.assertEquals(1, configuration.getAddressConfigurations().get(0).getQueueConfigs().size());
Assert.assertTrue(configuration.getStatus().contains("\"errors\":[]"));
properties.clear();
properties.put("addressConfigurations.\"LB.TEST\"", "-");
configuration.parsePrefixedProperties(properties, null);
Assert.assertEquals(0, configuration.getAddressConfigurations().size());
Assert.assertTrue(configuration.getStatus().contains("\"errors\":[]"));
}
@Test
public void testRoleRemovalViaCustomRemoveProperties() throws Throwable {
ConfigurationImpl configuration = new ConfigurationImpl();
Properties properties = new Properties();
properties.put("securityRoles.TEST.users.send", "true");
configuration.parsePrefixedProperties(properties, null);
Assert.assertEquals(1, configuration.getSecurityRoles().size());
Assert.assertTrue(configuration.getStatus().contains("\"errors\":[]"));
properties.clear();
properties.put(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_REMOVE_VALUE_PROPERTY, "^");
properties.put("securityRoles.TEST", "^");
configuration.parsePrefixedProperties(properties, null);
Assert.assertEquals(0, configuration.getSecurityRoles().size());
Assert.assertTrue(configuration.getStatus().contains("\"errors\":[]"));
}
@Test
public void testIDCacheSizeViaProperties() throws Throwable {
ConfigurationImpl configuration = new ConfigurationImpl();
@ -1639,6 +1679,8 @@ public class ConfigurationImplTest extends ActiveMQTestBase {
Assert.assertEquals(SimpleString.toSimpleString("sharedExpiry"), configuration.getAddressSettings().get("#").getExpiryAddress());
Assert.assertEquals(SimpleString.toSimpleString("important"), configuration.getAddressSettings().get("NeedToTrackExpired").getExpiryAddress());
Assert.assertEquals(SimpleString.toSimpleString("moreImportant"), configuration.getAddressSettings().get("Name.With.Dots").getExpiryAddress());
Assert.assertTrue(configuration.getStatus().contains("\"errors\":[]"));
}
@Test

View File

@ -91,6 +91,7 @@ reflect the camelCase java naming convention.
Collections need some special treatment to allow additions and reference. We utilise the name attribute of configuration
entities to find existing entries and when populating new entities, we set the name to match the requested key.
Removal of configuration from named collections is supported by setting a key value to "-". The remove match value can be configured with a property key "remove.value".
For example, a properties file containing: