Skip to content

Commit

Permalink
ARTEMIS-4256 - support removal of configuration via properties
Browse files Browse the repository at this point in the history
  • Loading branch information
gtully committed May 5, 2023
1 parent cf3afc4 commit c5d8725
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -569,8 +569,12 @@ public static String getDefaultHapolicyBackupStrategy() {

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;
Expand Down Expand Up @@ -1625,6 +1629,10 @@ public static String getDefaultBrokerPropertiesKeySurround() {
return BROKER_PROPERTIES_KEY_SURROUND;
}

public static String getDefaultBrokerPropertiesRemoveValue() {
return BROKER_PROPERTIES_REMOVE_VALUE;
}

public static String getDefaultNetworkCheckList() {
return DEFAULT_NETWORK_CHECK_LIST;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -502,6 +505,14 @@ public void setBrokerPropertiesKeySurround(String brokerPropertiesKeySurround) {
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)
Expand Down Expand Up @@ -588,7 +599,7 @@ public void parsePrefixedProperties(Object target, String name, Properties prope
}

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
Expand All @@ -613,8 +624,38 @@ public void setProperty(final Object bean, String name, final Object value) thro
}
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)
Expand Down Expand Up @@ -814,12 +855,20 @@ private synchronized void updateReadPropertiesStatus(String propsId, long alder3

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();
Expand Down Expand Up @@ -3077,8 +3126,13 @@ private static class CollectionAutoFillPropertiesUtil extends PropertyUtilsBean

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
Expand Down Expand Up @@ -3144,6 +3198,18 @@ private Object findByNameProperty(String key, Collection collection) throws Invo
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
Expand Down Expand Up @@ -3257,6 +3323,10 @@ public void setBeanUtilsBean(BeanUtilsBean beanUtilsBean) {
// we want type conversion
this.beanUtilsBean = beanUtilsBean;
}

public boolean isRemoveValue(Object value) {
return removeValue != null && removeValue.equals(value);
}
}

private static class SurroundResolver extends DefaultResolver {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,46 @@ public void testAddressViaProperties() throws Throwable {
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();
Expand Down Expand Up @@ -1639,6 +1679,8 @@ public void testNameWithDotsSurroundWithDollarDollar() throws Throwable {
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
Expand Down
1 change: 1 addition & 0 deletions docs/user-manual/en/configuration-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down

0 comments on commit c5d8725

Please sign in to comment.