Skip to content

Commit 9a0b518

Browse files
authored
fix: parsing failed for Collection<Collection<T>> (#262)
1 parent b8d71e1 commit 9a0b518

File tree

12 files changed

+437
-131
lines changed

12 files changed

+437
-131
lines changed

arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/ReflectUtil.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
import java.lang.reflect.Field;
44
import java.lang.reflect.Method;
5+
import java.util.ArrayList;
6+
import java.util.Collection;
7+
import java.util.Collections;
8+
import java.util.HashSet;
9+
import java.util.LinkedHashSet;
10+
import java.util.LinkedList;
11+
import java.util.TreeSet;
512

613
public class ReflectUtil {
714

@@ -32,4 +39,39 @@ public static Object getFieldOrInvokeMethod(Reflector<Object> reflector, Object
3239
public interface Reflector<T> {
3340
T reflect() throws Exception;
3441
}
42+
43+
/**
44+
* Return new instance of collection by type name per time, do not cache.
45+
* @param typeName type name of collection
46+
* @return new instance of collection
47+
*/
48+
public static <T> Collection<T> getCollectionInstance(String typeName) {
49+
if ("java.util.ArrayList".equals(typeName)) {
50+
return new ArrayList<>();
51+
}
52+
if ("java.util.LinkedList".equals(typeName)) {
53+
return new LinkedList<>();
54+
}
55+
if ("java.util.Collections$EmptyList".equals(typeName)) {
56+
return Collections.emptyList();
57+
}
58+
if ("java.util.HashSet".equals(typeName)) {
59+
return new HashSet<>();
60+
}
61+
if ("java.util.LinkedHashSet".equals(typeName)) {
62+
return new LinkedHashSet<>();
63+
}
64+
if ("java.util.TreeSet".equals(typeName)) {
65+
return new TreeSet<>();
66+
}
67+
if ("java.util.Collections$EmptySet".equals(typeName)) {
68+
return Collections.emptySet();
69+
}
70+
71+
try {
72+
return (Collection<T>) Class.forName(typeName).getDeclaredConstructor().newInstance();
73+
} catch (Exception e) {
74+
return null;
75+
}
76+
}
3577
}

arex-agent-bootstrap/src/test/java/io/arex/agent/bootstrap/util/ReflectUtilTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
package io.arex.agent.bootstrap.util;
22

3+
import java.util.ArrayDeque;
4+
import java.util.ArrayList;
5+
import java.util.Collection;
6+
import java.util.Collections;
7+
import java.util.HashSet;
8+
import java.util.LinkedHashSet;
9+
import java.util.LinkedList;
10+
import java.util.TreeSet;
311
import org.junit.jupiter.api.Test;
412

513
import static org.junit.jupiter.api.Assertions.*;
@@ -11,4 +19,34 @@ void getFieldOrInvokeMethod() throws Exception {
1119
assertEquals(0, ReflectUtil.getFieldOrInvokeMethod(() -> String.class.getDeclaredField("hash"),"mock"));
1220
assertNull(ReflectUtil.getFieldOrInvokeMethod(() -> System.class.getDeclaredMethod("checkIO"), null));
1321
}
22+
23+
@Test
24+
void getCollectionInstance() {
25+
Collection<?> actualResult = ReflectUtil.getCollectionInstance("java.util.ArrayList");
26+
assertInstanceOf(ArrayList.class, actualResult);
27+
28+
actualResult = ReflectUtil.getCollectionInstance("java.util.LinkedList");
29+
assertInstanceOf(LinkedList.class, actualResult);
30+
31+
actualResult = ReflectUtil.getCollectionInstance("java.util.Collections$EmptyList");
32+
assertInstanceOf(Collections.emptyList().getClass(), actualResult);
33+
34+
actualResult = ReflectUtil.getCollectionInstance("java.util.HashSet");
35+
assertInstanceOf(HashSet.class, actualResult);
36+
37+
actualResult = ReflectUtil.getCollectionInstance("java.util.LinkedHashSet");
38+
assertInstanceOf(LinkedHashSet.class, actualResult);
39+
40+
actualResult = ReflectUtil.getCollectionInstance("java.util.TreeSet");
41+
assertInstanceOf(TreeSet.class, actualResult);
42+
43+
actualResult = ReflectUtil.getCollectionInstance("java.util.Collections$EmptySet");
44+
assertInstanceOf(Collections.emptySet().getClass(), actualResult);
45+
46+
actualResult = ReflectUtil.getCollectionInstance("java.util.ArrayDeque");
47+
assertInstanceOf(ArrayDeque.class, actualResult);
48+
49+
actualResult = ReflectUtil.getCollectionInstance("java.util.HashMap");
50+
assertNull(actualResult);
51+
}
1452
}

arex-instrumentation-api/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@
2828
<groupId>com.google.auto.service</groupId>
2929
<artifactId>auto-service</artifactId>
3030
</dependency>
31+
<dependency>
32+
<groupId>com.fasterxml.jackson.core</groupId>
33+
<artifactId>jackson-databind</artifactId>
34+
<version>${jackson.version}</version>
35+
<scope>test</scope>
36+
</dependency>
37+
<dependency>
38+
<groupId>com.google.code.gson</groupId>
39+
<artifactId>gson</artifactId>
40+
<version>${gson.version}</version>
41+
<scope>test</scope>
42+
</dependency>
3143
</dependencies>
3244

3345
</project>

arex-instrumentation-api/src/main/java/io/arex/inst/runtime/serializer/Serializer.java

Lines changed: 78 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.arex.inst.runtime.serializer;
22

3+
import io.arex.agent.bootstrap.util.ArrayUtils;
34
import io.arex.agent.bootstrap.util.CollectionUtil;
5+
import io.arex.agent.bootstrap.util.ReflectUtil;
46
import io.arex.agent.bootstrap.util.StringUtil;
57
import io.arex.inst.runtime.log.LogManager;
68
import io.arex.inst.runtime.util.TypeUtil;
@@ -24,7 +26,6 @@ public static Builder builder(List<StringSerializable> serializableList) {
2426
}
2527

2628
public static final String EMPTY_LIST_JSON = "[]";
27-
private static final String NESTED_LIST = "java.util.ArrayList-java.util.ArrayList";
2829
private static final String HASH_MAP_VALUES_CLASS = "java.util.HashMap$Values";
2930
private static final String ARRAY_LIST_CLASS = "java.util.ArrayList";
3031
public static final String SERIALIZE_SEPARATOR = "A@R#E$X";
@@ -40,20 +41,31 @@ public static String serializeWithException(Object object, String serializer) th
4041
return null;
4142
}
4243

