package com.googlecode.jsonrpc4j;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
/**
* A multiple service dispatcher that supports JSON-RPC "method" names
* that use dot-notation to select a server endpoint. For example:
* <pre>
* {
* "jsonrpc": "2.0",
* "method": "service.method",
* "params": {"foo", "bar"},
* "id": 1
* }
* </pre>
* An example of using this class is:
* <code>
* JsonRpcMultiServer rpcServer = new JsonRpcMultiServer();
* rpcServer.addService("Foo", new FooService())
* .addService("Bar", new BarService());
* </code>
* A client can then call a <i>test(String, String)</i> method on the Foo service
* like this:
* <pre>
* {
* "jsonrpc": "2.0",
* "method": "Foo.test",
* "params": ["some", "thing"],
* "id": 1
* }
* </pre>
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class JsonRpcMultiServer extends JsonRpcServer {
public static final char DEFAULT_SEPARATOR = '.';
private static final Logger logger = LoggerFactory.getLogger(JsonRpcMultiServer.class);
private final Map<String, Object> handlerMap;
private final Map<String, Class<?>> interfaceMap;
private char separator = DEFAULT_SEPARATOR;
public JsonRpcMultiServer() {
this(new ObjectMapper());
logger.debug("created empty multi server");
}
public JsonRpcMultiServer(ObjectMapper mapper) {
super(mapper, null);
this.handlerMap = new HashMap<>();
this.interfaceMap = new HashMap<>();
}
public JsonRpcMultiServer addService(String name, Object handler) {
return addService(name, handler, null);
}
public JsonRpcMultiServer addService(String name, Object handler, Class<?> remoteInterface) {
logger.debug("add service interface {} with handler {}", remoteInterface, handler);
handlerMap.put(name, handler);
if (remoteInterface != null) {
interfaceMap.put(name, remoteInterface);
}
return this;
}
public char getSeparator() {
return this.separator;
}
public void setSeparator(char separator) {
this.separator = separator;
}
/**
* Returns the handler's class or interfaces. The serviceName is used
* to look up a registered handler.
*
* @param serviceName the optional name of a service
* @return the class
*/
@Override
protected Class<?>[] getHandlerInterfaces(String serviceName) {
Class<?> remoteInterface = interfaceMap.get(serviceName);
if (remoteInterface != null) {
return new Class<?>[]{remoteInterface};
} else if (Proxy.isProxyClass(getHandler(serviceName).getClass())) {
return getHandler(serviceName).getClass().getInterfaces();
} else {
return new Class<?>[]{getHandler(serviceName).getClass()};
}
}
/**
* Get the service name from the methodNode. JSON-RPC methods with the form
* Service.method will result in "Service" being returned in this case.
*
* @param methodName method name
* @return the name of the service, or <code>null</code>
*/
@Override
protected String getServiceName(final String methodName) {
if (methodName != null) {
int ndx = methodName.indexOf(this.separator);
if (ndx > 0) {
return methodName.substring(0, ndx);
}
}
return methodName;
}
/**
* Get the method name from the methodNode, stripping off the service name.
*
* @param methodName the JsonNode for the method
* @return the name of the method that should be invoked
*/
@Override
protected String getMethodName(final String methodName) {
if (methodName != null) {
int ndx = methodName.indexOf(this.separator);
if (ndx > 0) {
return methodName.substring(ndx + 1);
}
}
return methodName;
}
/**
* Get the handler (object) that should be invoked to execute the specified
* RPC method based on the specified service name.
*
* @param serviceName the service name
* @return the handler to invoke the RPC call against
*/
@Override
protected Object getHandler(String serviceName) {
Object handler = handlerMap.get(serviceName);
if (handler == null) {
logger.error("Service '{}' is not registered in this multi-server", serviceName);
throw new RuntimeException("Service '" + serviceName + "' does not exist");
}
return handler;
}
}