nextInvocation = invocations.get(i + 1);
}
+ final String methodName = invocation.getMethodName();
+
+ if (!ApplicationConnection.UPDATE_VARIABLE_METHOD
+ .equals(methodName)) {
+ // handle other RPC calls than variable changes
+ applyInvocation(invocation);
+ continue;
+ }
+
final VariableOwner owner = getVariableOwner(invocation
.getPaintableId());
- final String methodName = invocation.getMethodName();
if (owner != null && owner.isEnabled()) {
- if (!ApplicationConnection.UPDATE_VARIABLE_METHOD
- .equals(methodName)) {
- // TODO handle other RPC calls
- continue;
- }
-
VariableChange change = new VariableChange(invocation);
// TODO could optimize with a single value map if only one
}
}
} else {
- // TODO convert window close to a separate RPC call, not a
- // variable change
+ // TODO convert window close to a separate RPC call and
+ // handle above - not a variable change
VariableChange change = new VariableChange(invocation);
return success;
}
+ /**
+ * Execute an RPC call from the client by finding its target and letting the
+ * RPC mechanism call the correct method for it.
+ *
+ * @param invocation
+ */
+ protected void applyInvocation(MethodInvocation invocation) {
+ final RpcTarget target = getRpcTarget(invocation.getPaintableId());
+ if (null != target) {
+ ServerRpcManager.applyInvocation(target, invocation);
+ } else {
+ // TODO better exception?
+ throw new RuntimeException("No RPC target for paintable "
+ + invocation.getPaintableId());
+ }
+ }
+
/**
* Parse a message burst from the client into a list of MethodInvocation
* instances.
return owner;
}
+ /**
+ * Returns the RPC call target for a paintable ID.
+ *
+ * @since 7.0
+ *
+ * @param string
+ * paintable ID
+ * @return RPC call target or null if none found
+ */
+ protected RpcTarget getRpcTarget(String string) {
+ // TODO improve this - VariableOwner and RpcManager separate?
+ VariableOwner owner = getVariableOwner(string);
+ if (owner instanceof RpcTarget) {
+ return (RpcTarget) owner;
+ } else {
+ return null;
+ }
+ }
+
private VariableOwner getDragAndDropService() {
if (dragAndDropService == null) {
dragAndDropService = new DragAndDropService(this);
--- /dev/null
+package com.vaadin.terminal.gwt.server;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.vaadin.terminal.Paintable;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+
+/**
+ * Server side RPC manager that handles RPC calls coming from the client.
+ *
+ * Each {@link RpcTarget} (typically a {@link Paintable}) should have its own
+ * instance of {@link ServerRpcManager} if it wants to receive RPC calls from
+ * the client.
+ *
+ * @since 7.0
+ */
+public class ServerRpcManager implements RpcManager {
+
+ private final RpcTarget target;
+ private final Object implementation;
+
+ private static final Map<String, Method> invocationMethodCache = new ConcurrentHashMap<String, Method>(
+ 128, 0.75f, 1);
+
+ private static final Map<Class<?>, Class<?>> boxedTypes = new HashMap<Class<?>, Class<?>>();
+ static {
+ try {
+ Class<?>[] boxClasses = new Class<?>[] { Boolean.class, Byte.class,
+ Short.class, Character.class, Integer.class, Long.class,
+ Float.class, Double.class };
+ for (Class<?> boxClass : boxClasses) {
+ Field typeField = boxClass.getField("TYPE");
+ Class<?> primitiveType = (Class<?>) typeField.get(boxClass);
+ boxedTypes.put(primitiveType, boxClass);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Create a RPC manager for an RPC target.
+ *
+ * @param target
+ * RPC call target (normally a {@link Paintable})
+ * @param implementation
+ * RPC interface implementation for the target
+ */
+ public ServerRpcManager(RpcTarget target, Object implementation) {
+ this.target = target;
+ this.implementation = implementation;
+ }
+
+ /**
+ * 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.
+ *
+ * @param target
+ * non-null target of the RPC call
+ * @param invocation
+ * method invocation to perform
+ */
+ public static void applyInvocation(RpcTarget target,
+ MethodInvocation invocation) {
+ RpcManager manager = target.getRpcManager();
+ if (manager != null) {
+ manager.applyInvocation(invocation);
+ } else {
+ throw new RuntimeException(
+ "RPC call to a target without an RPC manager.");
+ }
+ }
+
+ /**
+ * Returns the RPC target of this RPC manager instance.
+ *
+ * @return RpcTarget, typically a {@link Paintable}
+ */
+ public RpcTarget getTarget() {
+ return target;
+ }
+
+ /**
+ * Returns the RPC interface implementation for the RPC target.
+ *
+ * @return RPC interface implementation
+ */
+ protected Object getImplementation() {
+ return implementation;
+ }
+
+ /**
+ * 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.
+ *
+ * @param invocation
+ * method invocation to perform
+ */
+ public void applyInvocation(MethodInvocation invocation) {
+ String methodName = invocation.getMethodName();
+ Object[] arguments = invocation.getParameters();
+
+ Method method = findInvocationMethod(implementation.getClass(),
+ methodName, arguments.length);
+ if (method == null) {
+ throw new RuntimeException(implementation + " does not contain "
+ + methodName + " with " + arguments.length + " parameters");
+ }
+
+ Class<?>[] parameterTypes = method.getParameterTypes();
+ Object[] args = new Object[parameterTypes.length];
+ for (int i = 0; i < args.length; i++) {
+ // no conversion needed for basic cases
+ // Class<?> type = parameterTypes[i];
+ // if (type.isPrimitive()) {
+ // type = boxedTypes.get(type);
+ // }
+ args[i] = arguments[i];
+ }
+ try {
+ method.invoke(implementation, args);
+ } catch (Exception e) {
+ throw new RuntimeException(methodName, e);
+ }
+ }
+
+ private static 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 + "("
+ + parameterCount;
+ Method invocationMethod = invocationMethodCache.get(signature);
+
+ if (invocationMethod == null) {
+ invocationMethod = doFindInvocationMethod(targetType, methodName,
+ parameterCount);
+
+ if (invocationMethod != null) {
+ invocationMethodCache.put(signature, invocationMethod);
+ }
+ }
+
+ return invocationMethod;
+ }
+
+ private static 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;
+ }
+ }
+ }
+ return null;
+ }
+
+}
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.Terminal;
import com.vaadin.terminal.gwt.server.ComponentSizeValidator;
+import com.vaadin.terminal.gwt.server.RpcManager;
+import com.vaadin.terminal.gwt.server.RpcTarget;
+import com.vaadin.terminal.gwt.server.ServerRpcManager;
import com.vaadin.tools.ReflectTools;
/**
*/
private ActionManager actionManager;
+ /**
+ * RPC call manager that handles incoming RPC calls.
+ */
+ private RpcManager rpcManager = null;
+
/* Constructor */
/**
}
}
+ /**
+ * Sets the RPC interface implementation for this component.
+ *
+ * A component should have exactly one RPC interface and its implementation
+ * to be able to receive RPC calls.
+ *
+ * @since 7.0
+ *
+ * @param implementation
+ * RPC interface implementation
+ */
+ protected void setRpcImplementation(Object implementation) {
+ if (this instanceof RpcTarget) {
+ rpcManager = new ServerRpcManager((RpcTarget) this, implementation);
+ } else {
+ throw new RuntimeException(
+ "Cannot register an RPC implementation for a component that is not an RpcTarget");
+ }
+ }
+
+ public RpcManager getRpcManager() {
+ return rpcManager;
+ }
+
}