com.almende.eve.protocol.jsonrpc.JSONRpc Maven / Gradle / Ivy
/*
* Copyright: Almende B.V. (2014), Rotterdam, The Netherlands
* License: The Apache Software License, Version 2.0
*/
package com.almende.eve.protocol.jsonrpc;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.almende.eve.protocol.auth.Authorizor;
import com.almende.eve.protocol.jsonrpc.NamespaceUtil.CallTuple;
import com.almende.eve.protocol.jsonrpc.annotation.Access;
import com.almende.eve.protocol.jsonrpc.annotation.AccessType;
import com.almende.eve.protocol.jsonrpc.annotation.Name;
import com.almende.eve.protocol.jsonrpc.annotation.Namespace;
import com.almende.eve.protocol.jsonrpc.annotation.Optional;
import com.almende.eve.protocol.jsonrpc.annotation.Sender;
import com.almende.eve.protocol.jsonrpc.formats.JSONRPCException;
import com.almende.eve.protocol.jsonrpc.formats.JSONRequest;
import com.almende.eve.protocol.jsonrpc.formats.JSONResponse;
import com.almende.eve.protocol.jsonrpc.formats.RequestParams;
import com.almende.util.AnnotationUtil;
import com.almende.util.AnnotationUtil.AnnotatedClass;
import com.almende.util.AnnotationUtil.AnnotatedMethod;
import com.almende.util.AnnotationUtil.AnnotatedParam;
import com.almende.util.Defines;
import com.almende.util.TypeUtil;
import com.almende.util.jackson.JOM;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* The Class JSONRPC.
*/
final public class JSONRpc {
private static final Logger LOG = Logger.getLogger(JSONRpc.class.getName());
static {
if (Defines.HASMETHODHANDLES) {
LOG.log(Level.FINE, "Using MethodHandle i.s.o. plain reflection!");
} else {
LOG.log(Level.FINE, "Using plain reflection i.s.o. MethodHandle!");
}
}
/**
* Instantiates a new jsonrpc.
*/
private JSONRpc() {}
// TODO: implement JSONRPC 2.0 Batch
/**
* Invoke a method on an object.
*
* @param destination
* the destination
* @param request
* A request in JSON-RPC format
* @param auth
* the auth
* @return the string
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static String invoke(final Object destination, final String request,
final Authorizor auth) throws IOException {
return invoke(destination, request, null, auth);
}
/**
* Invoke a method on an object.
*
* @param destination
* the destination
* @param request
* A request in JSON-RPC format
* @param requestParams
* Optional request parameters
* @param auth
* the auth
* @return the string
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static String invoke(final Object destination, final String request,
final RequestParams requestParams, final Authorizor auth)
throws IOException {
JSONRequest jsonRequest = null;
JSONResponse jsonResponse = null;
try {
jsonRequest = new JSONRequest(request);
jsonResponse = invoke(destination, jsonRequest, requestParams, auth);
} catch (final JSONRPCException err) {
jsonResponse = new JSONResponse(err);
}
return jsonResponse.toString();
}
/**
* Invoke a method on an object.
*
* @param destination
* destination url
* @param request
* the request
* @param auth
* the auth
* @return the jSON response
*/
public static JSONResponse invoke(final Object destination,
final JSONRequest request, final Authorizor auth) {
return invoke(destination, request, null, auth);
}
/**
* Invoke a method on an object.
*
* @param destination
* the destination
* @param request
* A request in JSON-RPC format
* @param requestParams
* Optional request parameters
* @param auth
* the auth
* @return the jSON response
*/
public static JSONResponse invoke(final Object destination,
final JSONRequest request, final RequestParams requestParams,
final Authorizor auth) {
final JSONResponse resp = new JSONResponse(request.getId(), null);
try {
final CallTuple tuple = NamespaceUtil.get(destination,
request.getMethod());
final Object realDest = tuple.getDestination();
final AnnotatedMethod annotatedMethod = tuple.getMethod();
if (!isAvailable(annotatedMethod, realDest, requestParams, auth)) {
throw new JSONRPCException(
JSONRPCException.CODE.METHOD_NOT_FOUND,
"Method '"
+ request.getMethod()
+ "' not found. The method does not exist or you are not authorized.");
}
final MethodHandle methodHandle = annotatedMethod.getMethodHandle();
final Method method = annotatedMethod.getActualMethod();
Object result;
if (Defines.HASMETHODHANDLES) {
final Object[] params = castParams(realDest,
request.getParams(), annotatedMethod.getParams(),
requestParams);
result = methodHandle.invokeExact(params);
} else {
final Object[] params = castParams(request.getParams(),
annotatedMethod.getParams(), requestParams);
result = method.invoke(realDest, params);
}
if (result == null) {
result = JOM.createNullNode();
}
resp.setResult(result);
} catch (final JSONRPCException err) {
resp.setError(err);
} catch (final Throwable err) {
final Throwable cause = err.getCause();
if (cause instanceof JSONRPCException) {
resp.setError((JSONRPCException) cause);
} else {
if (err instanceof InvocationTargetException && cause != null) {
LOG.log(Level.WARNING,
"Exception raised, returning its cause as JSONRPCException. Request:"
+ request, cause);
final JSONRPCException jsonError = new JSONRPCException(
JSONRPCException.CODE.INTERNAL_ERROR,
getMessage(cause), cause);
jsonError.setData(cause);
resp.setError(jsonError);
} else {
LOG.log(Level.WARNING,
"Exception raised, returning it as JSONRPCException. Request:"
+ request, err);
final JSONRPCException jsonError = new JSONRPCException(
JSONRPCException.CODE.INTERNAL_ERROR,
getMessage(err), err);
jsonError.setData(err);
resp.setError(jsonError);
}
}
}
if (resp.getId() == null || resp.getId().isNull()) {
return null;
} else {
return resp;
}
}
/**
* Validate whether the given class contains valid JSON-RPC methods. A class
* if valid when:
* - There are no public methods with equal names
* - The parameters of all public methods have the @Name annotation
* If the class is not valid, an Exception is thrown
*
* @param c
* The class to be verified
* @param requestParams
* optional request parameters
* @return errors A list with validation errors. When no problems are found,
* an empty list is returned
*/
public static List validate(final Class> c,
final RequestParams requestParams) {
final List errors = new ArrayList(0);
final Set methodNames = new HashSet(10);
AnnotatedClass ac = null;
try {
ac = AnnotationUtil.get(c);
if (ac != null) {
for (final AnnotatedMethod method : ac.getMethods()) {
final boolean available = isAvailable(method, null,
requestParams, null);
if (available) {
// The method name may only occur once
final String name = method.getName();
if (methodNames.contains(name)) {
errors.add("Public method '"
+ name
+ "' is defined more than once, which is not"
+ " allowed for JSON-RPC.");
}
methodNames.add(name);
// TODO: I removed duplicate @Name check. If you reach
// this point the function at least has named
// parameters, due to the isAvailable() call. Should we
// add a duplicates check to isAvailable()?
}
}
}
} catch (final Exception e) {
LOG.log(Level.WARNING, "Problems wrapping class for annotation", e);
errors.add("Class can't be wrapped for annotation, exception raised:"
+ e.getLocalizedMessage());
}
return errors;
}
/**
* _describe.
*
* @param c
* the c
* @param requestParams
* the request params
* @param namespace
* the namespace
* @param auth
* the auth
* @return the map
*/
private static Map _describe(final Object c,
final RequestParams requestParams, String namespace,
final Authorizor auth) {
final Map methods = new TreeMap();
try {
if (c == null) {
return methods;
}
final AnnotatedClass annotatedClass = AnnotationUtil.get(c
.getClass());
for (final AnnotatedMethod method : annotatedClass.getMethods()) {
if (isAvailable(method, null, requestParams, auth)) {
// format as JSON
final List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy