Skip to content

Commit 01f10d6

Browse files
committed
Add JSON abstraction for JSON RPC support
[#159302201] Fixes #378
1 parent e28b34a commit 01f10d6

15 files changed

+246
-227
lines changed

src/main/java/com/rabbitmq/tools/json/JSONReader.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
import java.util.List;
4545
import java.util.Map;
4646

47+
/**
48+
* Will be removed in 6.0
49+
*
50+
* @deprecated Use a third-party JSON library, e.g. Jackson or GJSON
51+
*/
4752
public class JSONReader {
4853

4954
private static final Object OBJECT_END = new Object();

src/main/java/com/rabbitmq/tools/json/JSONSerializable.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818

1919
/**
2020
* Interface for classes that wish to control their own serialization.
21+
*
22+
* Will be removed in 6.0
23+
*
24+
* @deprecated Use a third-party JSON library, e.g. Jackson or GJSON
2125
*/
2226
public interface JSONSerializable {
2327
/**

src/main/java/com/rabbitmq/tools/json/JSONUtil.java

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
// If you have any questions regarding licensing, please contact us at
1414
// info@rabbitmq.com.
1515

16-
1716
package com.rabbitmq.tools.json;
1817

1918
import org.slf4j.Logger;
@@ -34,54 +33,52 @@
3433
*/
3534
public class JSONUtil {
3635

37-
private static final Logger LOGGER = LoggerFactory.getLogger(JSONUtil.class);
36+
private static final Logger LOGGER = LoggerFactory.getLogger(JSONUtil.class);
37+
3838
/**
3939
* Uses reflection to fill public fields and Bean properties of
4040
* the target object from the source Map.
4141
*/
4242
public static Object fill(Object target, Map<String, Object> source)
43-
throws IntrospectionException, IllegalAccessException, InvocationTargetException
44-
{
45-
return fill(target, source, true);
43+
throws IntrospectionException, IllegalAccessException, InvocationTargetException {
44+
return fill(target, source, true);
4645
}
4746

4847
/**
4948
* Uses reflection to fill public fields and optionally Bean
5049
* properties of the target object from the source Map.
5150
*/
5251
public static Object fill(Object target, Map<String, Object> source, boolean useProperties)
53-
throws IntrospectionException, IllegalAccessException, InvocationTargetException
54-
{
55-
if (useProperties) {
56-
BeanInfo info = Introspector.getBeanInfo(target.getClass());
52+
throws IntrospectionException, IllegalAccessException, InvocationTargetException {
53+
if (useProperties) {
54+
BeanInfo info = Introspector.getBeanInfo(target.getClass());
5755

58-
PropertyDescriptor[] props = info.getPropertyDescriptors();
59-
for (int i = 0; i < props.length; ++i) {
60-
PropertyDescriptor prop = props[i];
61-
String name = prop.getName();
62-
Method setter = prop.getWriteMethod();
63-
if (setter != null && !Modifier.isStatic(setter.getModifiers())) {
64-
setter.invoke(target, source.get(name));
65-
}
66-
}
67-
}
56+
PropertyDescriptor[] props = info.getPropertyDescriptors();
57+
for (int i = 0; i < props.length; ++i) {
58+
PropertyDescriptor prop = props[i];
59+
String name = prop.getName();
60+
Method setter = prop.getWriteMethod();
61+
if (setter != null && !Modifier.isStatic(setter.getModifiers())) {
62+
setter.invoke(target, source.get(name));
63+
}
64+
}
65+
}
6866

69-
Field[] ff = target.getClass().getDeclaredFields();
70-
for (int i = 0; i < ff.length; ++i) {
71-
Field field = ff[i];
67+
Field[] ff = target.getClass().getDeclaredFields();
68+
for (int i = 0; i < ff.length; ++i) {
69+
Field field = ff[i];
7270
int fieldMod = field.getModifiers();
73-
if (Modifier.isPublic(fieldMod) && !(Modifier.isFinal(fieldMod) ||
74-
Modifier.isStatic(fieldMod)))
75-
{
76-
try {
77-
field.set(target, source.get(field.getName()));
78-
} catch (IllegalArgumentException iae) {
79-
// no special error processing required
71+
if (Modifier.isPublic(fieldMod) && !(Modifier.isFinal(fieldMod) ||
72+
Modifier.isStatic(fieldMod))) {
73+
try {
74+
field.set(target, source.get(field.getName()));
75+
} catch (IllegalArgumentException iae) {
76+
// no special error processing required
8077
}
81-
}
82-
}
78+
}
79+
}
8380

84-
return target;
81+
return target;
8582
}
8683

8784
/**
@@ -90,14 +87,14 @@ public static Object fill(Object target, Map<String, Object> source, boolean use
9087
* source Map.
9188
*/
9289
public static void tryFill(Object target, Map<String, Object> source) {
93-
try {
94-
fill(target, source);
95-
} catch (IntrospectionException ie) {
96-
LOGGER.error("Error in tryFill", ie);
97-
} catch (IllegalAccessException iae) {
98-
LOGGER.error("Error in tryFill", iae);
99-
} catch (InvocationTargetException ite) {
100-
LOGGER.error("Error in tryFill", ite);
101-
}
90+
try {
91+
fill(target, source);
92+
} catch (IntrospectionException ie) {
93+
LOGGER.error("Error in tryFill", ie);
94+
} catch (IllegalAccessException iae) {
95+
LOGGER.error("Error in tryFill", iae);
96+
} catch (InvocationTargetException ite) {
97+
LOGGER.error("Error in tryFill", ite);
98+
}
10299
}
103100
}

src/main/java/com/rabbitmq/tools/json/JSONWriter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@
5353
import java.util.Map;
5454
import java.util.Set;
5555

56+
/**
57+
* Will be removed in 6.0
58+
* @deprecated Use a third-party JSON library, e.g. Jackson or GJSON
59+
*/
5660
public class JSONWriter {
5761
private boolean indentMode = false;
5862
private int indentLevel = 0;

src/main/java/com/rabbitmq/tools/jsonrpc/DefaultJsonRpcMapper.java

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,27 @@
1818
import com.rabbitmq.tools.json.JSONReader;
1919
import com.rabbitmq.tools.json.JSONWriter;
2020

21-
import java.lang.reflect.Method;
2221
import java.util.List;
2322
import java.util.Map;
2423

2524
/**
25+
* Simple {@link JsonRpcMapper} based on homegrown JSON utilities.
26+
* Handles integers, doubles, strings, booleans, and arrays of those types.
27+
* <p>
28+
* For a more comprehensive set of features, use {@link JacksonJsonRpcMapper}.
29+
* <p>
30+
* Will be removed in 6.0
2631
*
32+
* @see JsonRpcMapper
33+
* @see JacksonJsonRpcMapper
34+
* @since 5.4.0
35+
* @deprecated use {@link JacksonJsonRpcMapper} instead
2736
*/
2837
public class DefaultJsonRpcMapper implements JsonRpcMapper {
2938

3039
@Override
3140
public JsonRpcRequest parse(String requestBody, ServiceDescription description) {
32-
Map<String, Object> request = (Map<String,Object>) new JSONReader().read(requestBody);
33-
41+
Map<String, Object> request = (Map<String, Object>) new JSONReader().read(requestBody);
3442
return new JsonRpcRequest(
3543
request.get("id"), request.get("version").toString(), request.get("method").toString(),
3644
((List<?>) request.get("params")).toArray()
@@ -52,40 +60,11 @@ public JsonRpcResponse parse(String responseBody, Class<?> expectedType) {
5260
error
5361
);
5462
}
55-
return new JsonRpcResponse(map, map.get("result"), map.get("error"), exception);
63+
return new JsonRpcResponse(map.get("result"), map.get("error"), exception);
5664
}
5765

5866
@Override
5967
public String write(Object input) {
6068
return new JSONWriter().write(input);
6169
}
62-
63-
/*
64-
@Override
65-
public Object[] parameters(JsonRpcRequest request, Method method) {
66-
Object[] parameters = request.getParameters();
67-
Object[] convertedParameters = new Object[parameters.length];
68-
Class<?>[] parameterTypes = method.getParameterTypes();
69-
for (int i = 0; i < parameters.length; i++) {
70-
convertedParameters[i] = convert(parameters[i], parameterTypes[i]);
71-
}
72-
return convertedParameters;
73-
}
74-
75-
76-
77-
protected Object convert(Object input, Class<?> expectedClass) {
78-
return input;
79-
// if (input == null || input.getClass().equals(expectedClass)) {
80-
// return input;
81-
// }
82-
// System.err.println(input.getClass() + " " + expectedClass);
83-
// if (Long.class.equals(expectedClass) && input instanceof Integer) {
84-
// return Long.valueOf(((Integer) input).longValue());
85-
// } else if (long.class.equals(expectedClass) && input instanceof Integer) {
86-
// return
87-
// }
88-
// return input;
89-
}
90-
*/
9170
}

src/main/java/com/rabbitmq/tools/jsonrpc/JacksonJsonRpcMapper.java

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import com.fasterxml.jackson.databind.MappingJsonFactory;
2424
import com.fasterxml.jackson.databind.ObjectMapper;
2525
import com.fasterxml.jackson.databind.node.ValueNode;
26-
import com.rabbitmq.tools.json.JSONReader;
27-
import com.rabbitmq.tools.json.JSONWriter;
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
2828

2929
import java.io.IOException;
3030
import java.lang.reflect.Method;
@@ -33,10 +33,16 @@
3333
import java.util.Map;
3434

3535
/**
36+
* {@link JsonRpcMapper} based on Jackson.
37+
* Uses the streaming and databind modules.
3638
*
39+
* @see JsonRpcMapper
40+
* @since 5.4.0
3741
*/
3842
public class JacksonJsonRpcMapper implements JsonRpcMapper {
3943

44+
private static final Logger LOGGER = LoggerFactory.getLogger(JacksonJsonRpcMapper.class);
45+
4046
private final ObjectMapper mapper;
4147

4248
public JacksonJsonRpcMapper(ObjectMapper mapper) {
@@ -62,7 +68,21 @@ public JsonRpcRequest parse(String requestBody, ServiceDescription description)
6268
if ("method".equals(name)) {
6369
method = parser.getValueAsString();
6470
} else if ("id".equals(name)) {
65-
// FIXME parse id, can be any type (handle only primitive and wrapper)
71+
TreeNode node = parser.readValueAsTree();
72+
if (node instanceof ValueNode) {
73+
ValueNode idNode = (ValueNode) node;
74+
if (idNode.isNull()) {
75+
id = null;
76+
} else if (idNode.isTextual()) {
77+
id = idNode.asText();
78+
} else if (idNode.isNumber()) {
79+
id = Long.valueOf(idNode.asLong());
80+
} else {
81+
LOGGER.warn("ID type not null, text, or number {}, ignoring", idNode);
82+
}
83+
} else {
84+
LOGGER.warn("ID not a scalar value {}, ignoring", node);
85+
}
6686
} else if ("version".equals(name)) {
6787
version = parser.getValueAsString();
6888
} else if ("params".equals(name)) {
@@ -80,6 +100,10 @@ public JsonRpcRequest parse(String requestBody, ServiceDescription description)
80100
throw new JsonRpcMappingException("Error during JSON parsing", e);
81101
}
82102

103+
if (method == null) {
104+
throw new IllegalArgumentException("Could not find method to invoke in request");
105+
}
106+
83107
List<Object> convertedParameters = new ArrayList<>(parameters.size());
84108
if (!parameters.isEmpty()) {
85109
ProcedureDescription proc = description.getProcedure(method, parameters.size());
@@ -102,69 +126,40 @@ public JsonRpcRequest parse(String requestBody, ServiceDescription description)
102126
);
103127
}
104128

105-
protected Object convert(TreeNode node, Class<?> expectedType) throws IOException {
106-
Object value;
107-
if (expectedType.isPrimitive()) {
108-
ValueNode valueNode = (ValueNode) node;
109-
if (expectedType == Boolean.TYPE) {
110-
value = valueNode.booleanValue();
111-
} else if (expectedType == Character.TYPE) {
112-
value = valueNode.textValue().charAt(0);
113-
} else if (expectedType == Short.TYPE) {
114-
value = valueNode.shortValue();
115-
} else if (expectedType == Integer.TYPE) {
116-
value = valueNode.intValue();
117-
} else if (expectedType == Long.TYPE) {
118-
value = valueNode.longValue();
119-
} else if (expectedType == Float.TYPE) {
120-
value = valueNode.floatValue();
121-
} else if (expectedType == Double.TYPE) {
122-
value = valueNode.doubleValue();
123-
} else {
124-
throw new IllegalArgumentException("Primitive type not supported: " + expectedType);
125-
}
126-
} else {
127-
value = mapper.readValue(node.traverse(), expectedType);
128-
}
129-
return value;
130-
}
131-
132129
@Override
133130
public JsonRpcResponse parse(String responseBody, Class<?> expectedReturnType) {
134131
JsonFactory jsonFactory = new MappingJsonFactory();
135132
Object result = null;
133+
JsonRpcException exception = null;
134+
Map<String, Object> errorMap = null;
136135
try (JsonParser parser = jsonFactory.createParser(responseBody)) {
137136
while (parser.nextToken() != null) {
138137
JsonToken token = parser.currentToken();
139138
if (token == JsonToken.FIELD_NAME) {
140139
String name = parser.currentName();
141-
parser.nextToken();
142140
if ("result".equals(name)) {
141+
parser.nextToken();
143142
if (expectedReturnType == Void.TYPE) {
144143
result = null;
145144
} else {
146145
result = convert(parser.readValueAsTree(), expectedReturnType);
147146
}
147+
} else if ("error".equals(name)) {
148+
errorMap = (Map<String, Object>) convert(parser.readValueAsTree(), Map.class);
149+
exception = new JsonRpcException(
150+
errorMap.toString(),
151+
(String) errorMap.get("name"),
152+
errorMap.get("code") == null ? 0 : (Integer) errorMap.get("code"),
153+
(String) errorMap.get("message"),
154+
errorMap
155+
);
148156
}
149157
}
150158
}
151159
} catch (IOException e) {
152160
throw new JsonRpcMappingException("Error during JSON parsing", e);
153161
}
154-
Map<String, Object> map = (Map<String, Object>) (new JSONReader().read(responseBody));
155-
Map<String, Object> error;
156-
JsonRpcException exception = null;
157-
if (map.containsKey("error")) {
158-
error = (Map<String, Object>) map.get("error");
159-
exception = new JsonRpcException(
160-
new JSONWriter().write(error),
161-
(String) error.get("name"),
162-
error.get("code") == null ? 0 : (Integer) error.get("code"),
163-
(String) error.get("message"),
164-
error
165-
);
166-
}
167-
return new JsonRpcResponse(map, result, map.get("error"), exception);
162+
return new JsonRpcResponse(result, errorMap, exception);
168163
}
169164

170165
@Override
@@ -175,4 +170,31 @@ public String write(Object input) {
175170
throw new JsonRpcMappingException("Error during JSON serialization", e);
176171
}
177172
}
173+
174+
protected Object convert(TreeNode node, Class<?> expectedType) throws IOException {
175+
Object value;
176+
if (expectedType.isPrimitive()) {
177+
ValueNode valueNode = (ValueNode) node;
178+
if (expectedType == Boolean.TYPE) {
179+
value = valueNode.booleanValue();
180+
} else if (expectedType == Character.TYPE) {
181+
value = valueNode.textValue().charAt(0);
182+
} else if (expectedType == Short.TYPE) {
183+
value = valueNode.shortValue();
184+
} else if (expectedType == Integer.TYPE) {
185+
value = valueNode.intValue();
186+
} else if (expectedType == Long.TYPE) {
187+
value = valueNode.longValue();
188+
} else if (expectedType == Float.TYPE) {
189+
value = valueNode.floatValue();
190+
} else if (expectedType == Double.TYPE) {
191+
value = valueNode.doubleValue();
192+
} else {
193+
throw new IllegalArgumentException("Primitive type not supported: " + expectedType);
194+
}
195+
} else {
196+
value = mapper.readValue(node.traverse(), expectedType);
197+
}
198+
return value;
199+
}
178200
}

0 commit comments

Comments
 (0)