Skip to content

Commit e28b34a

Browse files
committed
Adding Jackson support in JSON RPC
WIP References #378
1 parent 0a3bed1 commit e28b34a

File tree

12 files changed

+724
-269
lines changed

12 files changed

+724
-269
lines changed

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<slf4j.version>1.7.25</slf4j.version>
5858
<metrics.version>3.2.6</metrics.version>
5959
<micrometer.version>1.0.2</micrometer.version>
60+
<jackson.version>2.9.6</jackson.version>
6061
<logback.version>1.2.3</logback.version>
6162
<commons-cli.version>1.1</commons-cli.version>
6263
<junit.version>4.12</junit.version>
@@ -640,6 +641,12 @@
640641
<version>${micrometer.version}</version>
641642
<optional>true</optional>
642643
</dependency>
644+
<dependency>
645+
<groupId>com.fasterxml.jackson.core</groupId>
646+
<artifactId>jackson-databind</artifactId>
647+
<version>${jackson.version}</version>
648+
<optional>true</optional>
649+
</dependency>
643650
<dependency>
644651
<groupId>commons-cli</groupId>
645652
<artifactId>commons-cli</artifactId>

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

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
// Copyright (c) 2018 Pivotal Software, Inc. All rights reserved.
2+
//
3+
// This software, the RabbitMQ Java client library, is triple-licensed under the
4+
// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2
5+
// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see
6+
// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL,
7+
// please see LICENSE-APACHE2.
8+
//
9+
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
10+
// either express or implied. See the LICENSE file for specific language governing
11+
// rights and limitations of this software.
12+
//
13+
// If you have any questions regarding licensing, please contact us at
14+
// info@rabbitmq.com.
15+
116
package com.rabbitmq.tools.jsonrpc;
217

318
import com.rabbitmq.tools.json.JSONReader;
@@ -23,7 +38,7 @@ public JsonRpcRequest parse(String requestBody, ServiceDescription description)
2338
}
2439

