This is an automated email from the ASF dual-hosted git repository.

robbie 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 4d8ccc4b2f ARTEMIS-4955 Support broker properties from JSON files
4d8ccc4b2f is described below

commit 4d8ccc4b2f31428b9caddad20643430e30c0cf32
Author: Domenico Francesco Bruscino <[email protected]>
AuthorDate: Fri May 17 09:38:15 2024 +0200

    ARTEMIS-4955 Support broker properties from JSON files
---
 .../core/config/impl/ConfigurationImpl.java        |  73 ++++++++--
 .../core/config/impl/ConfigurationImplTest.java    | 162 +++++++++++++++++++++
 2 files changed, 219 insertions(+), 16 deletions(-)

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 9f4ac3ccfb..2a532d16bb 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
@@ -18,14 +18,16 @@ package org.apache.activemq.artemis.core.config.impl;
 
 import java.beans.IndexedPropertyDescriptor;
 import java.beans.PropertyDescriptor;
-import java.io.BufferedInputStream;
+import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.PrintWriter;
+import java.io.Reader;
 import java.io.Serializable;
 import java.io.StringWriter;
 import java.lang.invoke.MethodHandles;
@@ -113,6 +115,7 @@ import 
org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
 import org.apache.activemq.artemis.json.JsonArrayBuilder;
 import org.apache.activemq.artemis.json.JsonObject;
 import org.apache.activemq.artemis.json.JsonObjectBuilder;
+import org.apache.activemq.artemis.json.JsonValue;
 import org.apache.activemq.artemis.utils.ByteUtil;
 import org.apache.activemq.artemis.utils.ClassloadingUtil;
 import org.apache.activemq.artemis.utils.Env;
