From 96016f0c104e980e06e3b1e53b0e8343df03a210 Mon Sep 17 00:00:00 2001
From: Rubbernecker <62733397+Rubbernecker@users.noreply.github.com>
Date: Fri, 25 Dec 2020 12:50:45 +0800
Subject: [PATCH] feature:Support to obtain multiple configurations through a
 single dataid in Nacos (#3303)

---
 changes/1.5.0.md                              |   2 +
 changes/en-us/1.5.0.md                        |   2 +
 .../config/nacos/NacosConfiguration.java      | 130 +++++++++++++++---
 script/client/conf/registry.conf              |   1 +
 script/server/config/registry.conf            |   1 +
 script/server/config/registry.properties      |   1 +
 script/server/config/registry.yml             |   9 +-
 .../config/ConfigNacosProperties.java         |  10 ++
 server/src/main/resources/registry.conf       |   1 +
 9 files changed, 137 insertions(+), 20 deletions(-)

diff --git a/changes/1.5.0.md b/changes/1.5.0.md
index 45a2301aa..69f9d75a5 100644
--- a/changes/1.5.0.md
+++ b/changes/1.5.0.md
@@ -42,6 +42,7 @@ Seata 鏄竴娆惧紑婧愮殑鍒嗗竷寮忎簨鍔¤В鍐虫柟妗堬紝鎻愪緵楂樻€ц兘鍜岀畝鍗�
   - [[#3365](https://github.com/seata/seata/pull/3365)] 淇ParameterParserTest娴嬭瘯鐢ㄤ緥
   - [[#3359](https://github.com/seata/seata/pull/3359)] 鍒犻櫎鏈娇鐢ㄧ殑娴嬭瘯鐢ㄤ緥
   - [[#3397](https://github.com/seata/seata/pull/3397)] 娣诲姞鏇存敼璁板綍鏂囦欢澶�
+  - [[#3303](https://github.com/seata/seata/pull/3303)] 鏀寔浠巒acos鍗曚竴dataId涓鍙栨墍鏈夐厤缃�
   
   
   
@@ -61,6 +62,7 @@ Seata 鏄竴娆惧紑婧愮殑鍒嗗竷寮忎簨鍔¤В鍐虫柟妗堬紝鎻愪緵楂樻€ц兘鍜岀畝鍗�
   - [a364176773](https://github.com/a364176773) 
   - [anselleeyy](https://github.com/anselleeyy)
   - [Ifdevil](https://github.com/Ifdevil)
+  - [Rubbernecker](https://github.com/Rubbernecker)
 
 鍚屾椂锛屾垜浠敹鍒颁簡绀惧尯鍙嶉鐨勫緢澶氭湁浠峰€肩殑issue鍜屽缓璁紝闈炲父鎰熻阿澶у銆�
 
diff --git a/changes/en-us/1.5.0.md b/changes/en-us/1.5.0.md
index 2c4e1a559..fb4364e98 100644
--- a/changes/en-us/1.5.0.md
+++ b/changes/en-us/1.5.0.md
@@ -41,6 +41,7 @@
   - [[#3365](https://github.com/seata/seata/pull/3365)] optimize ParameterParserTest test case failed
   - [[#3359](https://github.com/seata/seata/pull/3359)] remove unused test case
   - [[#3397](https://github.com/seata/seata/pull/3397)] add the change records folder
+  - [[#3303](https://github.com/seata/seata/pull/3303)] supports reading all configurations from a single Nacos dataId
   
   ### test
   
@@ -57,6 +58,7 @@
   - [a364176773](https://github.com/a364176773)
   - [anselleeyy](https://github.com/anselleeyy)
   - [Ifdevil](https://github.com/Ifdevil)
+  - [Rubbernecker](https://github.com/Rubbernecker)
 
   Also, we receive many valuable issues, questions and advices from our community. Thanks for you all.
 
diff --git a/config/seata-config-nacos/src/main/java/io/seata/config/nacos/NacosConfiguration.java b/config/seata-config-nacos/src/main/java/io/seata/config/nacos/NacosConfiguration.java
index bdf45b9ee..c544810f2 100644
--- a/config/seata-config-nacos/src/main/java/io/seata/config/nacos/NacosConfiguration.java
+++ b/config/seata-config-nacos/src/main/java/io/seata/config/nacos/NacosConfiguration.java
@@ -15,6 +15,12 @@
  */
 package io.seata.config.nacos;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.Enumeration;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
@@ -50,8 +56,10 @@ public class NacosConfiguration extends AbstractConfiguration {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(NacosConfiguration.class);
     private static final String DEFAULT_GROUP = "SEATA_GROUP";
+    private static final String DEFAULT_DATA_ID = "seata.properties";
     private static final String GROUP_KEY = "group";
     private static final String PRO_SERVER_ADDR_KEY = "serverAddr";
+    private static final String NACOS_DATA_ID_KEY = "dataId";
     private static final String ENDPOINT_KEY = "endpoint";
     private static final String CONFIG_TYPE = "nacos";
     private static final String DEFAULT_NAMESPACE = "";
@@ -63,8 +71,9 @@ public class NacosConfiguration extends AbstractConfiguration {
     private static final Configuration FILE_CONFIG = ConfigurationFactory.CURRENT_FILE_INSTANCE;
     private static volatile ConfigService configService;
     private static final int MAP_INITIAL_CAPACITY = 8;
-    private ConcurrentMap<String, ConcurrentMap<ConfigurationChangeListener, NacosListener>> configListenersMap
-        = new ConcurrentHashMap<>(MAP_INITIAL_CAPACITY);
+    private static final ConcurrentMap<String, ConcurrentMap<ConfigurationChangeListener, NacosListener>> CONFIG_LISTENERS_MAP
+            = new ConcurrentHashMap<>(MAP_INITIAL_CAPACITY);
+    private static volatile Properties seataConfig = new Properties();
 
     /**
      * Get instance of NacosConfiguration
@@ -89,6 +98,7 @@ public class NacosConfiguration extends AbstractConfiguration {
         if (configService == null) {
             try {
                 configService = NacosFactory.createConfigService(getConfigProperties());
+                initSeataConfig();
             } catch (NacosException e) {
                 throw new RuntimeException(e);
             }
@@ -101,11 +111,17 @@ public class NacosConfiguration extends AbstractConfiguration {
         if (value != null) {
             return value;
         }
-        try {
-            value = configService.getConfig(dataId, getNacosGroup(), timeoutMills);
-        } catch (NacosException exx) {
-            LOGGER.error(exx.getErrMsg());
+
+        value = seataConfig.getProperty(dataId);
+
+        if (null == value) {
+            try {
+                value = configService.getConfig(dataId, getNacosGroup(), timeoutMills);
+            } catch (NacosException exx) {
+                LOGGER.error(exx.getErrMsg());
+            }
         }
+
         return value == null ? defaultValue : value;
     }
 
@@ -113,7 +129,12 @@ public class NacosConfiguration extends AbstractConfiguration {
     public boolean putConfig(String dataId, String content, long timeoutMills) {
         boolean result = false;
         try {
-            result = configService.publishConfig(dataId, getNacosGroup(), content);
+            if (!seataConfig.isEmpty()) {
+                seataConfig.setProperty(dataId, content);
+                result = configService.publishConfig(getNacosDataId(), getNacosGroup(), getSeataConfigStr());
+            } else {
+                result = configService.publishConfig(dataId, getNacosGroup(), content);
+            }
         } catch (NacosException exx) {
             LOGGER.error(exx.getErrMsg());
         }
@@ -129,7 +150,12 @@ public class NacosConfiguration extends AbstractConfiguration {
     public boolean removeConfig(String dataId, long timeoutMills) {
         boolean result = false;
         try {
-            result = configService.removeConfig(dataId, getNacosGroup());
+            if (!seataConfig.isEmpty()) {
+                seataConfig.remove(dataId);
+                result = configService.publishConfig(getNacosDataId(), getNacosGroup(), getSeataConfigStr());
+            } else {
+                result = configService.removeConfig(dataId, getNacosGroup());
+            }
         } catch (NacosException exx) {
             LOGGER.error(exx.getErrMsg());
         }
@@ -143,7 +169,7 @@ public class NacosConfiguration extends AbstractConfiguration {
         }
         try {
             NacosListener nacosListener = new NacosListener(dataId, listener);
-            configListenersMap.computeIfAbsent(dataId, key -> new ConcurrentHashMap<>())
+            CONFIG_LISTENERS_MAP.computeIfAbsent(dataId, key -> new ConcurrentHashMap<>())
                     .put(listener, nacosListener);
             configService.addListener(dataId, getNacosGroup(), nacosListener);
         } catch (Exception exx) {
@@ -161,7 +187,7 @@ public class NacosConfiguration extends AbstractConfiguration {
             for (ConfigurationChangeListener entry : configChangeListeners) {
                 if (listener.equals(entry)) {
                     NacosListener nacosListener = null;
-                    Map<ConfigurationChangeListener, NacosListener> configListeners = configListenersMap.get(dataId);
+                    Map<ConfigurationChangeListener, NacosListener> configListeners = CONFIG_LISTENERS_MAP.get(dataId);
                     if (configListeners != null) {
                         nacosListener = configListeners.get(listener);
                         configListeners.remove(entry);
@@ -177,7 +203,7 @@ public class NacosConfiguration extends AbstractConfiguration {
 
     @Override
     public Set<ConfigurationChangeListener> getConfigListeners(String dataId) {
-        Map<ConfigurationChangeListener, NacosListener> configListeners = configListenersMap.get(dataId);
+        Map<ConfigurationChangeListener, NacosListener> configListeners = CONFIG_LISTENERS_MAP.get(dataId);
         if (CollectionUtils.isNotEmpty(configListeners)) {
             return configListeners.keySet();
         } else {
@@ -210,10 +236,10 @@ public class NacosConfiguration extends AbstractConfiguration {
             properties.setProperty(PRO_NAMESPACE_KEY, namespace);
         }
         String userName = StringUtils.isNotBlank(System.getProperty(USER_NAME)) ? System.getProperty(USER_NAME)
-            : FILE_CONFIG.getConfig(getNacosUserName());
+                : FILE_CONFIG.getConfig(getNacosUserName());
         if (StringUtils.isNotBlank(userName)) {
             String password = StringUtils.isNotBlank(System.getProperty(PASSWORD)) ? System.getProperty(PASSWORD)
-                : FILE_CONFIG.getConfig(getNacosPassword());
+                    : FILE_CONFIG.getConfig(getNacosPassword());
             if (StringUtils.isNotBlank(password)) {
                 properties.setProperty(USER_NAME, userName);
                 properties.setProperty(PASSWORD, password);
@@ -234,20 +260,57 @@ public class NacosConfiguration extends AbstractConfiguration {
         return String.join(ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR, ConfigurationKeys.FILE_ROOT_CONFIG, CONFIG_TYPE, GROUP_KEY);
     }
 
+    private static String getNacosDataIdKey() {
+        return String.join(ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR, ConfigurationKeys.FILE_ROOT_CONFIG, CONFIG_TYPE, NACOS_DATA_ID_KEY);
+    }
+
     private static String getNacosUserName() {
         return String.join(ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR, ConfigurationKeys.FILE_ROOT_CONFIG, CONFIG_TYPE,
-            USER_NAME);
+                USER_NAME);
     }
 
     private static String getNacosPassword() {
         return String.join(ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR, ConfigurationKeys.FILE_ROOT_CONFIG, CONFIG_TYPE,
-            PASSWORD);
+                PASSWORD);
     }
 
     private static String getNacosGroup() {
         return FILE_CONFIG.getConfig(getNacosGroupKey(), DEFAULT_GROUP);
     }
 
+    private static String getNacosDataId() {
+        return FILE_CONFIG.getConfig(getNacosDataIdKey(), DEFAULT_DATA_ID);
+    }
+
+    private static String getSeataConfigStr() {
+        StringBuilder sb = new StringBuilder();
+
+        Enumeration<?> enumeration = seataConfig.propertyNames();
+        while (enumeration.hasMoreElements()) {
+            String key = (String) enumeration.nextElement();
+            String property = seataConfig.getProperty(key);
+            sb.append(key).append("=").append(property).append("\n");
+        }
+
+        return sb.toString();
+    }
+
+    private static void initSeataConfig() {
+        try {
+            String nacosDataId = getNacosDataId();
+            String config = configService.getConfig(nacosDataId, getNacosGroup(), DEFAULT_CONFIG_TIMEOUT);
+            if (StringUtils.isNotBlank(config)) {
+                try (Reader reader = new InputStreamReader(new ByteArrayInputStream(config.getBytes()), StandardCharsets.UTF_8)) {
+                    seataConfig.load(reader);
+                }
+                NacosListener nacosListener = new NacosListener(nacosDataId, null);
+                configService.addListener(nacosDataId, getNacosGroup(), nacosListener);
+            }
+        } catch (NacosException | IOException e) {
+            LOGGER.error("init config properties error", e);
+        }
+    }
+
     @Override
     public String getTypeName() {
         return CONFIG_TYPE;
@@ -282,8 +345,43 @@ public class NacosConfiguration extends AbstractConfiguration {
 
         @Override
         public void innerReceive(String dataId, String group, String configInfo) {
+            //The new configuration method to puts all configurations into a dateId
+            if (getNacosDataId().equals(dataId)) {
+                Properties seataConfigNew = new Properties();
+                if (StringUtils.isNotBlank(configInfo)) {
+                    try (Reader reader = new InputStreamReader(new ByteArrayInputStream(configInfo.getBytes()), StandardCharsets.UTF_8)) {
+                        seataConfigNew.load(reader);
+                    } catch (IOException e) {
+                        LOGGER.error("load config properties error", e);
+                        return;
+                    }
+                }
+
+                //Get all the monitored dataids and judge whether it has been modified
+                for (Map.Entry<String, ConcurrentMap<ConfigurationChangeListener, NacosListener>> entry : CONFIG_LISTENERS_MAP.entrySet()) {
+                    String listenedDataId = entry.getKey();
+                    String propertyOld = seataConfig.getProperty(listenedDataId, "");
+                    String propertyNew = seataConfigNew.getProperty(listenedDataId, "");
+                    if (!propertyOld.equals(propertyNew)) {
+                        ConfigurationChangeEvent event = new ConfigurationChangeEvent()
+                                .setDataId(listenedDataId)
+                                .setNewValue(propertyNew)
+                                .setNamespace(group);
+
+                        ConcurrentMap<ConfigurationChangeListener, NacosListener> configListeners = entry.getValue();
+                        for (ConfigurationChangeListener configListener : configListeners.keySet()) {
+                            configListener.onProcessEvent(event);
+                        }
+                    }
+                }
+
+                seataConfig = seataConfigNew;
+                return;
+            }
+
+            //Compatible with old writing
             ConfigurationChangeEvent event = new ConfigurationChangeEvent().setDataId(dataId).setNewValue(configInfo)
-                .setNamespace(group);
+                    .setNamespace(group);
             listener.onProcessEvent(event);
         }
     }
diff --git a/script/client/conf/registry.conf b/script/client/conf/registry.conf
index b984696dc..fc04bb37f 100644
--- a/script/client/conf/registry.conf
+++ b/script/client/conf/registry.conf
@@ -60,6 +60,7 @@ config {
     group = "SEATA_GROUP"
     username = ""
     password = ""
+    dataId = "seata.properties"
   }
   consul {
     serverAddr = "127.0.0.1:8500"
diff --git a/script/server/config/registry.conf b/script/server/config/registry.conf
index cd85ac735..fefd59e61 100644
--- a/script/server/config/registry.conf
+++ b/script/server/config/registry.conf
@@ -65,6 +65,7 @@ config {
     group = "SEATA_GROUP"
     username = ""
     password = ""
+    dataId = "seataServer.properties"
   }
   consul {
     serverAddr = "127.0.0.1:8500"
diff --git a/script/server/config/registry.properties b/script/server/config/registry.properties
index 9c859befe..b478f5061 100644
--- a/script/server/config/registry.properties
+++ b/script/server/config/registry.properties
@@ -40,6 +40,7 @@ config.nacos.namespace=
 config.nacos.group=SEATA_GROUP
 config.nacos.username=
 config.nacos.password=
+config.nacos.dataId=seataServer.properties
 config.consul.serverAddr=127.0.0.1:8500
 config.apollo.appId=seata-server
 config.apollo.apolloMeta=http://192.168.1.204:8801
diff --git a/script/server/config/registry.yml b/script/server/config/registry.yml
index 13d0d9cae..45ca471a2 100644
--- a/script/server/config/registry.yml
+++ b/script/server/config/registry.yml
@@ -11,8 +11,8 @@ registry:
     namespace: 
     cluster: default
     username: 
-    password: 
-  
+    password:
+
   eureka:
     serviceUrl: http://localhost:8761/eureka
     application: default
@@ -64,11 +64,12 @@ config:
     namespace: 
     group: SEATA_GROUP
     username: 
-    password: 
+    password:
+    dataId: seataServer.properties
   
   consul:
     serverAddr: 127.0.0.1:8500
-  
+
   apollo:
     appId: seata-server
     apolloMeta: http://192.168.1.204:8801
diff --git a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/config/ConfigNacosProperties.java b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/config/ConfigNacosProperties.java
index 798c9e73d..2d75286cf 100644
--- a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/config/ConfigNacosProperties.java
+++ b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/config/ConfigNacosProperties.java
@@ -31,6 +31,7 @@ public class ConfigNacosProperties {
     private String group = "SEATA_GROUP";
     private String username = "";
     private String password = "";
+    private String dataId = "seata.properties";
 
     public String getServerAddr() {
         return serverAddr;
@@ -76,4 +77,13 @@ public class ConfigNacosProperties {
         this.password = password;
         return this;
     }
+
+    public String getDataId() {
+        return dataId;
+    }
+
+    public ConfigNacosProperties setDataId(String dataId) {
+        this.dataId = dataId;
+        return this;
+    }
 }
diff --git a/server/src/main/resources/registry.conf b/server/src/main/resources/registry.conf
index 97f8d0b78..7f0210050 100644
--- a/server/src/main/resources/registry.conf
+++ b/server/src/main/resources/registry.conf
@@ -65,6 +65,7 @@ config {
     group = "SEATA_GROUP"
     username = ""
     password = ""
+    dataId = "seataServer.properties"
   }
   consul {
     serverAddr = "127.0.0.1:8500"
-- 
GitLab