From edda8dbe9f477610fac41ab2ff85646cc5c40207 Mon Sep 17 00:00:00 2001 From: Artur Date: Wed, 18 Apr 2012 10:09:53 +0300 Subject: [PATCH] Made it possible to find out RPC parameter types (#8667) Refactored legacy change variables handling to LegacyChangeVariablesInvocation --- .../communication/MethodInvocation.java | 16 +- .../server/AbstractCommunicationManager.java | 223 +++++++++--------- .../vaadin/terminal/gwt/server/JsonCodec.java | 163 +++++++------ .../LegacyChangeVariablesInvocation.java | 38 +++ .../terminal/gwt/server/RpcManager.java | 4 +- .../gwt/server/ServerRPCMethodInvocation.java | 107 +++++++++ .../terminal/gwt/server/ServerRpcManager.java | 91 ++----- 7 files changed, 378 insertions(+), 264 deletions(-) create mode 100644 src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java create mode 100644 src/com/vaadin/terminal/gwt/server/ServerRPCMethodInvocation.java diff --git a/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java b/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java index f4305ffcaa..b77477a76a 100644 --- a/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java +++ b/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java @@ -17,14 +17,19 @@ public class MethodInvocation { private final String connectorId; private final String interfaceName; private final String methodName; - private final Object[] parameters; + private Object[] parameters; public MethodInvocation(String connectorId, String interfaceName, - String methodName, Object[] parameters) { + String methodName) { this.connectorId = connectorId; this.interfaceName = interfaceName; this.methodName = methodName; - this.parameters = parameters; + } + + public MethodInvocation(String connectorId, String interfaceName, + String methodName, Object[] parameters) { + this(connectorId, interfaceName, methodName); + setParameters(parameters); } public String getConnectorId() { @@ -43,9 +48,14 @@ public class MethodInvocation { return parameters; } + public void setParameters(Object[] parameters) { + this.parameters = parameters; + } + @Override public String toString() { return connectorId + ":" + interfaceName + "." + methodName + "(" + Arrays.toString(parameters) + ")"; } + } \ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 31297c0c6c..6e2602ff9c 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -17,6 +17,7 @@ import java.io.Serializable; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.security.GeneralSecurityException; import java.text.CharacterIterator; import java.text.DateFormat; @@ -807,8 +808,9 @@ public abstract class AbstractCommunicationManager implements Serializable { if (null != state) { // encode and send shared state try { + // FIXME Use declared type JSONArray stateJsonArray = JsonCodec.encode(state, - application); + state.getClass(), application); sharedStates .put(connector.getConnectorId(), stateJsonArray); } catch (JSONException e) { @@ -1368,43 +1370,6 @@ public abstract class AbstractCommunicationManager implements Serializable { return success; } - /** - * Helper class for parsing variable change RPC calls. - * - * Note that variable changes still only support the old data types and - * partly use Vaadin 6 way of encoding of values. Other RPC method calls - * support more data types. - * - * @since 7.0 - */ - private class VariableChange { - private final String name; - private final Object value; - - public VariableChange(MethodInvocation invocation) throws JSONException { - name = (String) invocation.getParameters()[0]; - value = invocation.getParameters()[1]; - } - - /** - * Returns the variable name for the modification. - * - * @return variable name - */ - public String getName() { - return name; - } - - /** - * Returns the (parsed and converted) value of the updated variable. - * - * @return variable value - */ - public Object getValue() { - return value; - } - } - /** * Processes a message burst received from the client. * @@ -1430,24 +1395,9 @@ public abstract class AbstractCommunicationManager implements Serializable { try { List invocations = parseInvocations(burst); - // Perform the method invocations, grouping consecutive variable - // changes for the same Paintable. - - // Combining of variable changes is currently needed to preserve the - // old semantics for any component that relies on them. If the - // support for legacy variable change events is removed, each call - // can be performed separately and thelogic here simplified. - for (int i = 0; i < invocations.size(); i++) { MethodInvocation invocation = invocations.get(i); - MethodInvocation nextInvocation = null; - if (i + 1 < invocations.size()) { - nextInvocation = invocations.get(i + 1); - } - - final String interfaceName = invocation.getInterfaceName(); - final ClientConnector connector = getConnector(app, invocation.getConnectorId()); @@ -1464,23 +1414,24 @@ public abstract class AbstractCommunicationManager implements Serializable { if (!connector.isConnectorEnabled()) { - if (ApplicationConnection.UPDATE_VARIABLE_INTERFACE - .equals(interfaceName)) { + if (invocation instanceof LegacyChangeVariablesInvocation) { + LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation; // TODO convert window close to a separate RPC call and // handle above - not a variable change - VariableChange change = new VariableChange(invocation); // Handle special case where window-close is called // after the window has been removed from the // application or the application has closed - if ("close".equals(change.getName()) - && Boolean.TRUE.equals(change.getValue())) { + Map changes = legacyInvocation + .getVariableChanges(); + if (changes.size() == 1 && changes.containsKey("close") + && Boolean.TRUE.equals(changes.get("close"))) { // Silently ignore this continue; } } - // Connector is disabled, log a warning and move the next + // Connector is disabled, log a warning and move to the next String msg = "Ignoring RPC call for disabled connector " + connector.getClass().getName(); if (connector instanceof Component) { @@ -1493,49 +1444,32 @@ public abstract class AbstractCommunicationManager implements Serializable { continue; } - if (!ApplicationConnection.UPDATE_VARIABLE_INTERFACE - .equals(interfaceName)) { - // handle other RPC calls than variable changes - ServerRpcManager.applyInvocation(connector, invocation); - continue; - } - - // All code below is for legacy variable changes - final VariableOwner owner = (VariableOwner) connector; - - VariableChange change = new VariableChange(invocation); - - Map m = new HashMap(); - m.put(change.getName(), change.getValue()); - while (nextInvocation != null - && invocation.getConnectorId().equals( - nextInvocation.getConnectorId()) - && ApplicationConnection.UPDATE_VARIABLE_METHOD - .equals(nextInvocation.getMethodName())) { - i++; - invocation = nextInvocation; - change = new VariableChange(invocation); - m.put(change.getName(), change.getValue()); - if (i + 1 < invocations.size()) { - nextInvocation = invocations.get(i + 1); - } else { - nextInvocation = null; - } - } + if (invocation instanceof ServerRPCMethodInvocation) { + ServerRpcManager.applyInvocation(connector, + (ServerRPCMethodInvocation) invocation); + } else { - try { - changeVariables(source, owner, m); - } catch (Exception e) { - Component errorComponent = null; - if (owner instanceof Component) { - errorComponent = (Component) owner; - } else if (owner instanceof DragAndDropService) { - if (m.get("dhowner") instanceof Component) { - errorComponent = (Component) m.get("dhowner"); + // All code below is for legacy variable changes + LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation; + Map changes = legacyInvocation + .getVariableChanges(); + try { + changeVariables(source, (VariableOwner) connector, + changes); + } catch (Exception e) { + Component errorComponent = null; + if (connector instanceof Component) { + errorComponent = (Component) connector; + } else if (connector instanceof DragAndDropService) { + Object dropHandlerOwner = changes.get("dhowner"); + if (dropHandlerOwner instanceof Component) { + errorComponent = (Component) dropHandlerOwner; + } } - } - handleChangeVariablesError(app, errorComponent, e, m); + handleChangeVariablesError(app, errorComponent, e, + changes); + } } } @@ -1564,25 +1498,94 @@ public abstract class AbstractCommunicationManager implements Serializable { ArrayList invocations = new ArrayList(); + MethodInvocation previousInvocation = null; // parse JSON to MethodInvocations for (int i = 0; i < invocationsJson.length(); ++i) { + JSONArray invocationJson = invocationsJson.getJSONArray(i); - String connectorId = invocationJson.getString(0); - 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] = JsonCodec.decode( - parametersJson.getJSONArray(j), application); + + MethodInvocation invocation = parseInvocation(invocationJson, + previousInvocation); + if (invocation != null) { + // Can be null iff the invocation was a legacy invocation and it + // was merged with the previous one + invocations.add(invocation); + previousInvocation = invocation; } - MethodInvocation invocation = new MethodInvocation(connectorId, - interfaceName, methodName, parameters); - invocations.add(invocation); } return invocations; } + private MethodInvocation parseInvocation(JSONArray invocationJson, + MethodInvocation previousInvocation) throws JSONException { + String connectorId = invocationJson.getString(0); + String interfaceName = invocationJson.getString(1); + String methodName = invocationJson.getString(2); + + JSONArray parametersJson = invocationJson.getJSONArray(3); + + if (LegacyChangeVariablesInvocation.isLegacyVariableChange( + interfaceName, methodName)) { + if (!(previousInvocation instanceof LegacyChangeVariablesInvocation)) { + previousInvocation = null; + } + + return parseLegacyChangeVariablesInvocation(connectorId, + interfaceName, methodName, + (LegacyChangeVariablesInvocation) previousInvocation, + parametersJson); + } else { + return parseServerRpcInvocation(connectorId, interfaceName, + methodName, parametersJson); + } + + } + + private LegacyChangeVariablesInvocation parseLegacyChangeVariablesInvocation( + String connectorId, String interfaceName, String methodName, + LegacyChangeVariablesInvocation previousInvocation, + JSONArray parametersJson) throws JSONException { + if (parametersJson.length() != 2) { + throw new JSONException( + "Invalid parameters in legacy change variables call. Expected 2, was " + + parametersJson.length()); + } + String variableName = (String) JsonCodec + .decodeInternalType(String.class, true, + parametersJson.getJSONArray(0), application); + Object value = JsonCodec.decodeInternalType( + parametersJson.getJSONArray(1), application); + + if (previousInvocation != null + && previousInvocation.getConnectorId().equals(connectorId)) { + previousInvocation.setVariableChange(variableName, value); + return null; + } else { + return new LegacyChangeVariablesInvocation(connectorId, + variableName, value); + } + } + + private ServerRPCMethodInvocation parseServerRpcInvocation( + String connectorId, String interfaceName, String methodName, + JSONArray parametersJson) throws JSONException { + ServerRPCMethodInvocation invocation = new ServerRPCMethodInvocation( + connectorId, interfaceName, methodName, parametersJson.length()); + + Object[] parameters = new Object[parametersJson.length()]; + Type[] declaredRpcMethodParameterTypes = invocation.getMethod() + .getGenericParameterTypes(); + + for (int j = 0; j < parametersJson.length(); ++j) { + JSONArray parameterJson = parametersJson.getJSONArray(j); + Type parameterType = declaredRpcMethodParameterTypes[j]; + parameters[j] = JsonCodec.decodeInternalOrCustomType(parameterType, + parameterJson, application); + } + invocation.setParameters(parameters); + return invocation; + } + protected void changeVariables(Object source, final VariableOwner owner, Map m) { owner.changeVariables(source, m); diff --git a/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/src/com/vaadin/terminal/gwt/server/JsonCodec.java index 0d2b0c2082..375cce4161 100644 --- a/src/com/vaadin/terminal/gwt/server/JsonCodec.java +++ b/src/com/vaadin/terminal/gwt/server/JsonCodec.java @@ -117,7 +117,7 @@ public class JsonCodec implements Serializable { application); } - public static T decodeInternalOrCustomType(Type targetType, + public static Object decodeInternalOrCustomType(Type targetType, JSONArray valueAndType, Application application) throws JSONException { if (isInternalType(targetType)) { @@ -128,7 +128,7 @@ public class JsonCodec implements Serializable { } } - public static T decodeCustomType(Type targetType, + public static Object decodeCustomType(Type targetType, JSONArray valueAndType, Application application) throws JSONException { if (isInternalType(targetType)) { @@ -137,7 +137,7 @@ public class JsonCodec implements Serializable { } String transportType = getCustomTransportType(getClassForType(targetType)); String encodedTransportType = valueAndType.getString(0); - if (!encodedTransportType.equals(transportType)) { + if (!transportTypesCompatible(encodedTransportType, transportType)) { throw new JSONException("Expected a value of type " + transportType + ", received " + encodedTransportType); } @@ -171,47 +171,49 @@ public class JsonCodec implements Serializable { * @return * @throws JSONException */ - public static T decodeInternalType(Type targetType, + public static Object decodeInternalType(Type targetType, boolean restrictToInternalTypes, JSONArray valueAndType, Application application) throws JSONException { + String encodedTransportType = valueAndType.getString(0); if (!isInternalType(targetType)) { - throw new JSONException("Cannot decode internal type. Type " - + targetType + " is not supported."); + throw new JSONException("Type " + targetType + + " is not a supported internal type."); } String transportType = getInternalTransportType(targetType); - String encodedTransportType = valueAndType.getString(0); - if (!encodedTransportType.equals(transportType)) { - throw new JSONException("Expected a value of type " + transportType - + ", received " + encodedTransportType); + if (!transportTypesCompatible(encodedTransportType, transportType)) { + throw new JSONException("Expected a value of type " + targetType + + ", received " + getType(encodedTransportType)); } Object encodedJsonValue = valueAndType.get(1); + if (JsonEncoder.VTYPE_NULL.equals(encodedTransportType)) { + return null; + } // Collections if (JsonEncoder.VTYPE_LIST.equals(transportType)) { - return (T) decodeList(targetType, restrictToInternalTypes, + return decodeList(targetType, restrictToInternalTypes, (JSONArray) encodedJsonValue, application); } else if (JsonEncoder.VTYPE_SET.equals(transportType)) { - return (T) decodeSet(targetType, restrictToInternalTypes, + return decodeSet(targetType, restrictToInternalTypes, (JSONArray) encodedJsonValue, application); } else if (JsonEncoder.VTYPE_MAP_CONNECTOR.equals(transportType)) { - return (T) decodeConnectorToObjectMap(targetType, + return decodeConnectorToObjectMap(targetType, restrictToInternalTypes, (JSONObject) encodedJsonValue, application); } else if (JsonEncoder.VTYPE_MAP.equals(transportType)) { - return (T) decodeStringToObjectMap(targetType, - restrictToInternalTypes, (JSONObject) encodedJsonValue, - application); + return decodeStringToObjectMap(targetType, restrictToInternalTypes, + (JSONObject) encodedJsonValue, application); } // Arrays if (JsonEncoder.VTYPE_ARRAY.equals(transportType)) { - return (T) decodeObjectArray(targetType, - (JSONArray) encodedJsonValue, application); + return decodeObjectArray(targetType, (JSONArray) encodedJsonValue, + application); } else if (JsonEncoder.VTYPE_STRINGARRAY.equals(transportType)) { - return (T) decodeStringArray((JSONArray) encodedJsonValue); + return decodeStringArray((JSONArray) encodedJsonValue); } // Special Vaadin types @@ -219,30 +221,43 @@ public class JsonCodec implements Serializable { String stringValue = String.valueOf(encodedJsonValue); if (JsonEncoder.VTYPE_CONNECTOR.equals(transportType)) { - return (T) application.getConnector(stringValue); + return application.getConnector(stringValue); } // Standard Java types if (JsonEncoder.VTYPE_STRING.equals(transportType)) { - return (T) stringValue; + return stringValue; } else if (JsonEncoder.VTYPE_INTEGER.equals(transportType)) { - return (T) Integer.valueOf(stringValue); + return Integer.valueOf(stringValue); } else if (JsonEncoder.VTYPE_LONG.equals(transportType)) { - return (T) Long.valueOf(stringValue); + return Long.valueOf(stringValue); } else if (JsonEncoder.VTYPE_FLOAT.equals(transportType)) { - return (T) Float.valueOf(stringValue); + return Float.valueOf(stringValue); } else if (JsonEncoder.VTYPE_DOUBLE.equals(transportType)) { - return (T) Double.valueOf(stringValue); + return Double.valueOf(stringValue); } else if (JsonEncoder.VTYPE_BOOLEAN.equals(transportType)) { - return (T) Boolean.valueOf(stringValue); - } else if (JsonEncoder.VTYPE_NULL.equals(transportType)) { - return null; + return Boolean.valueOf(stringValue); } throw new JSONException("Unknown type " + transportType); } + private static boolean transportTypesCompatible( + String encodedTransportType, String transportType) { + if (encodedTransportType == null) { + return false; + } + if (encodedTransportType.equals(transportType)) { + return true; + } + if (encodedTransportType.equals(JsonEncoder.VTYPE_NULL)) { + return true; + } + + return false; + } + @Deprecated private static Map decodeStringToObjectMap(Type targetType, boolean restrictToInternalTypes, JSONObject jsonMap, @@ -252,54 +267,60 @@ public class JsonCodec implements Serializable { while (it.hasNext()) { String key = it.next(); JSONArray encodedValueAndType = jsonMap.getJSONArray(key); - Object decodedChild; - if (!restrictToInternalTypes - && targetType instanceof ParameterizedType) { - Type mapValueType = ((ParameterizedType) targetType) - .getActualTypeArguments()[1]; - // Only decode the given type - decodedChild = decodeInternalOrCustomType(mapValueType, - encodedValueAndType, application); - } else { - // Only internal types when not enforcing a given type to avoid - // security issues - decodedChild = decodeInternalType(encodedValueAndType, - application); - } + Object decodedChild = decodeChild(targetType, + restrictToInternalTypes, 1, encodedValueAndType, + application); map.put(key, decodedChild); } return map; } @Deprecated - private static Object decodeConnectorToObjectMap(Type targetType, - boolean restrictToInternalTypes, JSONObject jsonMap, - Application application) throws JSONException { + private static Map decodeConnectorToObjectMap( + Type targetType, boolean restrictToInternalTypes, + JSONObject jsonMap, Application application) throws JSONException { HashMap map = new HashMap(); Iterator it = jsonMap.keys(); while (it.hasNext()) { String connectorId = it.next(); Connector connector = application.getConnector(connectorId); JSONArray encodedValueAndType = jsonMap.getJSONArray(connectorId); - Object decodedChild; - if (!restrictToInternalTypes - && targetType instanceof ParameterizedType) { - Type mapValueType = ((ParameterizedType) targetType) - .getActualTypeArguments()[1]; - // Only decode the given type - decodedChild = decodeInternalOrCustomType(mapValueType, - encodedValueAndType, application); - } else { - // Only internal types when not enforcing a given type to avoid - // security issues - decodedChild = decodeInternalType(encodedValueAndType, - application); - } + Object decodedChild = decodeChild(targetType, + restrictToInternalTypes, 1, encodedValueAndType, + application); map.put(connector, decodedChild); } return map; } + /** + * @param targetType + * @param restrictToInternalTypes + * @param typeIndex + * The index of a generic type to use to define the child type + * that should be decoded + * @param encodedValueAndType + * @param application + * @return + * @throws JSONException + */ + private static Object decodeChild(Type targetType, + boolean restrictToInternalTypes, int typeIndex, + JSONArray encodedValueAndType, Application application) + throws JSONException { + if (!restrictToInternalTypes && targetType instanceof ParameterizedType) { + Type childType = ((ParameterizedType) targetType) + .getActualTypeArguments()[typeIndex]; + // Only decode the given type + return decodeInternalOrCustomType(childType, encodedValueAndType, + application); + } else { + // Only internal types when not enforcing a given type to avoid + // security issues + return decodeInternalType(encodedValueAndType, application); + } + } + private static String[] decodeStringArray(JSONArray jsonArray) throws JSONException { int length = jsonArray.length(); @@ -322,20 +343,10 @@ public class JsonCodec implements Serializable { List list = new ArrayList(); for (int i = 0; i < jsonArray.length(); ++i) { // each entry always has two elements: type and value - JSONArray entryArray = jsonArray.getJSONArray(i); - Object decodedChild; - if (!restrictToInternalTypes - && targetType instanceof ParameterizedType) { - // Only decode the given type - Type childType = ((ParameterizedType) targetType) - .getActualTypeArguments()[0]; - decodedChild = decodeInternalOrCustomType(childType, - entryArray, application); - } else { - // Only internal types when not enforcing a given type to avoid - // security issues - decodedChild = decodeInternalType(entryArray, application); - } + JSONArray encodedValueAndType = jsonArray.getJSONArray(i); + Object decodedChild = decodeChild(targetType, + restrictToInternalTypes, 0, encodedValueAndType, + application); list.add(decodedChild); } return list; @@ -368,7 +379,7 @@ public class JsonCodec implements Serializable { return pd.getWriteMethod().getName().substring(3); } - private static T decodeObject(Type targetType, + private static Object decodeObject(Type targetType, JSONObject serializedObject, Application application) throws JSONException { @@ -391,7 +402,7 @@ public class JsonCodec implements Serializable { pd.getWriteMethod().invoke(decodedObject, decodedFieldValue); } - return (T) decodedObject; + return decodedObject; } catch (IllegalArgumentException e) { throw new JSONException(e); } catch (IllegalAccessException e) { @@ -566,7 +577,7 @@ public class JsonCodec implements Serializable { * @throws JSONException */ private static String getInternalTransportType(Type valueType) { - return typeToTransportType.get(valueType); + return typeToTransportType.get(getClassForType(valueType)); } private static String getCustomTransportType(Class targetType) { diff --git a/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java b/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java new file mode 100644 index 0000000000..42fa3ab5a5 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java @@ -0,0 +1,38 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.server; + +import java.util.HashMap; +import java.util.Map; + +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.communication.MethodInvocation; + +public class LegacyChangeVariablesInvocation extends MethodInvocation { + private Map variableChanges = new HashMap(); + + public LegacyChangeVariablesInvocation(String connectorId, + String variableName, Object value) { + super(connectorId, ApplicationConnection.UPDATE_VARIABLE_INTERFACE, + ApplicationConnection.UPDATE_VARIABLE_METHOD); + setVariableChange(variableName, value); + } + + public static boolean isLegacyVariableChange(String interfaceName, + String methodName) { + return ApplicationConnection.UPDATE_VARIABLE_METHOD + .equals(interfaceName) + && ApplicationConnection.UPDATE_VARIABLE_METHOD + .equals(methodName); + } + + public void setVariableChange(String name, Object value) { + variableChanges.put(name, value); + } + + public Map getVariableChanges() { + return variableChanges; + } + +} diff --git a/src/com/vaadin/terminal/gwt/server/RpcManager.java b/src/com/vaadin/terminal/gwt/server/RpcManager.java index 5fcfda50a5..a629447069 100644 --- a/src/com/vaadin/terminal/gwt/server/RpcManager.java +++ b/src/com/vaadin/terminal/gwt/server/RpcManager.java @@ -6,8 +6,6 @@ package com.vaadin.terminal.gwt.server; import java.io.Serializable; -import com.vaadin.terminal.gwt.client.communication.MethodInvocation; - /** * Server side RPC manager that can invoke methods based on RPC calls received * from the client. @@ -15,5 +13,5 @@ import com.vaadin.terminal.gwt.client.communication.MethodInvocation; * @since 7.0 */ public interface RpcManager extends Serializable { - public void applyInvocation(MethodInvocation invocation); + public void applyInvocation(ServerRPCMethodInvocation invocation); } diff --git a/src/com/vaadin/terminal/gwt/server/ServerRPCMethodInvocation.java b/src/com/vaadin/terminal/gwt/server/ServerRPCMethodInvocation.java new file mode 100644 index 0000000000..6afb549502 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/server/ServerRPCMethodInvocation.java @@ -0,0 +1,107 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.server; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.vaadin.terminal.gwt.client.communication.MethodInvocation; +import com.vaadin.terminal.gwt.client.communication.ServerRpc; + +public class ServerRPCMethodInvocation extends MethodInvocation { + + private static final Map invocationMethodCache = new ConcurrentHashMap( + 128, 0.75f, 1); + + private final Method method; + + private Class interfaceClass; + + public ServerRPCMethodInvocation(String connectorId, String interfaceName, + String methodName, int parameterCount) { + super(connectorId, interfaceName, methodName); + + interfaceClass = findClass(); + method = findInvocationMethod(interfaceClass, methodName, + parameterCount); + } + + private Class findClass() { + try { + Class rpcInterface = Class.forName(getInterfaceName()); + if (!ServerRpc.class.isAssignableFrom(rpcInterface)) { + throw new IllegalArgumentException("The interface " + + getInterfaceName() + "is not a server RPC interface."); + } + return (Class) rpcInterface; + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("The server RPC interface " + + getInterfaceName() + " could not be found", e); + } finally { + + } + } + + public Class getInterfaceClass() { + return interfaceClass; + } + + public Method getMethod() { + return method; + } + + /** + * Tries to find the method from the cache or alternatively by invoking + * {@link #doFindInvocationMethod(Class, String, int)} and updating the + * cache. + * + * @param targetType + * @param methodName + * @param parameterCount + * @return + */ + 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 + "(" + + parameterCount; + Method invocationMethod = invocationMethodCache.get(signature); + + if (invocationMethod == null) { + invocationMethod = doFindInvocationMethod(targetType, methodName, + parameterCount); + + if (invocationMethod != null) { + invocationMethodCache.put(signature, invocationMethod); + } + } + + return invocationMethod; + } + + /** + * Tries to find the method from the class by looping through available + * methods. + * + * @param targetType + * @param methodName + * @param parameterCount + * @return + */ + private Method doFindInvocationMethod(Class targetType, + String methodName, int parameterCount) { + Method[] methods = targetType.getMethods(); + for (Method method : methods) { + Class[] parameterTypes = method.getParameterTypes(); + if (method.getName().equals(methodName) + && parameterTypes.length == parameterCount) { + return method; + } + } + return null; + } + +} diff --git a/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java b/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java index 288e0bf933..025af7d61d 100644 --- a/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java +++ b/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java @@ -8,12 +8,10 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import com.vaadin.terminal.gwt.client.Connector; -import com.vaadin.terminal.gwt.client.communication.MethodInvocation; /** * Server side RPC manager that handles RPC calls coming from the client. @@ -29,9 +27,6 @@ public class ServerRpcManager implements RpcManager { private final T implementation; private final Class rpcInterface; - private static final Map invocationMethodCache = new ConcurrentHashMap( - 128, 0.75f, 1); - private static final Map, Class> boxedTypes = new HashMap, Class>(); static { try { @@ -73,26 +68,19 @@ public class ServerRpcManager implements RpcManager { * method invocation to perform */ public static void applyInvocation(RpcTarget target, - MethodInvocation invocation) { - try { - Class rpcInterfaceClass = Class.forName(invocation - .getInterfaceName()); - RpcManager manager = target.getRpcManager(rpcInterfaceClass); - if (manager != null) { - manager.applyInvocation(invocation); - } else { - getLogger() - .log(Level.WARNING, - "RPC call received for RpcTarget " - + target.getClass().getName() - + " (" - + invocation.getConnectorId() - + ") but the target has not registered any RPC interfaces"); - } - } catch (ClassNotFoundException e) { - throw new RuntimeException("Class for RPC interface " - + invocation.getInterfaceName() + " of the target " - + target + " could not be found."); + ServerRPCMethodInvocation invocation) { + RpcManager manager = target.getRpcManager(invocation + .getInterfaceClass()); + if (manager != null) { + manager.applyInvocation(invocation); + } else { + getLogger() + .log(Level.WARNING, + "RPC call received for RpcTarget " + + target.getClass().getName() + + " (" + + invocation.getConnectorId() + + ") but the target has not registered any RPC interfaces"); } } @@ -121,21 +109,11 @@ public class ServerRpcManager implements RpcManager { * @param invocation * method invocation to perform */ - 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(rpcInterface, methodName, - arguments.length); - if (method == null) { - throw new RuntimeException(implementation + " does not contain " - + rpcInterface.getName() + "." + methodName + " with " - + arguments.length + " parameters"); - } - + public void applyInvocation(ServerRPCMethodInvocation invocation) { + Method method = invocation.getMethod(); Class[] parameterTypes = method.getParameterTypes(); Object[] args = new Object[parameterTypes.length]; + Object[] arguments = invocation.getParameters(); for (int i = 0; i < args.length; i++) { // no conversion needed for basic cases // Class type = parameterTypes[i]; @@ -147,41 +125,10 @@ public class ServerRpcManager implements RpcManager { try { method.invoke(implementation, args); } catch (Exception e) { - throw new RuntimeException(methodName, e); - } - } - - 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 + "(" - + parameterCount; - Method invocationMethod = invocationMethodCache.get(signature); - - if (invocationMethod == null) { - invocationMethod = doFindInvocationMethod(targetType, methodName, - parameterCount); - - if (invocationMethod != null) { - invocationMethodCache.put(signature, invocationMethod); - } - } - - return invocationMethod; - } - - private Method doFindInvocationMethod(Class targetType, - String methodName, int parameterCount) { - Method[] methods = targetType.getMethods(); - for (Method method : methods) { - Class[] parameterTypes = method.getParameterTypes(); - if (method.getName().equals(methodName) - && parameterTypes.length == parameterCount) { - return method; - } + throw new RuntimeException("Unable to invoke method " + + invocation.getMethodName() + " in " + + invocation.getInterfaceName(), e); } - return null; } private static Logger getLogger() { -- 2.39.5