Skip to content

Commit

Permalink
[3.0] Add ignore properties support (#9628)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlbumenJ authored Jan 26, 2022
1 parent ccf20fb commit dfe4fca
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
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;
Expand Down Expand Up @@ -255,6 +256,10 @@ protected static String extractPropertyName(String setter) {
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), ".");
Expand Down Expand Up @@ -415,6 +420,7 @@ protected void checkScopeModel(ScopeModel scopeModel) {
* }
* }
* </pre>
*
* @param oldScopeModel
* @param newScopeModel
*/
Expand Down Expand Up @@ -558,6 +564,84 @@ public static String getTypePrefix(Class<? extends AbstractConfig> cls) {
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
*/
Expand Down Expand Up @@ -615,11 +699,23 @@ public void refresh() {
}

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, "-");

Expand All @@ -639,6 +735,20 @@ private void assignProperties(Object obj, Environment environment, Map<String, S
}
} 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)) {
Expand All @@ -647,7 +757,24 @@ private void assignProperties(Object obj, Environment environment, Map<String, S
// 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];
Expand All @@ -664,6 +791,20 @@ private void assignProperties(Object obj, Environment environment, Map<String, S
}
}

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,7 @@ protected <C extends AbstractConfig> Optional<C> findDuplicatedConfig(Map<String
}

// 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) {
Expand Down Expand Up @@ -448,9 +444,25 @@ protected <C extends AbstractConfig> Optional<C> checkUniqueConfig(Map<String, C
}
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ private void checkConfigs() {
}
}

ConfigMode getConfigMode() {
public ConfigMode getConfigMode() {
return configMode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,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
*/
Expand Down

0 comments on commit dfe4fca

Please sign in to comment.