From: Leif Åstrand Date: Thu, 31 May 2012 13:31:26 +0000 (+0300) Subject: Refactor RpcManager to provide info about parameter types (#8879) X-Git-Tag: 7.0.0.alpha3~204 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=dc7e238f2dc9ec6c9faad5ab6dcfdab8f2eaae46;p=vaadin-framework.git Refactor RpcManager to provide info about parameter types (#8879) This is an intermediate step towards decoding everything received from the server based on its declared type. --- diff --git a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml index 74dc78d9b8..daa5e9f24d 100644 --- a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml +++ b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml @@ -63,9 +63,9 @@ + class="com.vaadin.terminal.gwt.widgetsetutils.GeneratedRpcMethodProviderGenerator"> + class="com.vaadin.terminal.gwt.client.communication.GeneratedRpcMethodProvider" /> getGeneratedRpcMethods(); +} diff --git a/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java index 302e6eaa55..1d3447687d 100644 --- a/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java +++ b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java @@ -4,9 +4,13 @@ package com.vaadin.terminal.gwt.client.communication; -import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import com.google.gwt.core.client.GWT; import com.vaadin.terminal.gwt.client.ConnectorMap; +import com.vaadin.terminal.gwt.client.ServerConnector; /** * Client side RPC manager that can invoke methods based on RPC calls received @@ -17,7 +21,21 @@ import com.vaadin.terminal.gwt.client.ConnectorMap; * * @since 7.0 */ -public interface RpcManager extends Serializable { +public class RpcManager { + + private final Map methodMap = new HashMap(); + + public RpcManager() { + GeneratedRpcMethodProvider provider = GWT + .create(GeneratedRpcMethodProvider.class); + Collection methods = provider.getGeneratedRpcMethods(); + for (RpcMethod rpcMethod : methods) { + methodMap.put( + rpcMethod.getInterfaceName() + "." + + rpcMethod.getMethodName(), rpcMethod); + } + } + /** * Perform server to client RPC invocation. * @@ -28,5 +46,40 @@ public interface RpcManager extends Serializable { * connectors referenced in parameters */ public void applyInvocation(MethodInvocation invocation, - ConnectorMap connectorMap); + ConnectorMap connectorMap) { + ServerConnector connector = connectorMap.getConnector(invocation + .getConnectorId()); + String signature = getSignature(invocation); + if (connector == null) { + throw new IllegalStateException("Target connector (" + + invocation.getConnectorId() + ") not found for RCC to " + + signature); + } + + RpcMethod rpcMethod = getRpcMethod(signature); + Collection implementations = connector + .getRpcImplementations(invocation.getInterfaceName()); + for (ClientRpc clientRpc : implementations) { + rpcMethod.applyInvocation(clientRpc, invocation.getParameters()); + } + } + + private RpcMethod getRpcMethod(String signature) { + RpcMethod rpcMethod = methodMap.get(signature); + if (rpcMethod == null) { + throw new IllegalStateException("There is no information about " + + signature + + ". Did you remember to compile the right widgetset?"); + } + return rpcMethod; + } + + private static String getSignature(MethodInvocation invocation) { + return invocation.getInterfaceName() + "." + invocation.getMethodName(); + } + + public Type[] getParameterTypes(MethodInvocation invocation) { + return getRpcMethod(getSignature(invocation)).getParameterTypes(); + } + } diff --git a/src/com/vaadin/terminal/gwt/client/communication/RpcMethod.java b/src/com/vaadin/terminal/gwt/client/communication/RpcMethod.java new file mode 100644 index 0000000000..abdcf73e2c --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/communication/RpcMethod.java @@ -0,0 +1,32 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.client.communication; + +public abstract class RpcMethod { + private String interfaceName; + private String methodName; + private Type[] parameterTypes; + + public RpcMethod(String interfaceName, String methodName, + Type... parameterTypes) { + this.interfaceName = interfaceName; + this.methodName = methodName; + this.parameterTypes = parameterTypes; + } + + public String getInterfaceName() { + return interfaceName; + } + + public String getMethodName() { + return methodName; + } + + public Type[] getParameterTypes() { + return parameterTypes; + } + + public abstract void applyInvocation(ClientRpc target, Object... parameters); + +} diff --git a/src/com/vaadin/terminal/gwt/client/communication/Type.java b/src/com/vaadin/terminal/gwt/client/communication/Type.java new file mode 100644 index 0000000000..dc33f760ff --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/communication/Type.java @@ -0,0 +1,40 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.client.communication; + +public class Type { + private final String baseTypeName; + private final Type[] parameterTypes; + + public Type(String baseTypeName, Type[] parameterTypes) { + this.baseTypeName = baseTypeName; + this.parameterTypes = parameterTypes; + } + + public String getBaseTypeName() { + return baseTypeName; + } + + public Type[] getParameterTypes() { + return parameterTypes; + } + + @Override + public String toString() { + String string = baseTypeName; + if (parameterTypes != null) { + string += '<'; + for (int i = 0; i < parameterTypes.length; i++) { + if (i != 0) { + string += ','; + } + string += parameterTypes[i].toString(); + } + string += '>'; + } + + return string; + } + +} diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java new file mode 100644 index 0000000000..f0db4886e4 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java @@ -0,0 +1,202 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.google.gwt.core.ext.Generator; +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.TreeLogger.Type; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.core.ext.typeinfo.JParameterizedType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; +import com.google.gwt.user.rebind.SourceWriter; +import com.vaadin.terminal.gwt.client.communication.ClientRpc; +import com.vaadin.terminal.gwt.client.communication.GeneratedRpcMethodProvider; +import com.vaadin.terminal.gwt.client.communication.RpcManager; +import com.vaadin.terminal.gwt.client.communication.RpcMethod; + +/** + * GWT generator that creates an implementation for {@link RpcManager} on the + * client side classes for executing RPC calls received from the the server. + * + * @since 7.0 + */ +public class GeneratedRpcMethodProviderGenerator extends Generator { + + @Override + public String generate(TreeLogger logger, GeneratorContext context, + String typeName) throws UnableToCompleteException { + + String packageName = null; + String className = null; + try { + TypeOracle typeOracle = context.getTypeOracle(); + + // get classType and save instance variables + JClassType classType = typeOracle.getType(typeName); + packageName = classType.getPackage().getName(); + className = classType.getSimpleSourceName() + "Impl"; + // Generate class source code for SerializerMapImpl + generateClass(logger, context, packageName, className); + } catch (Exception e) { + logger.log(TreeLogger.ERROR, + "SerializerMapGenerator creation failed", e); + } + // return the fully qualifed name of the class generated + return packageName + "." + className; + } + + /** + * Generate source code for RpcManagerImpl + * + * @param logger + * Logger object + * @param context + * Generator context + * @param packageName + * package name for the class to generate + * @param className + * class name for the class to generate + */ + private void generateClass(TreeLogger logger, GeneratorContext context, + String packageName, String className) { + // get print writer that receives the source code + PrintWriter printWriter = null; + printWriter = context.tryCreate(logger, packageName, className); + // print writer if null, source code has ALREADY been generated + if (printWriter == null) { + return; + } + logger.log(Type.INFO, + "Detecting server to client RPC interface types..."); + Date date = new Date(); + TypeOracle typeOracle = context.getTypeOracle(); + JClassType serverToClientRpcType = typeOracle.findType(ClientRpc.class + .getName()); + JClassType[] rpcInterfaceSubtypes = serverToClientRpcType.getSubtypes(); + + // init composer, set class properties, create source writer + ClassSourceFileComposerFactory composer = null; + composer = new ClassSourceFileComposerFactory(packageName, className); + composer.addImport("com.google.gwt.core.client.GWT"); + composer.addImport(RpcMethod.class.getName()); + composer.addImport(ClientRpc.class.getName()); + composer.addImport(com.vaadin.terminal.gwt.client.communication.Type.class + .getName()); + composer.addImplementedInterface(GeneratedRpcMethodProvider.class + .getName()); + SourceWriter sourceWriter = composer.createSourceWriter(context, + printWriter); + sourceWriter.indent(); + + List rpcMethods = new ArrayList(); + + sourceWriter + .println("public java.util.Collection getGeneratedRpcMethods() {"); + sourceWriter.indent(); + + sourceWriter + .println("java.util.ArrayList list = new java.util.ArrayList();"); + + // iterate over RPC interfaces and create helper methods for each + // interface + for (JClassType type : rpcInterfaceSubtypes) { + if (null == type.isInterface()) { + // only interested in interfaces here, not implementations + continue; + } + + // loop over the methods of the interface and its superinterfaces + // methods + for (JClassType currentType : type.getFlattenedSupertypeHierarchy()) { + for (JMethod method : currentType.getMethods()) { + + // RpcMethod(String interfaceName, String methodName, + // Type... parameterTypes) + sourceWriter.print("list.add(new RpcMethod(\"" + + type.getQualifiedSourceName() + "\", \"" + + method.getName() + "\""); + JType[] parameterTypes = method.getParameterTypes(); + for (JType parameter : parameterTypes) { + sourceWriter.print(", "); + writeTypeCreator(sourceWriter, parameter); + } + sourceWriter.println(") {"); + sourceWriter.indent(); + + sourceWriter + .println("public void applyInvocation(ClientRpc target, Object... parameters) {"); + sourceWriter.indent(); + + sourceWriter.print("((" + type.getQualifiedSourceName() + + ")target)." + method.getName() + "("); + for (int i = 0; i < parameterTypes.length; i++) { + JType parameterType = parameterTypes[i]; + if (i != 0) { + sourceWriter.print(", "); + } + sourceWriter.print("(" + + parameterType.getQualifiedSourceName() + + ") parameters[" + i + "]"); + } + sourceWriter.println(");"); + + sourceWriter.outdent(); + sourceWriter.println("}"); + + sourceWriter.outdent(); + sourceWriter.println("});"); + } + } + } + + sourceWriter.println("return list;"); + + sourceWriter.outdent(); + sourceWriter.println("}"); + sourceWriter.println(); + + // close generated class + sourceWriter.outdent(); + sourceWriter.println("}"); + // commit generated class + context.commit(logger, printWriter); + logger.log(Type.INFO, + "Done. (" + (new Date().getTime() - date.getTime()) / 1000 + + "seconds)"); + + } + + private void writeTypeCreator(SourceWriter sourceWriter, JType type) { + sourceWriter.print("new Type(\"" + + type.getErasedType().getQualifiedSourceName() + "\", "); + JParameterizedType parameterized = type.isParameterized(); + if (parameterized != null) { + sourceWriter.print("new Type[] {"); + JClassType[] typeArgs = parameterized.getTypeArgs(); + for (JClassType jClassType : typeArgs) { + writeTypeCreator(sourceWriter, jClassType); + sourceWriter.print(", "); + } + sourceWriter.print("}"); + } else { + sourceWriter.print("null"); + } + sourceWriter.print(")"); + } + + private String getInvokeMethodName(JClassType type) { + return "invoke" + type.getQualifiedSourceName().replaceAll("\\.", "_"); + } +} diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java deleted file mode 100644 index 2899061204..0000000000 --- a/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java +++ /dev/null @@ -1,197 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import com.google.gwt.core.ext.Generator; -import com.google.gwt.core.ext.GeneratorContext; -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.TreeLogger.Type; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.JMethod; -import com.google.gwt.core.ext.typeinfo.JType; -import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; -import com.google.gwt.user.rebind.SourceWriter; -import com.vaadin.terminal.gwt.client.ServerConnector; -import com.vaadin.terminal.gwt.client.ConnectorMap; -import com.vaadin.terminal.gwt.client.communication.ClientRpc; -import com.vaadin.terminal.gwt.client.communication.MethodInvocation; -import com.vaadin.terminal.gwt.client.communication.RpcManager; - -/** - * GWT generator that creates an implementation for {@link RpcManager} on the - * client side classes for executing RPC calls received from the the server. - * - * @since 7.0 - */ -public class RpcManagerGenerator extends Generator { - - @Override - public String generate(TreeLogger logger, GeneratorContext context, - String typeName) throws UnableToCompleteException { - - String packageName = null; - String className = null; - try { - TypeOracle typeOracle = context.getTypeOracle(); - - // get classType and save instance variables - JClassType classType = typeOracle.getType(typeName); - packageName = classType.getPackage().getName(); - className = classType.getSimpleSourceName() + "Impl"; - // Generate class source code for SerializerMapImpl - generateClass(logger, context, packageName, className); - } catch (Exception e) { - logger.log(TreeLogger.ERROR, - "SerializerMapGenerator creation failed", e); - } - // return the fully qualifed name of the class generated - return packageName + "." + className; - } - - /** - * Generate source code for RpcManagerImpl - * - * @param logger - * Logger object - * @param context - * Generator context - * @param packageName - * package name for the class to generate - * @param className - * class name for the class to generate - */ - private void generateClass(TreeLogger logger, GeneratorContext context, - String packageName, String className) { - // get print writer that receives the source code - PrintWriter printWriter = null; - printWriter = context.tryCreate(logger, packageName, className); - // print writer if null, source code has ALREADY been generated - if (printWriter == null) { - return; - } - logger.log(Type.INFO, - "Detecting server to client RPC interface types..."); - Date date = new Date(); - TypeOracle typeOracle = context.getTypeOracle(); - JClassType serverToClientRpcType = typeOracle.findType(ClientRpc.class - .getName()); - JClassType[] rpcInterfaceSubtypes = serverToClientRpcType.getSubtypes(); - - // init composer, set class properties, create source writer - ClassSourceFileComposerFactory composer = null; - composer = new ClassSourceFileComposerFactory(packageName, className); - composer.addImport("com.google.gwt.core.client.GWT"); - composer.addImplementedInterface(RpcManager.class.getName()); - SourceWriter sourceWriter = composer.createSourceWriter(context, - printWriter); - sourceWriter.indent(); - - List rpcInterfaces = new ArrayList(); - - // iterate over RPC interfaces and create helper methods for each - // interface - for (JClassType type : rpcInterfaceSubtypes) { - if (null == type.isInterface()) { - // only interested in interfaces here, not implementations - continue; - } - rpcInterfaces.add(type); - // generate method to call methods of an RPC interface - sourceWriter.println("private void " + getInvokeMethodName(type) - + "(" + MethodInvocation.class.getName() + " invocation, " - + ConnectorMap.class.getName() + " connectorMap) {"); - sourceWriter.indent(); - - // loop over the methods of the interface and its superinterfaces - // methods - for (JClassType currentType : type.getFlattenedSupertypeHierarchy()) { - for (JMethod method : currentType.getMethods()) { - sourceWriter.println("if (\"" + method.getName() - + "\".equals(invocation.getMethodName())) {"); - sourceWriter.indent(); - // construct parameter string with appropriate casts - String paramString = ""; - JType[] parameterTypes = method.getParameterTypes(); - for (int i = 0; i < parameterTypes.length; ++i) { - paramString = paramString + "(" - + parameterTypes[i].getQualifiedSourceName() - + ") invocation.getParameters()[" + i + "]"; - if (i < parameterTypes.length - 1) { - paramString = paramString + ", "; - } - } - sourceWriter - .println(ServerConnector.class.getName() - + " connector = connectorMap.getConnector(invocation.getConnectorId());"); - sourceWriter - .println("for (" - + ClientRpc.class.getName() - + " rpcImplementation : connector.getRpcImplementations(\"" - + type.getQualifiedSourceName() + "\")) {"); - sourceWriter.indent(); - sourceWriter.println("((" + type.getQualifiedSourceName() - + ") rpcImplementation)." + method.getName() + "(" - + paramString + ");"); - sourceWriter.outdent(); - sourceWriter.println("}"); - sourceWriter.println("return;"); - sourceWriter.outdent(); - sourceWriter.println("}"); - } - } - - sourceWriter.outdent(); - sourceWriter.println("}"); - - logger.log(Type.DEBUG, - "Constructed helper method for server to client RPC for " - + type.getName()); - } - - // generate top-level "switch-case" method to select the correct - // previously generated method based on the RPC interface - sourceWriter.println("public void applyInvocation(" - + MethodInvocation.class.getName() + " invocation, " - + ConnectorMap.class.getName() + " connectorMap) {"); - sourceWriter.indent(); - - for (JClassType type : rpcInterfaces) { - sourceWriter.println("if (\"" + type.getQualifiedSourceName() - + "\".equals(invocation.getInterfaceName())) {"); - sourceWriter.indent(); - sourceWriter.println(getInvokeMethodName(type) - + "(invocation, connectorMap);"); - sourceWriter.println("return;"); - sourceWriter.outdent(); - sourceWriter.println("}"); - - logger.log(Type.INFO, - "Configured server to client RPC for " + type.getName()); - } - sourceWriter.outdent(); - sourceWriter.println("}"); - - // close generated class - sourceWriter.outdent(); - sourceWriter.println("}"); - // commit generated class - context.commit(logger, printWriter); - logger.log(Type.INFO, - "Done. (" + (new Date().getTime() - date.getTime()) / 1000 - + "seconds)"); - - } - - private String getInvokeMethodName(JClassType type) { - return "invoke" + type.getQualifiedSourceName().replaceAll("\\.", "_"); - } -}