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 c5d872575e ARTEMIS-4256 - support removal of configuration via 
properties
c5d872575e is described below

commit c5d872575e5ed08a3d5d13e6bcdac8eaf230e0b1
Author: Gary Tully <[email protected]>
AuthorDate: Thu May 4 15:50:40 2023 +0100

    ARTEMIS-4256 - support removal of configuration via properties
---
 .../api/config/ActiveMQDefaultConfiguration.java   |  8 +++
 .../core/config/impl/ConfigurationImpl.java        | 76 +++++++++++++++++++++-
 .../core/config/impl/ConfigurationImplTest.java    | 42 ++++++++++++
 docs/user-manual/en/configuration-index.md         |  1 +
 4 files changed, 124 insertions(+), 3 deletions(-)

diff --git 
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java
 
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java
index 05e8e7494d..69c1aba3c9 100644
--- 
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java
+++ 
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java
@@ -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;
    }
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 37db97b804..6d2b006ce0 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
@@ -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 {
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 064b552ddd..018ac4183d 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
@@ -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
diff --git a/docs/user-manual/en/configuration-index.md 
b/docs/user-manual/en/configuration-index.md
index 296552bf53..8f522934eb 100644
--- a/docs/user-manual/en/configuration-index.md
+++ b/docs/user-manual/en/configuration-index.md
@@ -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:
 

Reply via email to