private static final String ERROR_CLASSNAME_EXT = "-error";
+ public static final String UPDATE_VARIABLE_INTERFACE = "v";
public static final String UPDATE_VARIABLE_METHOD = "v";
public static final char VAR_BURST_SEPARATOR = '\u001d';
// note that type is now deduced from value
// TODO could eliminate invocations of same shared variable setter
addMethodInvocationToQueue(new MethodInvocation(paintableId,
- UPDATE_VARIABLE_METHOD, new Object[] { variableName, value }),
- immediate);
+ UPDATE_VARIABLE_INTERFACE, UPDATE_VARIABLE_METHOD,
+ new Object[] { variableName, value }), immediate);
}
/**
invocationJson.set(0,
new JSONString(invocation.getPaintableId()));
invocationJson.set(1,
+ new JSONString(invocation.getInterfaceName()));
+ invocationJson.set(2,
new JSONString(invocation.getMethodName()));
JSONArray paramJson = new JSONArray();
for (int i = 0; i < invocation.getParameters().length; ++i) {
paramJson.set(i, JsonEncoder.encode(
invocation.getParameters()[i], getPaintableMap()));
}
- invocationJson.set(2, paramJson);
+ invocationJson.set(3, paramJson);
reqJson.set(reqJson.size(), invocationJson);
}
formattedParams = (null != parameters) ? parameters
.toString() : null;
}
- VConsole.log("\t\t" + invocation.getMethodName() + "("
- + formattedParams + ")");
+ VConsole.log("\t\t" + invocation.getInterfaceName() + "."
+ + invocation.getMethodName() + "(" + formattedParams
+ + ")");
}
}
}
public class MethodInvocation {
private final String paintableId;
+ private final String interfaceName;
private final String methodName;
private final Object[] parameters;
- public MethodInvocation(String paintableId, String methodName,
- Object[] parameters) {
+ public MethodInvocation(String paintableId, String interfaceName,
+ String methodName, Object[] parameters) {
this.paintableId = paintableId;
+ this.interfaceName = interfaceName;
this.methodName = methodName;
this.parameters = parameters;
}
return paintableId;
}
+ public String getInterfaceName() {
+ return interfaceName;
+ }
+
public String getMethodName() {
return methodName;
}
nextInvocation = invocations.get(i + 1);
}
- final String methodName = invocation.getMethodName();
+ final String interfaceName = invocation.getInterfaceName();
- if (!ApplicationConnection.UPDATE_VARIABLE_METHOD
- .equals(methodName)) {
+ if (!ApplicationConnection.UPDATE_VARIABLE_INTERFACE
+ .equals(interfaceName)) {
// handle other RPC calls than variable changes
applyInvocation(invocation);
continue;
for (int i = 0; i < invocationsJson.length(); ++i) {
JSONArray invocationJson = invocationsJson.getJSONArray(i);
String paintableId = invocationJson.getString(0);
- String methodName = invocationJson.getString(1);
- JSONArray parametersJson = invocationJson.getJSONArray(2);
+ String interfaceName = invocationJson.getString(1);
+ String methodName = invocationJson.getString(2);
+ JSONArray parametersJson = invocationJson.getJSONArray(3);
Object[] parameters = new Object[parametersJson.length()];
for (int j = 0; j < parametersJson.length(); ++j) {
parameters[j] = JsonDecoder.convertVariableValue(
parametersJson.getJSONArray(j), this);
}
MethodInvocation invocation = new MethodInvocation(paintableId,
- methodName, parameters);
+ interfaceName, methodName, parameters);
invocations.add(invocation);
}
return invocations;
* @since 7.0
*/
public interface RpcTarget {
- public RpcManager getRpcManager();
+ /**
+ * Returns the RPC manager instance to use when receiving calls for an RPC
+ * interface.
+ *
+ * @param rpcInterface
+ * interface for which the call was made
+ * @return RpcManager or null if none found for the interface
+ */
+ public RpcManager getRpcManager(Class<?> rpcInterface);
}
*
* @since 7.0
*/
-public class ServerRpcManager implements RpcManager {
+public class ServerRpcManager<T> implements RpcManager {
private final RpcTarget target;
- private final Object implementation;
+ private final T implementation;
+ private final Class<T> rpcInterface;
private static final Map<String, Method> invocationMethodCache = new ConcurrentHashMap<String, Method>(
128, 0.75f, 1);
* RPC call target (normally a {@link Paintable})
* @param implementation
* RPC interface implementation for the target
+ * @param rpcInterface
+ * RPC interface type
*/
- public ServerRpcManager(RpcTarget target, Object implementation) {
+ public ServerRpcManager(RpcTarget target, T implementation,
+ Class<T> rpcInterface) {
this.target = target;
this.implementation = implementation;
+ this.rpcInterface = rpcInterface;
}
/**
*/
public static void applyInvocation(RpcTarget target,
MethodInvocation invocation) {
- RpcManager manager = target.getRpcManager();
- if (manager != null) {
- manager.applyInvocation(invocation);
- } else {
+ try {
+ Class<?> rpcInterfaceClass = Class.forName(invocation
+ .getInterfaceName());
+ RpcManager manager = target.getRpcManager(rpcInterfaceClass);
+ if (manager != null) {
+ manager.applyInvocation(invocation);
+ } else {
+ throw new RuntimeException(
+ "RPC call to a target without an RPC manager.");
+ }
+ } catch (ClassNotFoundException e) {
throw new RuntimeException(
- "RPC call to a target without an RPC manager.");
+ "No RPC manager registered for RPC interface "
+ + invocation.getInterfaceName() + " of the target "
+ + target + ".");
}
}
*
* @return RPC interface implementation
*/
- protected Object getImplementation() {
+ protected T getImplementation() {
return implementation;
}
+ /**
+ * Returns the RPC interface type managed by this RPC manager instance.
+ *
+ * @return RPC interface type
+ */
+ protected Class<T> getRpcInterface() {
+ return rpcInterface;
+ }
+
/**
* Invoke a method in a server side RPC target class. This method is to be
* used by the RPC framework and unit testing tools only.
*/
public void applyInvocation(MethodInvocation invocation) {
String methodName = invocation.getMethodName();
+ // here, we already know that the interface is an rpcInterface
Object[] arguments = invocation.getParameters();
- Method method = findInvocationMethod(implementation.getClass(),
- methodName, arguments.length);
+ Method method = findInvocationMethod(rpcInterface, methodName,
+ arguments.length);
if (method == null) {
throw new RuntimeException(implementation + " does not contain "
- + methodName + " with " + arguments.length + " parameters");
+ + rpcInterface.getName() + "." + methodName + " with "
+ + arguments.length + " parameters");
}
Class<?>[] parameterTypes = method.getParameterTypes();
}
}
- private static Method findInvocationMethod(Class<?> targetType,
- String methodName, int parameterCount) {
+ private Method findInvocationMethod(Class<?> targetType, String methodName,
+ int parameterCount) {
// TODO currently only using method name and number of parameters as the
// signature
String signature = targetType.getName() + "." + methodName + "("
return invocationMethod;
}
- private static Method doFindInvocationMethod(Class<?> targetType,
+ private Method doFindInvocationMethod(Class<?> targetType,
String methodName, int parameterCount) {
- Class<?>[] interfaces = targetType.getInterfaces();
- for (Class<?> iface : interfaces) {
- Method[] methods = iface.getMethods();
- for (Method method : methods) {
- Class<?>[] parameterTypes = method.getParameterTypes();
- if (method.getName().equals(methodName)
- && parameterTypes.length == parameterCount) {
- return method;
- }
+ Method[] methods = targetType.getMethods();
+ for (Method method : methods) {
+ Class<?>[] parameterTypes = method.getParameterTypes();
+ if (method.getName().equals(methodName)
+ && parameterTypes.length == parameterCount) {
+ return method;
}
}
return null;
ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(
requestedType.getPackage().getName(), generatedClassName);
- // composer.setSuperclass(requestedType.getQualifiedSourceName());
composer.addImplementedInterface(requestedType.getQualifiedSourceName());
composer.addImplementedInterface(initializableInterface
.getQualifiedSourceName());
writeCommonFieldsAndMethods(logger, writer, typeOracle);
// actual proxy methods forwarding calls to the server
- writeRemoteProxyMethods(logger, writer, typeOracle, requestedType
- .isClassOrInterface().getMethods());
+ writeRemoteProxyMethods(logger, writer, typeOracle, requestedType,
+ requestedType.isClassOrInterface().getMethods());
// End of class
writer.outdent();
}
private static void writeRemoteProxyMethods(TreeLogger logger,
- SourceWriter writer, TypeOracle typeOracle, JMethod[] methods) {
+ SourceWriter writer, TypeOracle typeOracle,
+ JClassType requestedType, JMethod[] methods) {
for (JMethod m : methods) {
writer.print(m.getReadableDeclaration(false, false, false, false,
true));
writer.println(" {");
writer.indent();
- writer.print("client.addMethodInvocationToQueue(new MethodInvocation(paintableId, \"");
+ writer.print("client.addMethodInvocationToQueue(new MethodInvocation(paintableId, \""
+ + requestedType.getQualifiedBinaryName() + "\", \"");
writer.print(m.getName());
writer.print("\", new Object[] {");
// new Object[] { ... } for parameters - autoboxing etc. by the
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
private ActionManager actionManager;
/**
- * RPC call manager that handles incoming RPC calls.
+ * A map from RPC interface class to the RPC call manager that handles
+ * incoming RPC calls for that interface.
*/
- private RpcManager rpcManager = null;
+ private Map<Class<?>, RpcManager> rpcManagerMap = new HashMap<Class<?>, RpcManager>();
/* Constructor */
}
/**
- * Sets the RPC interface implementation for this component.
+ * Registers an RPC interface implementation for this component.
*
- * A component should have exactly one RPC interface and its implementation
- * to be able to receive RPC calls.
+ * A component can listen to multiple RPC interfaces, and subclasses can
+ * register additional implementations.
*
* @since 7.0
*
* @param implementation
* RPC interface implementation
+ * @param rpcInterfaceType
+ * RPC interface class for which the implementation should be
+ * registered
*/
- protected void setRpcImplementation(Object implementation) {
+ protected <T> void registerRpcImplementation(T implementation,
+ Class<T> rpcInterfaceType) {
if (this instanceof RpcTarget) {
- rpcManager = new ServerRpcManager((RpcTarget) this, implementation);
+ rpcManagerMap.put(rpcInterfaceType, new ServerRpcManager<T>(
+ (RpcTarget) this, implementation, rpcInterfaceType));
} else {
throw new RuntimeException(
"Cannot register an RPC implementation for a component that is not an RpcTarget");
}
}
- public RpcManager getRpcManager() {
- return rpcManager;
+ public RpcManager getRpcManager(Class<?> rpcInterface) {
+ return rpcManagerMap.get(rpcInterface);
}
}