43-
String typeName = TypeUtil.getName(object);
44-
if (typeName.contains(NESTED_LIST)) {
45-
StringBuilder jsonBuilder = new StringBuilder();
46-
List<List<?>> ans = (List<List<?>>) object;
47-
for (int i = 0; i < ans.size(); i++) {
48-
jsonBuilder.append(serializeWithException(ans.get(i), serializer));
49-
if (i == ans.size() - 1) {
50-
continue;
51-
}
44+
Collection<Collection<?>> nestedCollection = TypeUtil.toNestedCollection(object);
45+
if (nestedCollection != null) {
46+
return serializeNestedCollection(serializer, nestedCollection);
47+
}
48+
49+
return INSTANCE.getSerializer(serializer).serialize(object);
50+
}
51+
52+
private static String serializeNestedCollection(String serializer, Collection<Collection<?>> nestedCollection) throws Throwable {
53+
StringBuilder jsonBuilder = new StringBuilder();
54+
Iterator<Collection<?>> collectionIterator = nestedCollection.iterator();
55+
while (collectionIterator.hasNext()) {
56+
Collection<?> collection = collectionIterator.next();
57+
if (collection == null) {
58+
jsonBuilder.append(NULL_STRING);
59+
} else if (collection.isEmpty()) {
60+
jsonBuilder.append(EMPTY_LIST_JSON);
61+
} else {
62+
jsonBuilder.append(serializeWithException(collection, serializer));
63+
}
64+
if (collectionIterator.hasNext()) {
5265
jsonBuilder.append(SERIALIZE_SEPARATOR);
5366
}
54-
return jsonBuilder.toString();
5567
}
56-
return INSTANCE.getSerializer(serializer).serialize(object);
68+
return jsonBuilder.toString();
5769
}
5870

5971
/**
@@ -78,10 +90,6 @@ public static String serialize(Object object, String serializer) {
7890
}
7991
}
8092

81-
public static Serializer getINSTANCE() {
82-
return INSTANCE;
83-
}
84-
8593
/**
8694
* Deserialize by Class
8795
*
@@ -133,12 +141,11 @@ public static <T> T deserialize(String value, Type type) {
133141
* @param typeName Complex type name, example: java.util.ArrayList-java.util.ArrayList,com.xxx.XXXType
134142
* @return T
135143
*/
136-
public static <T> T deserialize(String value, String typeName) {
144+
public static <T> T deserialize(String value, String typeName, String serializer) {
137145
if (StringUtil.isEmpty(value) || StringUtil.isEmpty(typeName)) {
138146
return null;
139147
}
140148

141-
String serializer = null;
142149
if (typeName.endsWith("Exception")) {
143150
serializer = "gson";
144151
}
@@ -147,43 +154,64 @@ public static <T> T deserialize(String value, String typeName) {
147154
return (T) restoreHashMapValues(value, typeName, serializer);
148155
}
149156

150-
if (!typeName.contains(NESTED_LIST)) {
151-
return deserialize(value, TypeUtil.forName(typeName), serializer);
157+
String[] typeNames = StringUtil.split(typeName, '-');
158+
if (ArrayUtils.isNotEmpty(typeNames) && TypeUtil.isCollection(typeNames[0])) {
159+
String[] innerTypeNames = StringUtil.split(typeNames[1], ',');
160+
if (ArrayUtils.isNotEmpty(innerTypeNames) && TypeUtil.isCollection(innerTypeNames[0])) {
161+
return (T) deserializeNestedCollection(value, typeNames[0], innerTypeNames, serializer);
162+
}
152163
}
153164

154-
try {
155-
// Divide the json string according to the object separator added during serialization
156-
String[] jsonList = StringUtil.splitByWholeSeparator(value, SERIALIZE_SEPARATOR);
157-
List<List<?>> list = new ArrayList<>(jsonList.length);
165+
return deserialize(value, TypeUtil.forName(typeName), serializer);
166+
}
158167

159-
String innerElementClass = StringUtil.substring(typeName, NESTED_LIST.length() + 1);
160-
String[] innerElementClasses = StringUtil.split(innerElementClass, ',');
168+
public static <T> T deserialize(String value, String typeName) {
169+
return deserialize(value, typeName, null);
170+
}
161171

162-
int elementIndex = 0;
163-
for (String innerJson : jsonList) {
164-
if (EMPTY_LIST_JSON.equals(innerJson)) {
165-
list.add(new ArrayList<>());
166-
continue;
167-
}
172+
/**
173+
* Deserialize nested collection
174+
* @param json json string
175+
* @param collectionType type name eg: java.util.HashSet-java.util.HashSet,java.lang.String,java.lang.String
176+
* @param serializer serializer
177+
*/
178+
private static <T> Collection<Collection<T>> deserializeNestedCollection(String json, String collectionType,
179+
String[] innerCollectionType, String serializer) {
180+
Collection<Collection<T>> collection = ReflectUtil.getCollectionInstance(collectionType);
181+
if (collection == null) {
182+
return null;
183+
}
168184

169-
if (NULL_STRING.equals(innerJson)) {
170-
list.add(null);
171-
continue;
172-
}
185+
if (ArrayUtils.isEmpty(innerCollectionType)) {
186+
return collection;
187+
}
173188

174-
if (innerElementClasses != null && innerElementClasses.length > elementIndex) {
175-
// The intercepted value and TypeName are deserialized in one-to-one correspondence
176-
String innerListTypeName = String.format("java.util.ArrayList-%s", innerElementClasses[elementIndex]);
177-
list.add(deserialize(innerJson, TypeUtil.forName(innerListTypeName), serializer));
178-
elementIndex++;
179-
}
189+
// Divide the json string according to the object separator added during serialization
190+
String[] jsonArray = StringUtil.splitByWholeSeparator(json, SERIALIZE_SEPARATOR);
191+
192+
int elementIndex = 1;
193+
StringBuilder builder = new StringBuilder();
194+
for (String innerJson : jsonArray) {
195+
if (EMPTY_LIST_JSON.equals(innerJson)) {
196+
collection.add(ReflectUtil.getCollectionInstance(innerCollectionType[0]));
197+
continue;
180198
}
181199

182-
return (T) list;
183-
} catch (Throwable ex) {
184-
LogManager.warn("serializer-deserialize-typeName", StringUtil.format("can not deserialize value %s to class %s, cause: %s", value, typeName, ex.toString()));
185-
return null;
200+
if (StringUtil.isNullWord(innerJson)) {
201+
collection.add(null);
202+
continue;
203+
}
204+
205+
if (innerCollectionType.length > elementIndex) {
206+
// The intercepted value and TypeName are deserialized in one-to-one correspondence
207+
builder.append(innerCollectionType[0]).append('-').append(innerCollectionType[elementIndex]);
208+
collection.add(deserialize(innerJson, TypeUtil.forName(builder.toString()), serializer));
209+
builder.setLength(0);
210+
elementIndex++;
211+
}
186212
}
213+
214+
return collection;
187215
}
188216

189217
private static Collection<?> restoreHashMapValues(String value, String typeName, String serializer) {
@@ -201,6 +229,10 @@ private static Collection<?> restoreHashMapValues(String value, String typeName,
201229
return map.values();
202230
}
203231

232+
public static Serializer getINSTANCE() {
233+
return INSTANCE;
234+
}
235+
204236
public Map<String, StringSerializable> getSerializers() {
205237
return serializers;
206238
}

0 commit comments

Comments
 (0)