This is an automated email from the ASF dual-hosted git repository.
liujun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/master by this push:
new 041f8bb Use safeConstructor instead of constructor when parsing yaml
(#7437)
041f8bb is described below
commit 041f8bb0a98a912bd363560d5a2339198dcf79a5
Author: Wu Zhiguo <[email protected]>
AuthorDate: Fri Mar 26 18:58:37 2021 +0800
Use safeConstructor instead of constructor when parsing yaml (#7437)
---
.../cluster/configurator/parser/ConfigParser.java | 24 +++---
.../config/model/ConditionRuleParser.java | 14 ++--
.../cluster/router/tag/model/TagRuleParser.java | 19 ++---
.../configurator/parser/ConfigParserTest.java | 12 +--
.../dubbo/rpc/cluster/router/TagRouterTest.java | 2 +-
.../org/apache/dubbo/common/utils/PojoUtils.java | 97 ++++++++++++++++++++++
.../apache/dubbo/common/utils/PojoUtilsTest.java | 50 +++++++++++
7 files changed, 181 insertions(+), 37 deletions(-)
diff --git
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java
index 0fc5fa9..8058fb5 100644
---
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java
+++
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java
@@ -16,33 +16,33 @@
*/
package org.apache.dubbo.rpc.cluster.configurator.parser;
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONValidator;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.PojoUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfigItem;
import
org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig;
-import org.yaml.snakeyaml.TypeDescription;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONValidator;
import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
import static
org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY;
import static
org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_CONFIGURATORS_CATEGORY;
+import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY;
/**
* Config parser
*/
public class ConfigParser {
- public static List<URL> parseConfigurators(String rawConfig) {
+ public static List<URL> parseConfigurators(String rawConfig) throws
Exception {
// compatible url JsonArray, such as [ "override://xxx",
"override://xxx" ]
if (isJsonArray(rawConfig)) {
return parseJsonArray(rawConfig);
@@ -72,14 +72,10 @@ public class ConfigParser {
return urls;
}
- private static <T> T parseObject(String rawConfig) {
- Constructor constructor = new Constructor(ConfiguratorConfig.class);
- TypeDescription itemDescription = new
TypeDescription(ConfiguratorConfig.class);
- itemDescription.addPropertyParameters("items", ConfigItem.class);
- constructor.addTypeDescription(itemDescription);
-
- Yaml yaml = new Yaml(constructor);
- return yaml.load(rawConfig);
+ private static <T> T parseObject(String rawConfig) throws Exception {
+ Yaml yaml = new Yaml(new SafeConstructor());
+ Map<String, Object> map = yaml.load(rawConfig);
+ return (T) PojoUtils.mapToPojo(map, ConfiguratorConfig.class);
}
private static List<URL> serviceItemToUrls(ConfigItem item,
ConfiguratorConfig config) {
diff --git
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java
index 07cc7ed..f64ae00 100644
---
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java
+++
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java
@@ -17,9 +17,12 @@
package org.apache.dubbo.rpc.cluster.router.condition.config.model;
import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.PojoUtils;
import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+
+import java.util.Map;
/**
* %YAML1.2
@@ -37,11 +40,10 @@ import org.yaml.snakeyaml.constructor.Constructor;
*/
public class ConditionRuleParser {
- public static ConditionRouterRule parse(String rawRule) {
- Constructor constructor = new Constructor(ConditionRouterRule.class);
-
- Yaml yaml = new Yaml(constructor);
- ConditionRouterRule rule = yaml.load(rawRule);
+ public static ConditionRouterRule parse(String rawRule) throws Exception {
+ Yaml yaml = new Yaml(new SafeConstructor());
+ Map<String, Object> map = yaml.load(rawRule);
+ ConditionRouterRule rule = PojoUtils.mapToPojo(map,
ConditionRouterRule.class);
rule.setRawRule(rawRule);
if (CollectionUtils.isEmpty(rule.getConditions())) {
rule.setValid(false);
diff --git
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java
index 4f6669f..d25f3dd 100644
---
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java
+++
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java
@@ -17,23 +17,22 @@
package org.apache.dubbo.rpc.cluster.router.tag.model;
import org.apache.dubbo.common.utils.CollectionUtils;
-import org.yaml.snakeyaml.TypeDescription;
+import org.apache.dubbo.common.utils.PojoUtils;
+
import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+
+import java.util.Map;
/**
*
*/
public class TagRuleParser {
- public static TagRouterRule parse(String rawRule) {
- Constructor constructor = new Constructor(TagRouterRule.class);
- TypeDescription tagDescription = new
TypeDescription(TagRouterRule.class);
- tagDescription.addPropertyParameters("tags", Tag.class);
- constructor.addTypeDescription(tagDescription);
-
- Yaml yaml = new Yaml(constructor);
- TagRouterRule rule = yaml.load(rawRule);
+ public static TagRouterRule parse(String rawRule) throws Exception {
+ Yaml yaml = new Yaml(new SafeConstructor());
+ Map<String, Object> map = yaml.load(rawRule);
+ TagRouterRule rule = PojoUtils.mapToPojo(map, TagRouterRule.class);
rule.setRawRule(rawRule);
if (CollectionUtils.isEmpty(rule.getTags())) {
rule.setValid(false);
diff --git
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java
index d8d616f..6dcd953 100644
---
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java
+++
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java
@@ -89,7 +89,7 @@ public class ConfigParserTest {
}
@Test
- public void parseConfiguratorsServiceMultiAppsTest() throws IOException {
+ public void parseConfiguratorsServiceMultiAppsTest() throws Exception {
try (InputStream yamlStream =
this.getClass().getResourceAsStream("/ServiceMultiApps.yml")) {
List<URL> urls =
ConfigParser.parseConfigurators(streamToString(yamlStream));
Assertions.assertNotNull(urls);
@@ -112,7 +112,7 @@ public class ConfigParserTest {
}
@Test
- public void parseConfiguratorsAppMultiServicesTest() throws IOException {
+ public void parseConfiguratorsAppMultiServicesTest() throws Exception {
try (InputStream yamlStream =
this.getClass().getResourceAsStream("/AppMultiServices.yml")) {
String yamlFile = streamToString(yamlStream);
List<URL> urls = ConfigParser.parseConfigurators(yamlFile);
@@ -129,7 +129,7 @@ public class ConfigParserTest {
@Test
- public void parseConfiguratorsAppAnyServicesTest() throws IOException {
+ public void parseConfiguratorsAppAnyServicesTest() throws Exception {
try (InputStream yamlStream =
this.getClass().getResourceAsStream("/AppAnyServices.yml")) {
List<URL> urls =
ConfigParser.parseConfigurators(streamToString(yamlStream));
Assertions.assertNotNull(urls);
@@ -144,7 +144,7 @@ public class ConfigParserTest {
}
@Test
- public void parseConfiguratorsAppNoServiceTest() throws IOException {
+ public void parseConfiguratorsAppNoServiceTest() throws Exception {
try (InputStream yamlStream =
this.getClass().getResourceAsStream("/AppNoService.yml")) {
List<URL> urls =
ConfigParser.parseConfigurators(streamToString(yamlStream));
Assertions.assertNotNull(urls);
@@ -159,7 +159,7 @@ public class ConfigParserTest {
}
@Test
- public void parseConsumerSpecificProvidersTest() throws IOException {
+ public void parseConsumerSpecificProvidersTest() throws Exception {
try (InputStream yamlStream =
this.getClass().getResourceAsStream("/ConsumerSpecificProviders.yml")) {
List<URL> urls =
ConfigParser.parseConfigurators(streamToString(yamlStream));
Assertions.assertNotNull(urls);
@@ -175,7 +175,7 @@ public class ConfigParserTest {
}
@Test
- public void parseURLJsonArrayCompatible() {
+ public void parseURLJsonArrayCompatible() throws Exception {
String configData =
"[\"override://0.0.0.0/com.xx.Service?category=configurators&timeout=6666&disabled=true&dynamic=false&enabled=true&group=dubbo&priority=1&version=1.0\"
]";
diff --git
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java
index 0981719..4215575 100644
---
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java
+++
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java
@@ -76,7 +76,7 @@ public class TagRouterTest {
* </pre>
*/
@Test
- public void tagRouterRuleParseTest(){
+ public void tagRouterRuleParseTest() throws Exception {
String tagRouterRuleConfig = "---\n" +
"force: false\n" +
"runtime: true\n" +
diff --git
a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
index 29328b2..d3fbe8d 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
@@ -23,6 +23,7 @@ import org.apache.dubbo.common.logger.LoggerFactory;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -30,6 +31,8 @@ import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -41,6 +44,7 @@ import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Properties;
import java.util.TreeMap;
import java.util.WeakHashMap;
@@ -50,6 +54,8 @@ import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom;
+
/**
* PojoUtils. Travel object deeply, and convert complex type to simple type.
* <p/>
@@ -68,6 +74,8 @@ public class PojoUtils {
private static final ConcurrentMap<String, Method> NAME_METHODS_CACHE =
new ConcurrentHashMap<String, Method>();
private static final ConcurrentMap<Class<?>, ConcurrentMap<String, Field>>
CLASS_FIELD_CACHE = new ConcurrentHashMap<Class<?>, ConcurrentMap<String,
Field>>();
private static final boolean GENERIC_WITH_CLZ =
Boolean.parseBoolean(ConfigUtils.getProperty(CommonConstants.GENERIC_WITH_CLZ_KEY,
"true"));
+ private static final List<Class<?>> CLASS_CAN_BE_STRING =
Arrays.asList(Byte.class, Short.class, Integer.class,
+ Long.class, Float.class, Double.class, Boolean.class,
Character.class);
public static Object[] generalize(Object[] objs) {
Object[] dests = new Object[objs.length];
@@ -678,4 +686,93 @@ public class PojoUtils {
}
}
+ /**
+ * convert map to a specific class instance
+ *
+ * @param map map wait for convert
+ * @param cls the specified class
+ * @param <T> the type of {@code cls}
+ * @return class instance declare in param {@code cls}
+ * @throws ReflectiveOperationException if the instance creation is failed
+ * @since 2.7.10
+ */
+ public static <T> T mapToPojo(Map<String, Object> map, Class<T> cls)
throws ReflectiveOperationException {
+ T instance = cls.getDeclaredConstructor().newInstance();
+ Map<String, Field> beanPropertyFields =
ReflectUtils.getBeanPropertyFields(cls);
+ for (Map.Entry<String, Field> entry : beanPropertyFields.entrySet()) {
+ String name = entry.getKey();
+ Field field = entry.getValue();
+ Object mapObject = map.get(name);
+ if (mapObject == null) {
+ continue;
+ }
+
+ Type type = field.getGenericType();
+ Object fieldObject = getFieldObject(mapObject, type);
+ field.set(instance, fieldObject);
+ }
+
+ return instance;
+ }
+
+ private static Object getFieldObject(Object mapObject, Type fieldType)
throws ReflectiveOperationException {
+ if (fieldType instanceof Class<?>) {
+ return convertClassType(mapObject, (Class<?>) fieldType);
+ } else if (fieldType instanceof ParameterizedType) {
+ return convertParameterizedType(mapObject, (ParameterizedType)
fieldType);
+ } else if (fieldType instanceof GenericArrayType || fieldType
instanceof TypeVariable<?> || fieldType instanceof WildcardType) {
+ // ignore these type currently
+ return null;
+ } else {
+ throw new IllegalArgumentException("Unrecognized Type: " +
fieldType.toString());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object convertClassType(Object mapObject, Class<?> type)
throws ReflectiveOperationException {
+ if (type.isPrimitive() || isAssignableFrom(type,
mapObject.getClass())) {
+ return mapObject;
+ } else if (Objects.equals(type, String.class) &&
CLASS_CAN_BE_STRING.contains(mapObject.getClass())) {
+ // auto convert specified type to string
+ return mapObject.toString();
+ } else if (mapObject instanceof Map) {
+ return mapToPojo((Map<String, Object>) mapObject, type);
+ } else {
+ // type didn't match and mapObject is not another Map struct.
+ // we just ignore this situation.
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object convertParameterizedType(Object mapObject,
ParameterizedType type) throws ReflectiveOperationException {
+ Type rawType = type.getRawType();
+ if (!isAssignableFrom((Class<?>) rawType, mapObject.getClass())) {
+ return null;
+ }
+
+ Type[] actualTypeArguments = type.getActualTypeArguments();
+ if (isAssignableFrom(Map.class, (Class<?>) rawType)) {
+ Map<Object, Object> map = (Map<Object, Object>)
mapObject.getClass().getDeclaredConstructor().newInstance();
+ for (Map.Entry<Object, Object> entry : ((Map<Object, Object>)
mapObject).entrySet()) {
+ Object key = getFieldObject(entry.getKey(),
actualTypeArguments[0]);
+ Object value = getFieldObject(entry.getValue(),
actualTypeArguments[1]);
+ map.put(key, value);
+ }
+
+ return map;
+ } else if (isAssignableFrom(Collection.class, (Class<?>) rawType)) {
+ Collection<Object> collection = (Collection<Object>)
mapObject.getClass().getDeclaredConstructor().newInstance();
+ for (Object m : (Iterable<?>) mapObject) {
+ Object ele = getFieldObject(m, actualTypeArguments[0]);
+ collection.add(ele);
+ }
+
+ return collection;
+ } else {
+ // ignore other type currently
+ return null;
+ }
+ }
+
}
diff --git
a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java
b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java
index e615dd9..b093bb2 100644
---
a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java
+++
b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java
@@ -760,6 +760,38 @@ public class PojoUtilsTest {
assertEquals(setResult, setStr);
}
+ @Test
+ public void testMapToPojo() throws Exception {
+ Map<String, Object> map = new HashMap<>();
+ map.put("gender", "male");
+ map.put("age", 40);
+
+ List<Map<String, Object>> children = new ArrayList<>();
+ Map<String, Object> child = new HashMap<>();
+ child.put("gender", "male");
+ child.put("age", 15);
+ children.add(child);
+ map.put("children", children);
+
+ Map<String, Object> features = new HashMap<>();
+ features.put("divorce", false);
+ features.put("money", 0);
+ features.put("height", "177cm");
+ map.put("features", features);
+
+ Parent parent = PojoUtils.mapToPojo(map, Parent.class);
+
+ assertEquals(parent.gender, "male");;
+ assertEquals(parent.getAge(), 40);
+ assertEquals(parent.getChildren().size(), 1);
+ assertEquals(parent.getChildren().get(0).gender, "male");
+ assertEquals(parent.getChildren().get(0).age, 15);
+ assertNotNull(parent.getFeatures());
+ assertEquals(parent.getFeatures().get("divorce"), "false");
+ assertEquals(parent.getFeatures().get("money"), "0");
+ assertEquals(parent.getFeatures().get("height"), "177cm");
+ }
+
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
@@ -842,7 +874,9 @@ public class PojoUtilsTest {
String name;
int age;
Child child;
+ private List<Child> children;
private String securityEmail;
+ private Map<String, String> features;
public static Parent getNewParent() {
return new Parent();
@@ -879,6 +913,22 @@ public class PojoUtilsTest {
public void setChild(Child child) {
this.child = child;
}
+
+ public List<Child> getChildren() {
+ return children;
+ }
+
+ public void setChildren(List<Child> children) {
+ this.children = children;
+ }
+
+ public Map<String, String> getFeatures() {
+ return features;
+ }
+
+ public void setFeatures(Map<String, String> features) {
+ this.features = features;
+ }
}
public static class Child {