@@ -559,31 +562,20 @@ public class ConfigurationImpl implements Configuration, 
Serializable {
       fileUrlToProperties = resolvePropertiesSources(fileUrlToProperties);
       if (fileUrlToProperties != null) {
          for (String fileUrl : fileUrlToProperties.split(",")) {
-            Properties brokerProperties = new InsertionOrderedProperties();
             if (fileUrl.endsWith("/")) {
                // treat as a directory and parse every property file in 
alphabetical order
                File dir = new File(fileUrl);
                if (dir.exists()) {
-                  String[] files = dir.list((file, s) -> 
s.endsWith(".properties"));
+                  String[] files = dir.list((file, s) -> s.endsWith(".json") 
|| s.endsWith(".properties"));
                   if (files != null && files.length > 0) {
                      Arrays.sort(files);
                      for (String fileName : files) {
-                        try (FileInputStream fileInputStream = new 
FileInputStream(new File(dir, fileName));
-                             BufferedInputStream reader = new 
BufferedInputStream(fileInputStream)) {
-                           brokerProperties.clear();
-                           brokerProperties.load(reader);
-                           parsePrefixedProperties(this, fileName, 
brokerProperties, null);
-                        }
+                        parseFileProperties(new File(dir, fileName));
                      }
                   }
                }
             } else {
-               File file = new File(fileUrl);
-               try (FileInputStream fileInputStream = new 
FileInputStream(file);
-                    BufferedInputStream reader = new 
BufferedInputStream(fileInputStream)) {
-                  brokerProperties.load(reader);
-                  parsePrefixedProperties(this, file.getName(), 
brokerProperties, null);
-               }
+               parseFileProperties(new File(fileUrl));
             }
          }
       }
@@ -591,6 +583,20 @@ public class ConfigurationImpl implements Configuration, 
Serializable {
       return this;
    }
 
+   private void parseFileProperties(File file) throws Exception {
+      InsertionOrderedProperties brokerProperties = new 
InsertionOrderedProperties();
+      try (FileReader fileReader = new FileReader(file);
+           BufferedReader reader = new BufferedReader(fileReader)) {
+         if (file.getName().endsWith(".json")) {
+            brokerProperties.loadJson(reader);
+         } else {
+            brokerProperties.load(reader);
+         }
+      }
+
+      parsePrefixedProperties(this, file.getName(), brokerProperties, null);
+   }
+
    public void parsePrefixedProperties(Properties properties, String prefix) 
throws Exception {
       parsePrefixedProperties(this, "system-" + prefix, properties, prefix);
    }
@@ -3691,5 +3697,40 @@ public class ConfigurationImpl implements Configuration, 
Serializable {
       public void clear() {
          orderedMap.clear();
       }
+
+      public synchronized boolean loadJson(Reader reader) throws IOException {
+         JsonObject jsonObject = JsonLoader.readObject(reader);
+
+         loadJsonObject("", jsonObject);
+
+         return true;
+      }
+
+      private void loadJsonObject(String parentKey, JsonObject jsonObject) {
+         
jsonObject.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(jsonEntry
 -> {
+            JsonValue jsonValue = jsonEntry.getValue();
+            JsonValue.ValueType jsonValueType = jsonValue.getValueType();
+            String jsonKey = jsonEntry.getKey();
+            if (jsonKey.contains(".")) {
+               jsonKey = "\"" + jsonKey + "\"";
+            }
+            String propertyKey = parentKey + jsonKey;
+            switch (jsonValueType) {
+               case OBJECT:
+                  loadJsonObject(propertyKey + ".", jsonValue.asJsonObject());
+                  break;
+               case STRING:
+                  put(propertyKey, jsonObject.getString(jsonKey));
+                  break;
+               case NUMBER:
+               case TRUE:
+               case FALSE:
+                  put(propertyKey, jsonValue.toString());
+                  break;
+               default:
+                  throw new IllegalStateException("JSON value type not 
supported: " + jsonValueType);
+            }
+         });
+      }
    }
 }
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 43a1ac25e7..dacb05e892 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
@@ -28,12 +28,15 @@ import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.PrintWriter;
+import java.io.StringReader;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Method;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Random;
@@ -93,7 +96,10 @@ import 
org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy;
 import org.apache.activemq.artemis.core.settings.impl.DeletionPolicy;
 import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
 import 
org.apache.activemq.artemis.core.settings.impl.SlowConsumerThresholdMeasurementUnit;
+import org.apache.activemq.artemis.json.JsonObject;
 import org.apache.activemq.artemis.logs.AssertionLoggerHandler;
+import org.apache.activemq.artemis.json.JsonObjectBuilder;
+import org.apache.activemq.artemis.utils.JsonLoader;
 import org.apache.activemq.artemis.utils.RandomUtil;
 import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy;
 import org.apache.commons.lang3.ClassUtils;
@@ -1912,6 +1918,162 @@ public class ConfigurationImplTest extends 
AbstractConfigurationTestBase {
       assertEquals(CriticalAnalyzerPolicy.SHUTDOWN, 
configuration.getCriticalAnalyzerPolicy());
    }
 
+   @Test
+   public void testJsonInsertionOrderedProperties() throws Exception {
+      ConfigurationImpl.InsertionOrderedProperties properties =
+         new ConfigurationImpl.InsertionOrderedProperties();
+
+      JsonObject configJsonObject = buildSimpleConfigJsonObject();
+      try (StringReader stringReader = new 
StringReader(configJsonObject.toString())) {
+         properties.loadJson(stringReader);
+      }
+
+      List<String> keys = new ArrayList<>();
+      properties.entrySet().forEach(entry -> keys.add((String) 
entry.getKey()));
+
+      List<String> sortedKeys = 
keys.stream().sorted().collect(Collectors.toList());
+      for (int i = 0; i < sortedKeys.size(); i++) {
+         assertEquals(i, keys.indexOf(sortedKeys.get(i)));
+      }
+   }
+
+   @Test
+   public void testTextPropertiesReaderFromFile() throws Exception {
+      List<String> textProperties = buildSimpleConfigTextList();
+      File tmpFile = File.createTempFile("text-props-test", "", 
temporaryFolder);
+      try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
+           PrintWriter printWriter = new PrintWriter(fileOutputStream)) {
+         for (String textProperty : textProperties) {
+            printWriter.println(textProperty);
+         }
+      }
+
+      ConfigurationImpl configuration = new ConfigurationImpl();
+      configuration.parseProperties(tmpFile.getAbsolutePath());
+
+      testSimpleConfig(configuration);
+   }
+
+   @Test
+   public void testJsonPropertiesReaderFromFile() throws Exception {
+
+      JsonObject configJsonObject = buildSimpleConfigJsonObject();
+      File tmpFile = File.createTempFile("json-props-test", ".json", 
temporaryFolder);
+      try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
+           PrintWriter printWriter = new PrintWriter(fileOutputStream)) {
+         printWriter.write(configJsonObject.toString());
+      }
+
+      ConfigurationImpl configuration = new ConfigurationImpl();
+      configuration.parseProperties(tmpFile.getAbsolutePath());
+
+      testSimpleConfig(configuration);
+   }
+
+   @Test
+   public void testInvalidJsonPropertiesReaderFromFile() throws Exception {
+
+      File tmpFile = File.createTempFile("json-props-test", ".json", 
temporaryFolder);
+      try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
+           PrintWriter printWriter = new PrintWriter(fileOutputStream)) {
+         printWriter.write("INVALID_JSON");
+      }
+
+      ConfigurationImpl configuration = new ConfigurationImpl();
+
+      try {
+         configuration.parseProperties(tmpFile.getAbsolutePath());
+         fail("Expected JSON parsing exception.");
+      } catch (Exception e) {
+      }
+   }
+
+   private JsonObject buildSimpleConfigJsonObject() {
+      JsonObjectBuilder configObjectBuilder = JsonLoader.createObjectBuilder();
+      {
+         configObjectBuilder.add("globalMaxSize", "25K");
+         configObjectBuilder.add("gracefulShutdownEnabled", true);
+         configObjectBuilder.add("securityEnabled", false);
+         configObjectBuilder.add("maxRedeliveryRecords", 123);
+
+         JsonObjectBuilder addressConfigObjectBuilder = 
JsonLoader.createObjectBuilder();
+         {
+            JsonObjectBuilder lbaObjectBuilder = 
JsonLoader.createObjectBuilder();
+            {
+               JsonObjectBuilder queueConfigBuilder = 
JsonLoader.createObjectBuilder();
+               {
+                  JsonObjectBuilder lbqObjectBuilder = 
JsonLoader.createObjectBuilder();
+                  {
+                     lbqObjectBuilder.add("routingType", "ANYCAST");
+                     lbqObjectBuilder.add("durable", false);
+                  }
+                  queueConfigBuilder.add("LB.TEST", lbqObjectBuilder.build());
+
+                  JsonObjectBuilder myqObjectBuilder = 
JsonLoader.createObjectBuilder();
+                  {
+                     myqObjectBuilder.add("routingType", "ANYCAST");
+                     myqObjectBuilder.add("durable", false);
+                  }
+                  queueConfigBuilder.add("my queue", myqObjectBuilder.build());
+               }
+               lbaObjectBuilder.add("queueConfigs", 
queueConfigBuilder.build());
+            }
+            addressConfigObjectBuilder.add("LB.TEST", 
lbaObjectBuilder.build());
+         }
+         configObjectBuilder.add("addressConfigurations", 
addressConfigObjectBuilder.build());
+
+         JsonObjectBuilder clusterConfigObjectBuilder = 
JsonLoader.createObjectBuilder();
+         {
+            JsonObjectBuilder ccObjectBuilder = 
JsonLoader.createObjectBuilder();
+            {
+               ccObjectBuilder.add("name", "cc");
+               ccObjectBuilder.add("messageLoadBalancingType", 
"OFF_WITH_REDISTRIBUTION");
+            }
+            clusterConfigObjectBuilder.add("cc", ccObjectBuilder.build());
+         }
+         configObjectBuilder.add("clusterConfigurations", 
clusterConfigObjectBuilder.build());
+
+         configObjectBuilder.add("criticalAnalyzerPolicy", "SHUTDOWN");
+      }
+
+      return configObjectBuilder.build();
+   }
+
+   private List<String> buildSimpleConfigTextList() {
+      List<String> textProperties = new ArrayList<>();
+      
textProperties.add("addressConfigurations.\"LB.TEST\".queueConfigs.\"LB.TEST\".routingType=ANYCAST");
+      
textProperties.add("addressConfigurations.\"LB.TEST\".queueConfigs.\"LB.TEST\".durable=false");
+      textProperties.add("addressConfigurations.\"LB.TEST\".queueConfigs.my\\ 
queue.routingType=ANYCAST");
+      textProperties.add("addressConfigurations.\"LB.TEST\".queueConfigs.my\\ 
queue.durable=false");
+      textProperties.add("globalMaxSize=25K");
+      textProperties.add("gracefulShutdownEnabled=true");
+      textProperties.add("securityEnabled=false");
+      textProperties.add("maxRedeliveryRecords=123");
+      textProperties.add("clusterConfigurations.cc.name=cc");
+      
textProperties.add("clusterConfigurations.cc.messageLoadBalancingType=OFF_WITH_REDISTRIBUTION");
+      textProperties.add("criticalAnalyzerPolicy=SHUTDOWN");
+
+      return textProperties;
+   }
+
+   private void testSimpleConfig(Configuration configuration) {
+      assertEquals(25 * 1024, configuration.getGlobalMaxSize());
+      assertEquals(true, configuration.isGracefulShutdownEnabled());
+      assertEquals(false, configuration.isSecurityEnabled());
+      assertEquals(123, configuration.getMaxRedeliveryRecords());
+
+      assertEquals(1, configuration.getAddressConfigurations().size());
+      assertEquals(2, 
configuration.getAddressConfigurations().get(0).getQueueConfigs().size());
+      assertEquals(SimpleString.of("LB.TEST"), 
configuration.getAddressConfigurations().get(0).getQueueConfigs().get(0).getAddress());
+      assertEquals(false, 
configuration.getAddressConfigurations().get(0).getQueueConfigs().get(0).isDurable());
+      assertEquals(SimpleString.of("my queue"), 
configuration.getAddressConfigurations().get(0).getQueueConfigs().get(1).getAddress());
+      assertEquals(false, 
configuration.getAddressConfigurations().get(0).getQueueConfigs().get(1).isDurable());
+
+      assertEquals("cc", 
configuration.getClusterConfigurations().get(0).getName());
+      assertEquals(MessageLoadBalancingType.OFF_WITH_REDISTRIBUTION, 
configuration.getClusterConfigurations().get(0).getMessageLoadBalancingType());
+      assertEquals(CriticalAnalyzerPolicy.SHUTDOWN, 
configuration.getCriticalAnalyzerPolicy());
+   }
+
    @Test
    public void testPropertiesReaderRespectsOrderFromFile() throws Exception {
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact


Reply via email to