diff --git a/CHANGES.md b/CHANGES.md
index 3394d9b14..49c45c229 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -6,9 +6,14 @@ Release Notes.
------------------
* Support set-type in the agent or plugin configurations
+* Optimize ConfigInitializer to output warning messages when the config value is truncated.
+* Fix the default value of the Map field would merge rather than override by new values in the config.
+* Support to set the value of Map/List field to an empty map/list.
#### Documentation
+* Update `configuration` doc about overriding default value as empty map/list accordingly.
+
All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/150?closed=1)
------------------
diff --git a/apm-commons/apm-util/src/main/java/org/apache/skywalking/apm/util/ConfigInitializer.java b/apm-commons/apm-util/src/main/java/org/apache/skywalking/apm/util/ConfigInitializer.java
index 912acbcf9..f09af7458 100644
--- a/apm-commons/apm-util/src/main/java/org/apache/skywalking/apm/util/ConfigInitializer.java
+++ b/apm-commons/apm-util/src/main/java/org/apache/skywalking/apm/util/ConfigInitializer.java
@@ -25,6 +25,7 @@ import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -32,6 +33,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
+import java.util.TreeMap;
import java.util.TreeSet;
/**
@@ -39,6 +41,7 @@ import java.util.TreeSet;
*
*/
public class ConfigInitializer {
+
public static void initialize(Properties properties, Class> rootConfigType) throws IllegalAccessException {
initNextLevel(properties, rootConfigType, new ConfigDesc());
}
@@ -49,48 +52,50 @@ public class ConfigInitializer {
if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) {
String configKey = (parentDesc + "." + field.getName()).toLowerCase();
Class> type = field.getType();
-
- if (type.equals(Map.class)) {
+ if (Map.class.isAssignableFrom(type)) {
/*
- * Map config format is, config_key[map_key]=map_value
- * Such as plugin.opgroup.resttemplate.rule[abc]=/url/path
+ * Map config format is, config_key[map_key]=map_value, such as plugin.opgroup.resttemplate.rule[abc]=/url/path
+ * "config_key[]=" will generate an empty Map , user could use this mechanism to set an empty Map
*/
- // Deduct two generic types of the map
ParameterizedType genericType = (ParameterizedType) field.getGenericType();
Type[] argumentTypes = genericType.getActualTypeArguments();
-
- Type keyType = null;
- Type valueType = null;
- if (argumentTypes != null && argumentTypes.length == 2) {
- // Get key type and value type of the map
- keyType = argumentTypes[0];
- valueType = argumentTypes[1];
- }
- Map map = (Map) field.get(null);
- // Set the map from config key and properties
- setForMapType(configKey, map, properties, keyType, valueType);
- } else if (Collection.class.isAssignableFrom(type)) {
- ParameterizedType genericType = (ParameterizedType) field.getGenericType();
- Type argumentType = genericType.getActualTypeArguments()[0];
- Collection collection = convertToCollection(argumentType, type, properties.getProperty(configKey));
- field.set(null, collection);
- } else {
- /*
- * Others typical field type
- */
- String value = properties.getProperty(configKey);
- // Convert the value into real type
- final Length lengthDefine = field.getAnnotation(Length.class);
- if (lengthDefine != null) {
- if (value != null && value.length() > lengthDefine.value()) {
- value = value.substring(0, lengthDefine.value());
+ Type keyType = argumentTypes[0];
+ Type valueType = argumentTypes[1];
+ // A chance to set an empty map
+ if (properties.containsKey(configKey + "[]")) {
+ Map currentValue = (Map) field.get(null);
+ if (currentValue != null && !currentValue.isEmpty()) {
+ field.set(null, initEmptyMap(type));
+ }
+ } else {
+ // Set the map from config key and properties
+ Map map = readMapType(type, configKey, properties, keyType, valueType);
+ if (map.size() != 0) {
+ field.set(null, map);
}
}
- Object convertedValue = convertToTypicalType(type, value);
- if (convertedValue != null) {
- field.set(null, convertedValue);
+ } else if (properties.containsKey(configKey)) {
+ //In order to guarantee the default value could be reset as empty , we parse the value even if it's blank
+ String propertyValue = properties.getProperty(configKey, "");
+ if (Collection.class.isAssignableFrom(type)) {
+ ParameterizedType genericType = (ParameterizedType) field.getGenericType();
+ Type argumentType = genericType.getActualTypeArguments()[0];
+ Collection collection = convertToCollection(argumentType, type, propertyValue);
+ field.set(null, collection);
+ } else {
+ // Convert the value into real type
+ final Length lengthDefine = field.getAnnotation(Length.class);
+ if (lengthDefine != null && propertyValue.length() > lengthDefine.value()) {
+ StringUtil.cut(propertyValue, lengthDefine.value());
+ System.err.printf("The config value will be truncated , because the length max than %d : %s -> %s%n", lengthDefine.value(), configKey, propertyValue);
+ }
+ Object convertedValue = convertToTypicalType(type, propertyValue);
+ if (convertedValue != null) {
+ field.set(null, convertedValue);
+ }
}
}
+
}
}
for (Class> innerConfiguration : recentConfigType.getClasses()) {
@@ -131,10 +136,9 @@ public class ConfigInitializer {
* @return converted value or null
*/
private static Object convertToTypicalType(Type type, String value) {
- if (value == null || type == null) {
+ if (StringUtil.isBlank(value)) {
return null;
}
-
Object result = null;
if (String.class.equals(type)) {
result = value;
@@ -160,21 +164,23 @@ public class ConfigInitializer {
/**
* Set map items.
*
+ * @param type the filed type
* @param configKey config key must not be null
- * @param map map to set must not be null
* @param properties properties must not be null
* @param keyType key type of the map
* @param valueType value type of the map
*/
- private static void setForMapType(String configKey, Map