2540
@Override
26-
public JsonRpcResponse parse(String responseBody) {
41+
public JsonRpcResponse parse(String responseBody, Class<?> expectedType) {
2742
Map<String, Object> map = (Map<String, Object>) (new JSONReader().read(responseBody));
2843
Map<String, Object> error;
2944
JsonRpcException exception = null;
@@ -40,6 +55,12 @@ public JsonRpcResponse parse(String responseBody) {
4055
return new JsonRpcResponse(map, map.get("result"), map.get("error"), exception);
4156
}
4257

58+
@Override
59+
public String write(Object input) {
60+
return new JSONWriter().write(input);
61+
}
62+
63+
/*
4364
@Override
4465
public Object[] parameters(JsonRpcRequest request, Method method) {
4566
Object[] parameters = request.getParameters();
@@ -51,10 +72,7 @@ public Object[] parameters(JsonRpcRequest request, Method method) {
5172
return convertedParameters;
5273
}
5374
54-
@Override
55-
public String write(Object input) {
56-
return new JSONWriter().write(input);
57-
}
75+
5876
5977
protected Object convert(Object input, Class<?> expectedClass) {
6078
return input;
@@ -69,4 +87,5 @@ protected Object convert(Object input, Class<?> expectedClass) {
6987
// }
7088
// return input;
7189
}
90+
*/
7291
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// Copyright (c) 2018 Pivotal Software, Inc. All rights reserved.
2+
//
3+
// This software, the RabbitMQ Java client library, is triple-licensed under the
4+
// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2
5+
// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see
6+
// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL,
7+
// please see LICENSE-APACHE2.
8+
//
9+
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
10+
// either express or implied. See the LICENSE file for specific language governing
11+
// rights and limitations of this software.
12+
//
13+
// If you have any questions regarding licensing, please contact us at
14+
// info@rabbitmq.com.
15+
16+
package com.rabbitmq.tools.jsonrpc;
17+
18+
import com.fasterxml.jackson.core.JsonFactory;
19+
import com.fasterxml.jackson.core.JsonParser;
20+
import com.fasterxml.jackson.core.JsonProcessingException;
21+
import com.fasterxml.jackson.core.JsonToken;
22+
import com.fasterxml.jackson.core.TreeNode;
23+
import com.fasterxml.jackson.databind.MappingJsonFactory;
24+
import com.fasterxml.jackson.databind.ObjectMapper;
25+
import com.fasterxml.jackson.databind.node.ValueNode;
26+
import com.rabbitmq.tools.json.JSONReader;
27+
import com.rabbitmq.tools.json.JSONWriter;
28+
29+
import java.io.IOException;
30+
import java.lang.reflect.Method;
31+
import java.util.ArrayList;
32+
import java.util.List;
33+
import java.util.Map;
34+
35+
/**
36+
*
37+
*/
38+
public class JacksonJsonRpcMapper implements JsonRpcMapper {
39+
40+
private final ObjectMapper mapper;
41+
42+
public JacksonJsonRpcMapper(ObjectMapper mapper) {
43+
this.mapper = mapper;
44+
}
45+
46+
public JacksonJsonRpcMapper() {
47+
this(new ObjectMapper());
48+
}
49+
50+
@Override
51+
public JsonRpcRequest parse(String requestBody, ServiceDescription description) {
52+
JsonFactory jsonFactory = new MappingJsonFactory();
53+
String method = null, version = null;
54+
final List<TreeNode> parameters = new ArrayList<>();
55+
Object id = null;
56+
try (JsonParser parser = jsonFactory.createParser(requestBody)) {
57+
while (parser.nextToken() != null) {
58+
JsonToken token = parser.currentToken();
59+
if (token == JsonToken.FIELD_NAME) {
60+
String name = parser.currentName();
61+
token = parser.nextToken();
62+
if ("method".equals(name)) {
63+
method = parser.getValueAsString();
64+
} else if ("id".equals(name)) {
65+
// FIXME parse id, can be any type (handle only primitive and wrapper)
66+
} else if ("version".equals(name)) {
67+
version = parser.getValueAsString();
68+
} else if ("params".equals(name)) {
69+
if (token == JsonToken.START_ARRAY) {
70+
while (parser.nextToken() != JsonToken.END_ARRAY) {
71+
parameters.add(parser.readValueAsTree());
72+
}
73+
} else {
74+
throw new IllegalStateException("Field params must be an array");
75+
}
76+
}
77+
}
78+
}
79+
} catch (IOException e) {
80+
throw new JsonRpcMappingException("Error during JSON parsing", e);
81+
}
82+
83+
List<Object> convertedParameters = new ArrayList<>(parameters.size());
84+
if (!parameters.isEmpty()) {
85+
ProcedureDescription proc = description.getProcedure(method, parameters.size());
86+
Method internalMethod = proc.internal_getMethod();
87+
for (int i = 0; i < internalMethod.getParameterCount(); i++) {
88+
TreeNode parameterNode = parameters.get(i);
89+
try {
90+
Class<?> parameterType = internalMethod.getParameterTypes()[i];
91+
Object value = convert(parameterNode, parameterType);
92+
convertedParameters.add(value);
93+
} catch (IOException e) {
94+
throw new JsonRpcMappingException("Error during parameter conversion", e);
95+
}
96+
}
97+
}
98+
99+
return new JsonRpcRequest(
100+
id, version, method,
101+
convertedParameters.toArray()
102+
);
103+
}
104+
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+
132+
@Override
133+
public JsonRpcResponse parse(String responseBody, Class<?> expectedReturnType) {
134+
JsonFactory jsonFactory = new MappingJsonFactory();
135+
Object result = null;
136+
try (JsonParser parser = jsonFactory.createParser(responseBody)) {
137+
while (parser.nextToken() != null) {
138+
JsonToken token = parser.currentToken();
139+
if (token == JsonToken.FIELD_NAME) {
140+
String name = parser.currentName();
141+
parser.nextToken();
142+
if ("result".equals(name)) {
143+
if (expectedReturnType == Void.TYPE) {
144+
result = null;
145+
} else {
146+
result = convert(parser.readValueAsTree(), expectedReturnType);
147+
}
148+
}
149+
}
150+
}
151+
} catch (IOException e) {
152+
throw new JsonRpcMappingException("Error during JSON parsing", e);
153+
}
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);
168+
}
169+
170+
@Override
171+
public String write(Object input) {
172+
try {
173+
return mapper.writeValueAsString(input);
174+
} catch (JsonProcessingException e) {
175+
throw new JsonRpcMappingException("Error during JSON serialization", e);
176+
}
177+
}
178+
}

0 commit comments

Comments
 (0)