Skip to content

Commit

Permalink
dynamic methods with tests;
Browse files Browse the repository at this point in the history
  • Loading branch information
kostapc committed Feb 17, 2017
1 parent 3728cee commit 86d510e
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 26 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Expand Up @@ -17,7 +17,7 @@ plugins {

description = 'This project aims to provide the facility to easily implement JSON-RPC for the java programming language.'
version = '1.5.0'
group = 'com.github.briandilley.jsonrpc4j'
group = 'com.github.briandilley.jsonrpc4j-dynamic'

sourceCompatibility = 1.7
targetCompatibility = 1.7
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Jan 11 14:37:59 GMT 2017
#Fri Feb 17 20:25:46 GMT+03:00 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
131 changes: 108 additions & 23 deletions src/main/java/com/googlecode/jsonrpc4j/JsonRpcBasicServer.java
Expand Up @@ -5,9 +5,8 @@
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.*;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.googlecode.jsonrpc4j.ErrorResolver.JsonError;
import net.iharder.Base64;
import org.slf4j.Logger;
Expand All @@ -18,19 +17,11 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.UndeclaredThrowableException;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.logging.Level;
import java.util.regex.Pattern;

import static com.googlecode.jsonrpc4j.ErrorResolver.JsonError.ERROR_NOT_HANDLED;
Expand Down Expand Up @@ -443,6 +434,31 @@ protected String getMethodName(final String methodName) {
protected Object getHandler(String serviceName) {
return handler;
}

private static Class getJavaTypeForJsonType(JsonNodeType jsonType) {
switch (jsonType) {
case ARRAY:
return List.class;
case BINARY:
return Object.class;
case BOOLEAN:
return Boolean.class;
case MISSING:
return Object.class;
case NULL:
return Object.class;
case NUMBER:
return Double.class;
case OBJECT:
return Object.class;
case POJO:
return Object.class;
case STRING:
return String.class;
default:
return Object.class;
}
}

/**
* Invokes the given method on the {@code handler} passing
Expand All @@ -460,12 +476,46 @@ protected Object getHandler(String serviceName) {
*/
private JsonNode invoke(Object target, Method method, List<JsonNode> params) throws IOException, IllegalAccessException, InvocationTargetException {
logger.debug("Invoking method: {} with args {}", method.getName(), params);
Object[] convertedParams = convertJsonToParameters(method, params);

Object[] convertedParams;

/*Object[] convertedParams = convertJsonToParameters(method, params);
if (convertedParameterTransformer != null) {
convertedParams = convertedParameterTransformer.transformConvertedParameters(target, convertedParams);
}
Object result = method.invoke(target, convertedParams);
}*/
//Object result = method.invoke(target, convertedParams);
Object result;

if(method.getGenericParameterTypes().length==1 && method.isVarArgs()) {
convertedParams = new Object[params.size()];
ObjectMapper mapper = new ObjectMapper();

for (int i = 0; i < params.size(); i++) {
JsonNode jsonNode = params.get(i);
Class type = getJavaTypeForJsonType(jsonNode.getNodeType());
Object object = mapper.convertValue(jsonNode, type);
logger.debug(String.format(
"[%s] param: %s -> %s",
method.getName(), i, type.getName()
));
convertedParams[i] = object;
}

result = method.invoke(target, new Object[] {convertedParams});

} else {
convertedParams = new Object[params.size()];
Type[] parameterTypes = method.getGenericParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
JsonParser paramJsonParser = mapper.treeAsTokens(params.get(i));
JavaType paramJavaType = TypeFactory.defaultInstance().constructType(parameterTypes[i]);
convertedParams[i] = mapper.readValue(paramJsonParser, paramJavaType);
}
result = method.invoke(target, convertedParams);
}

logger.debug("Invoked method: {}, result {}", method.getName(), result);

return hasReturnValue(method) ? mapper.valueToTree(result) : null;
}

Expand Down Expand Up @@ -557,12 +607,47 @@ private ObjectNode createResponseSuccess(String jsonRpc, Object id, JsonNode res
* @return the {@link AMethodWithItsArgs}
*/
private AMethodWithItsArgs findBestMethodByParamsNode(Set<Method> methods, JsonNode paramsNode) {
if (hasNoParameters(paramsNode)) return findBestMethodUsingParamIndexes(methods, 0, null);
if (paramsNode.isArray())
return findBestMethodUsingParamIndexes(methods, paramsNode.size(), ArrayNode.class.cast(paramsNode));
if (paramsNode.isObject())
return findBestMethodUsingParamNames(methods, collectFieldNames(paramsNode), ObjectNode.class.cast(paramsNode));
throw new IllegalArgumentException("Unknown params node type: " + paramsNode.toString());
if (hasNoParameters(paramsNode)) {
return findBestMethodUsingParamIndexes(methods, 0, null);
}
AMethodWithItsArgs matchedMethod = null;
if (paramsNode.isArray()) {
matchedMethod = findBestMethodUsingParamIndexes(methods, paramsNode.size(), ArrayNode.class.cast(paramsNode));
} else if (paramsNode.isObject()) {
matchedMethod = findBestMethodUsingParamNames(methods, collectFieldNames(paramsNode), ObjectNode.class.cast(paramsNode));
} else {
throw new IllegalArgumentException("Unknown params node type: " + paramsNode.toString());
}
if(matchedMethod==null) {
for (Method method : methods) {
if(method.getParameterTypes().length!=1) {
continue;
}
if(method.isVarArgs()) {
matchedMethod = new AMethodWithItsArgs(method);

if (paramsNode.isArray()) {
ArrayNode arrayNode = ArrayNode.class.cast(paramsNode);
for (int i = 0; i < paramsNode.size(); i++) {
matchedMethod.arguments.add(arrayNode.get(i));
}
}

if (paramsNode.isObject()) {
ObjectNode objectNode = ObjectNode.class.cast(paramsNode);
Iterator<Map.Entry<String, JsonNode>> items = objectNode.fields();
while (items.hasNext()) {
Map.Entry<String, JsonNode> item = items.next();
JsonNode name = JsonNodeFactory.instance.objectNode().put(item.getKey(),item.getKey());
matchedMethod.arguments.add(name.get(item.getKey()));
matchedMethod.arguments.add(item.getValue());
}
}
break;
}
}
}
return matchedMethod;
}

private Set<String> collectFieldNames(JsonNode paramsNode) {
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/googlecode/jsonrpc4j/VarArgsUtil.java
@@ -0,0 +1,24 @@
package com.googlecode.jsonrpc4j;

import java.util.HashMap;
import java.util.Map;

/**
* 20.04.2016
* KostaPC
*/

public class VarArgsUtil {

public static Map<String, Object> convertArgs(Object[] params) {

final Map<String, Object> unsafeMap = new HashMap<>();
for (int i = 0; i < params.length; i += 2) {
if(params[i] instanceof String && params[i]!=null && !params[i].toString().isEmpty()) {
unsafeMap.put(params[i].toString(), params[i + 1]);
}
}
return unsafeMap;

}
}
@@ -0,0 +1,70 @@
package com.googlecode.jsonrpc4j.server;

import com.fasterxml.jackson.databind.JsonNode;
import com.googlecode.jsonrpc4j.JsonRpcBasicServer;
import com.googlecode.jsonrpc4j.JsonRpcMethod;
import org.easymock.EasyMockRunner;
import org.easymock.Mock;
import org.easymock.MockType;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import static com.googlecode.jsonrpc4j.JsonRpcBasicServer.RESULT;
import static com.googlecode.jsonrpc4j.util.Util.*;

@RunWith(EasyMockRunner.class)
public class JsonRpcServerAnnotateMethodVarArgsTest {

@Mock(type = MockType.NICE)
private ServiceInterfaceWithCustomMethodNameWithVarArgsAnnotation mockService;

private ByteArrayOutputStream byteArrayOutputStream;
private JsonRpcBasicServer jsonRpcServerAnnotatedMethod;

@Before
public void setup() {
byteArrayOutputStream = new ByteArrayOutputStream();
jsonRpcServerAnnotatedMethod = new JsonRpcBasicServer(mapper, mockService, ServiceInterfaceWithCustomMethodNameWithVarArgsAnnotation.class);
}

@Test
public void callMethodWithVarArgParameters() throws Exception {
jsonRpcServerAnnotatedMethod.handleRequest(
messageWithMapParamsStream(
"testMethodVararg",
"argOne","one",
"argTwo",2,
"argThree","three",
"argFour", 4,
"argFive", (Object)"five",
"argSix", 6.0f,
"argSeven", 7d
),
byteArrayOutputStream
);

System.out.println("res: "+result());

}

private JsonNode result() throws IOException {
return decodeAnswer(byteArrayOutputStream).get(RESULT);
}


public interface ServiceInterfaceWithCustomMethodNameWithVarArgsAnnotation {
@JsonRpcMethod("Test.custom")
String customMethod();

@JsonRpcMethod("Test.custom2")
String customMethod2(String stringParam1);

@JsonRpcMethod("testMethodVararg")
String testMethodVararg(Object... params);
}

}

0 comments on commit 86d510e

Please sign in to comment.