You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2022/01/26 03:41:18 UTC

[dubbo] branch 3.0 updated: [3.0] Add ignore properties support (#9628)

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

albumenj pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new dfe4fca  [3.0] Add ignore properties support (#9628)
dfe4fca is described below

commit dfe4fcaea107ac292e6f2e967807a0756f59c7e3
Author: Albumen Kevin <jh...@gmail.com>
AuthorDate: Wed Jan 26 11:40:56 2022 +0800

    [3.0] Add ignore properties support (#9628)
---
 .../org/apache/dubbo/config/AbstractConfig.java    | 143 ++++++++++++++++++++-
 .../config/context/AbstractConfigManager.java      |  24 +++-
 .../apache/dubbo/config/context/ConfigManager.java |   2 +-
 .../apache/dubbo/config/context/ConfigMode.java    |  10 ++
 4 files changed, 171 insertions(+), 8 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
index 4dc78b3..563b56d 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
@@ -31,6 +31,7 @@ import org.apache.dubbo.common.utils.MethodUtils;
 import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.config.context.ConfigMode;
 import org.apache.dubbo.config.support.Nested;
 import org.apache.dubbo.config.support.Parameter;
 import org.apache.dubbo.rpc.model.ApplicationModel;
@@ -255,6 +256,10 @@ public abstract class AbstractConfig implements Serializable {
         return propertyName;
     }
 
+    private static String calculatePropertyToGetter(String name) {
+        return "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
+    }
+
     private static String calculatePropertyFromGetter(String name) {
         int i = name.startsWith("get") ? 3 : 2;
         return StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), ".");
@@ -415,6 +420,7 @@ public abstract class AbstractConfig implements Serializable {
      *   }
      * }
      * </pre>
+     *
      * @param oldScopeModel
      * @param newScopeModel
      */
@@ -558,6 +564,84 @@ public abstract class AbstractConfig implements Serializable {
         return CommonConstants.DUBBO + "." + getTagName(cls);
     }
 
+    public ConfigMode getConfigMode() {
+        return getApplicationModel().getApplicationConfigManager().getConfigMode();
+    }
+
+    public void overrideWithConfig(AbstractConfig newOne, boolean overrideAll) {
+        if (!Objects.equals(this.getClass(), newOne.getClass())) {
+            // ignore if two config is not the same class
+            return;
+        }
+
+        List<Method> methods = MethodUtils.getMethods(this.getClass(), method -> method.getDeclaringClass() != Object.class);
+        for (Method method : methods) {
+            try {
+                Method getterMethod;
+                try {
+                    String propertyName = extractPropertyName(method.getName());
+                    String getterName = calculatePropertyToGetter(propertyName);
+                    getterMethod = this.getClass().getDeclaredMethod(getterName);
+                } catch (Exception ignore) {
+                    continue;
+                }
+
+                if (MethodUtils.isSetter(method)) {
+                    Object oldOne = getterMethod.invoke(this);
+
+                    // if old one is null or need to override
+                    if (overrideAll || oldOne == null) {
+                        Object newResult = getterMethod.invoke(newOne);
+                        // if new one is non-null and new one is not equals old one
+                        if (newResult != null && Objects.equals(newResult, oldOne)) {
+                            method.invoke(this, newResult);
+                        }
+                    }
+                } else if (isParametersSetter(method)) {
+                    Object oldOne = getterMethod.invoke(this);
+                    Object newResult = getterMethod.invoke(newOne);
+
+                    Map<String, String> oldMap = null;
+                    if (oldOne instanceof Map) {
+                        oldMap = (Map) oldOne;
+                    }
+
+                    Map<String, String> newMap = null;
+                    if (newResult instanceof Map) {
+                        newMap = (Map) newResult;
+                    }
+
+                    // if new map is null, skip
+                    if (newMap == null) {
+                        continue;
+                    }
+
+                    // if old map is null, override with new map
+                    if (oldMap == null) {
+                        invokeSetParameters(newMap, this);
+                        continue;
+                    }
+
+                    // if mode is OVERRIDE_IF_ABSENT, put all old map entries to new map, will override the same key
+                    // if mode is OVERRIDE_ALL, put all keyed entries not in new map from old map to new map (ignore the same key appeared in old map)
+                    if (overrideAll) {
+                        oldMap.forEach(newMap::putIfAbsent);
+                    } else {
+                        newMap.putAll(oldMap);
+                    }
+
+                    invokeSetParameters(newMap, this);
+                } else if (isNestedSetter(this, method)) {
+                    // not support
+                }
+
+            } catch (Throwable t) {
+                logger.error("Failed to override field value of config bean: " + this, t);
+                throw new IllegalStateException("Failed to override field value of config bean: " + this, t);
+            }
+        }
+    }
+
     /**
      * Dubbo config property override
      */
@@ -615,11 +699,23 @@ public abstract class AbstractConfig implements Serializable {
     }
 
     private void assignProperties(Object obj, Environment environment, Map<String, String> properties, InmemoryConfiguration configuration) {
+        // if old one (this) contains non-null value, do not override
+        boolean overrideIfAbsent = getConfigMode() == ConfigMode.OVERRIDE_IF_ABSENT;
+
+        // even if old one (this) contains non-null value, do override
+        boolean overrideAll = getConfigMode() == ConfigMode.OVERRIDE_ALL;
+
         // loop methods, get override value and set the new value back to method
         List<Method> methods = MethodUtils.getMethods(obj.getClass(), method -> method.getDeclaringClass() != Object.class);
         for (Method method : methods) {
             if (MethodUtils.isSetter(method)) {
                 String propertyName = extractPropertyName(method.getName());
+
+                // if config mode is OVERRIDE_IF_ABSENT and property has set, skip
+                if (overrideIfAbsent && isPropertySet(propertyName)) {
+                    continue;
+                }
+
                 // convert camelCase/snake_case to kebab-case
                 String kebabPropertyName = StringUtils.convertToSplitName(propertyName, "-");
 
@@ -639,6 +735,20 @@ public abstract class AbstractConfig implements Serializable {
                 }
             } else if (isParametersSetter(method)) {
                 String propertyName = extractPropertyName(method.getName());
+
+                // get old map from original obj
+                Map<String, String> oldMap = null;
+                try {
+                    String getterName = calculatePropertyToGetter(propertyName);
+                    Method getterMethod = this.getClass().getDeclaredMethod(getterName);
+                    Object oldOne = getterMethod.invoke(this);
+                    if (oldOne instanceof Map) {
+                        oldMap = (Map) oldOne;
+                    }
+                } catch (Exception ignore) {
+
+                }
+
                 String value = StringUtils.trim(configuration.getString(propertyName));
                 Map<String, String> parameterMap;
                 if (StringUtils.hasText(value)) {
@@ -647,7 +757,24 @@ public abstract class AbstractConfig implements Serializable {
                     // in this case, maybe parameters.item3=value3.
                     parameterMap = ConfigurationUtils.getSubProperties(properties, PARAMETERS);
                 }
-                invokeSetParameters(convert(parameterMap, ""), obj);
+                Map<String, String> newMap = convert(parameterMap, "");
+
+                // if old map is null, directly set params
+                if (oldMap == null) {
+                    invokeSetParameters(newMap, obj);
+                    continue;
+                }
+
+                // if mode is OVERRIDE_IF_ABSENT, put all old map entries to new map, will override the same key
+                // if mode is OVERRIDE_ALL, put all keyed entries not in new map from old map to new map (ignore the same key appeared in old map)
+                // if mode is others, override with new map
+                if (overrideIfAbsent) {
+                    newMap.putAll(oldMap);
+                } else if (overrideAll) {
+                    oldMap.forEach(newMap::putIfAbsent);
+                }
+
+                invokeSetParameters(newMap, obj);
             } else if (isNestedSetter(obj, method)) {
                 try {
                     Class<?> clazz = method.getParameterTypes()[0];
@@ -664,6 +791,20 @@ public abstract class AbstractConfig implements Serializable {
         }
     }
 
+    private boolean isPropertySet(String propertyName) {
+        try {
+            String getterName = calculatePropertyToGetter(propertyName);
+            Method getterMethod = this.getClass().getDeclaredMethod(getterName);
+            Object oldOne = getterMethod.invoke(this);
+            if (oldOne != null) {
+                return true;
+            }
+        } catch (Exception ignore) {
+
+        }
+        return false;
+    }
+
     private void invokeSetParameters(Map<String, String> values, Object obj) {
         if (CollectionUtils.isEmptyMap(values)) {
             return;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java
index 69c6771..ebd903b 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java
@@ -266,11 +266,7 @@ public abstract class AbstractConfigManager extends LifecycleAdapter {
         }
 
         // check unique config
-        Optional<C> oldOne = checkUniqueConfig(configsMap, config);
-        if (oldOne != null) {
-            return oldOne;
-        }
-        return Optional.empty();
+        return checkUniqueConfig(configsMap, config);
     }
 
     public <C extends AbstractConfig> Map<String, C> getConfigsMap(Class<C> cls) {
@@ -448,9 +444,25 @@ public abstract class AbstractConfigManager extends LifecycleAdapter {
                     }
                     break;
                 }
+                case OVERRIDE_ALL: {
+                    // override old one's properties with the new one
+                    oldOne.overrideWithConfig(config, true);
+                    if (logger.isWarnEnabled() && duplicatedConfigs.add(config)) {
+                        logger.warn(msgPrefix + "override previous config with later config");
+                    }
+                    return Optional.of(oldOne);
+                }
+                case OVERRIDE_IF_ABSENT: {
+                    // override old one's properties with the new one
+                    oldOne.overrideWithConfig(config, false);
+                    if (logger.isWarnEnabled() && duplicatedConfigs.add(config)) {
+                        logger.warn(msgPrefix + "override previous config with later config");
+                    }
+                    return Optional.of(oldOne);
+                }
             }
         }
-        return null;
+        return Optional.empty();
     }
 
     public abstract void loadConfigs();
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
index a6f4484..e599eb7 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
@@ -297,7 +297,7 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE
         }
     }
 
-    ConfigMode getConfigMode() {
+    public ConfigMode getConfigMode() {
         return configMode;
     }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigMode.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigMode.java
index 5474a48..634a907 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigMode.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigMode.java
@@ -32,6 +32,16 @@ public enum ConfigMode {
     OVERRIDE,
 
     /**
+     * Override mode: accept last config, override previous config
+     */
+    OVERRIDE_ALL,
+
+    /**
+     * Override mode: accept last config, override previous config
+     */
+    OVERRIDE_IF_ABSENT,
+
+    /**
      * Ignore mode: accept first config, ignore later configs
      */
     IGNORE