This patch addresses issue 535, generates smaller client side proxies, and allows mixin interfaces for RemoteServices.
Refactored the generated client side proxy classes to move the boiler plate code into the new RemoteServiceProxy and RequestCallbackAdapter classes.
The client side proxy now uses RequestBuilder. The underlying HTTP Request object will be returned by the proxy if the asynchronous remote service interface method returns a Request type.
Behavioral change: the client side proxy will use the RemoteService interface name for the type used in the GWT.create call instead of the interface on which the method is defined this was preventing mixins from working. The server-side RPC class has been updated to use Class.getMethod instead of Class.getDeclaredMethod which avoids the recursive lookup.
Patch by: mmendez
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1524 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/RemoteService.gwt.xml b/user/src/com/google/gwt/user/RemoteService.gwt.xml
index 5d93d21..45cc4f2 100644
--- a/user/src/com/google/gwt/user/RemoteService.gwt.xml
+++ b/user/src/com/google/gwt/user/RemoteService.gwt.xml
@@ -17,7 +17,7 @@
<!-- This module is typically inherited via com.google.gwt.user.User -->
<!-- -->
<module>
- <inherits name="com.google.gwt.user.HTTPRequest"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
<!--
Declare a property to determine whether or not RPC type versioning
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java b/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java
new file mode 100644
index 0000000..4289a93
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc.impl;
+
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.RequestException;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.InvocationException;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.ServiceDefTarget;
+import com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.ResponseReader;
+
+/**
+ * Superclass for client-side
+ * {@link com.google.gwt.user.client.rpc.RemoteService RemoteService} proxies.
+ *
+ * For internal use only.
+ */
+public abstract class RemoteServiceProxy implements ServiceDefTarget {
+ /**
+ * The module base URL as specified during construction.
+ */
+ private final String moduleBaseURL;
+
+ /**
+ * The name of the remote service interface that we will invoke methods on.
+ */
+ private final String remoteServiceIntfName;
+
+ /**
+ * URL of the
+ * {@link com.google.gwt.user.client.rpc.RemoteService RemoteService}.
+ */
+ private String remoteServiceURL;
+
+ /**
+ * The name of the serialization policy file specified during construction.
+ */
+ private final String serializationPolicyName;
+
+ /**
+ * The {@link Serializer} instance used to serialize and deserialize
+ * instances.
+ */
+ private final Serializer serializer;
+
+ protected RemoteServiceProxy(String moduleBaseURL, String remoteServiceURL,
+ String serializationPolicyName, String remoteServiceInfName,
+ Serializer serializer) {
+ this.moduleBaseURL = moduleBaseURL;
+ this.remoteServiceIntfName = remoteServiceInfName;
+ this.remoteServiceURL = remoteServiceURL;
+ this.serializer = serializer;
+ this.serializationPolicyName = serializationPolicyName;
+ }
+
+ /**
+ * Returns a
+ * {@link com.google.gwt.user.client.rpc.SerializationStreamReader SerializationStreamReader}
+ * that is ready for reading.
+ *
+ * @param encoded string that encodes the response of an RPC request
+ * @return {@link com.google.gwt.user.client.rpc.SerializationStreamReader SerializationStreamReader}
+ * that is ready for reading
+ * @throws SerializationException
+ */
+ public ClientSerializationStreamReader createStreamReader(String encoded)
+ throws SerializationException {
+ ClientSerializationStreamReader clientSerializationStreamReader = new ClientSerializationStreamReader(
+ getSerializer());
+ clientSerializationStreamReader.prepareToRead(RequestCallbackAdapter.getEncodedInstance(encoded));
+ return clientSerializationStreamReader;
+ }
+
+ /**
+ * Returns a
+ * {@link com.google.gwt.user.client.rpc.SerializationStreamWriter SerializationStreamWriter}
+ * that has had {@link ClientSerializationStreamWriter#prepareToWrite()}
+ * called on it and it has already had had the name of the remote service
+ * interface written as well.
+ *
+ * @return {@link com.google.gwt.user.client.rpc.SerializationStreamWriter SerializationStreamWriter}
+ * that has had
+ * {@link ClientSerializationStreamWriter#prepareToWrite()} called on
+ * it and it has already had had the name of the remote service
+ * interface written as well
+ */
+ public ClientSerializationStreamWriter createStreamWriter() {
+ ClientSerializationStreamWriter clientSerializationStreamWriter = new ClientSerializationStreamWriter(
+ getSerializer(), getModuleBaseURL(), getSerializationPolicyName());
+ clientSerializationStreamWriter.prepareToWrite();
+ clientSerializationStreamWriter.writeString(remoteServiceIntfName);
+ return clientSerializationStreamWriter;
+ }
+
+ /**
+ * @see ServiceDefTarget#getServiceEntryPoint()
+ */
+ public String getServiceEntryPoint() {
+ return remoteServiceURL;
+ }
+
+ /**
+ * @see ServiceDefTarget#setServiceEntryPoint(String)
+ */
+ public void setServiceEntryPoint(String url) {
+ this.remoteServiceURL = url;
+ }
+
+ /**
+ * Performs a remote service method invocation.
+ *
+ * @param <T> return type for the AsyncCallback
+ * @param responseReader instance used to read the return value of the
+ * invocation
+ * @param requestData payload that encodes the addressing and arguments of the
+ * RPC call
+ * @param callback callback handler
+ *
+ * @return a {@link Request} object that can be used to track the request
+ */
+ protected <T> Request doInvoke(ResponseReader responseReader,
+ String requestData, AsyncCallback<T> callback) {
+
+ if (getServiceEntryPoint() == null) {
+ throw new NoServiceEntryPointSpecifiedException();
+ }
+
+ RequestCallbackAdapter<T> responseHandler = new RequestCallbackAdapter<T>(
+ getSerializer(), callback, responseReader);
+ RequestBuilder rb = new RequestBuilder(RequestBuilder.POST,
+ getServiceEntryPoint());
+ try {
+ return rb.sendRequest(requestData, responseHandler);
+ } catch (RequestException ex) {
+ InvocationException iex = new InvocationException(
+ "Unable to initiate the asynchronous service invocation -- check the network connection",
+ ex);
+ callback.onFailure(iex);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the this proxy's module base URL.
+ *
+ * @return this proxy's module base URL
+ */
+ protected String getModuleBaseURL() {
+ return moduleBaseURL;
+ }
+
+ /**
+ * Returns the name of the serialization policy.
+ *
+ * @return name of the serialization policy
+ */
+ protected String getSerializationPolicyName() {
+ return serializationPolicyName;
+ }
+
+ /**
+ * Returns the {@link Serializer} instance used by this client proxy.
+ *
+ * @return {@link Serializer} instance used by this client proxy
+ */
+ protected Serializer getSerializer() {
+ return serializer;
+ }
+}
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/RequestCallbackAdapter.java b/user/src/com/google/gwt/user/client/rpc/impl/RequestCallbackAdapter.java
new file mode 100644
index 0000000..9b8f2c2
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/impl/RequestCallbackAdapter.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc.impl;
+
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+
+/**
+ * Adapter from a {@link RequestCallback} interface to an {@link AsyncCallback}
+ * interface.
+ *
+ * For internal use only.
+ *
+ * @param <T> the type parameter for the {@link AsyncCallback}
+ */
+public class RequestCallbackAdapter<T> implements RequestCallback {
+ /**
+ * Enumeration used to read specific return types out of a
+ * {@link SerializationStreamReader}.
+ */
+ public enum ResponseReader {
+ BOOLEAN {
+ @Override
+ public Object read(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return streamReader.readBoolean();
+ }
+ },
+
+ BYTE {
+ @Override
+ public Object read(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return streamReader.readByte();
+ }
+ },
+
+ CHAR {
+ @Override
+ public Object read(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return streamReader.readChar();
+ }
+ },
+
+ DOUBLE {
+ @Override
+ public Object read(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return streamReader.readDouble();
+ }
+ },
+
+ FLOAT {
+ @Override
+ public Object read(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return streamReader.readFloat();
+ }
+ },
+
+ INT {
+ @Override
+ public Object read(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return streamReader.readInt();
+ }
+ },
+
+ LONG {
+ @Override
+ public Object read(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return streamReader.readLong();
+ }
+ },
+
+ OBJECT {
+ @Override
+ public Object read(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return streamReader.readObject();
+ }
+ },
+
+ SHORT {
+ @Override
+ public Object read(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return streamReader.readShort();
+ }
+ },
+
+ STRING {
+ @Override
+ public Object read(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return streamReader.readString();
+ }
+ },
+
+ VOID {
+ @Override
+ public Object read(SerializationStreamReader streamReader) {
+ return null;
+ }
+ };
+
+ public abstract Object read(SerializationStreamReader streamReader)
+ throws SerializationException;
+ }
+
+ /**
+ * Returns a string that encodes the result of a method invocation.
+ * Effectively, this just removes any headers from the encoded response.
+ *
+ * @param encodedResponse
+ * @return string that encodes the result of a method invocation
+ */
+ static String getEncodedInstance(String encodedResponse) {
+ assert (!isInvocationException(encodedResponse));
+ return encodedResponse.substring(4);
+ }
+
+ private static boolean isInvocationException(String encodedResponse) {
+ return !isThrownException(encodedResponse)
+ && !isReturnValue(encodedResponse);
+ }
+
+ /**
+ * Return <code>true</code> if the encoded response contains a value
+ * returned by the method invocation.
+ *
+ * @param encodedResponse
+ * @return <code>true</code> if the encoded response contains a value
+ * returned by the method invocation
+ */
+ private static boolean isReturnValue(String encodedResponse) {
+ return encodedResponse.startsWith("//OK");
+ }
+
+ /**
+ * Return <code>true</code> if the encoded response contains a checked
+ * exception that was thrown by the method invocation.
+ *
+ * @param encodedResponse
+ * @return <code>true</code> if the encoded response contains a checked
+ * exception that was thrown by the method invocation
+ */
+ private static boolean isThrownException(String encodedResponse) {
+ return encodedResponse.startsWith("//EX");
+ }
+
+ /**
+ * {@link AsyncCallback} to notify or success or failure.
+ */
+ private final AsyncCallback<T> callback;
+
+ /**
+ * Instance which will read the expected return type out of the
+ * {@link SerializationStreamReader}.
+ */
+ private final ResponseReader responseReader;
+
+ /**
+ * {@link Serializer} instance used by the
+ * {@link ClientSerializationStreamReader} for serialization.
+ */
+ private final Serializer serializer;
+
+ public RequestCallbackAdapter(Serializer serializer,
+ AsyncCallback<T> callback, ResponseReader responseReader) {
+ assert (serializer != null);
+ assert (callback != null);
+ assert (responseReader != null);
+
+ this.serializer = serializer;
+ this.callback = callback;
+ this.responseReader = responseReader;
+ }
+
+ public void onError(Request request, Throwable exception) {
+ callback.onFailure(exception);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void onResponseReceived(Request request, Response response) {
+ final ClientSerializationStreamReader streamReader = new ClientSerializationStreamReader(
+ serializer);
+ T result = null;
+ Throwable caught = null;
+ String encodedResponse = response.getText();
+ try {
+ if (isReturnValue(encodedResponse)) {
+ streamReader.prepareToRead(getEncodedInstance(encodedResponse));
+ result = (T) responseReader.read(streamReader);
+ } else if (isThrownException(encodedResponse)) {
+ streamReader.prepareToRead(getEncodedInstance(encodedResponse));
+ caught = (Throwable) streamReader.readObject();
+ } else {
+ assert (isInvocationException(encodedResponse));
+ caught = new com.google.gwt.user.client.rpc.InvocationException(
+ encodedResponse);
+ }
+ } catch (com.google.gwt.user.client.rpc.SerializationException e) {
+ caught = new com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException();
+ } catch (Throwable e) {
+ caught = e;
+ }
+
+ if (caught == null) {
+ callback.onSuccess(result);
+ } else {
+ callback.onFailure(caught);
+ }
+ }
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
index 1438787..44df0f5 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -16,7 +16,6 @@
package com.google.gwt.user.rebind.rpc;
import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
@@ -24,21 +23,16 @@
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JPackage;
import com.google.gwt.core.ext.typeinfo.JParameter;
-import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.generator.NameFactory;
import com.google.gwt.dev.util.Util;
-import com.google.gwt.user.client.ResponseTextHandler;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
-import com.google.gwt.user.client.rpc.InvocationException;
import com.google.gwt.user.client.rpc.SerializationException;
-import com.google.gwt.user.client.rpc.ServiceDefTarget;
-import com.google.gwt.user.client.rpc.ServiceDefTarget.NoServiceEntryPointSpecifiedException;
+import com.google.gwt.user.client.rpc.impl.RemoteServiceProxy;
import com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader;
import com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter;
+import com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.ResponseReader;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
@@ -49,6 +43,8 @@
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
/**
* Creates a client-side proxy for a
@@ -58,25 +54,34 @@
class ProxyCreator {
private static final String ENTRY_POINT_TAG = "gwt.defaultEntryPoint";
+ private static final Map<JPrimitiveType, ResponseReader> JPRIMITIVETYPE_TO_RESPONSEREADER = new HashMap<JPrimitiveType, ResponseReader>();
+
private static final String PROXY_SUFFIX = "_Proxy";
- /*
- * This method returns the real type name. Currently, it only affects
- * JParameterizedType since their names are not legal Java names.
- */
- private static String getJavaTypeName(JType type) {
- JParameterizedType parameterizedType = type.isParameterized();
- if (parameterizedType != null) {
- return parameterizedType.getRawType().getQualifiedSourceName();
- }
-
- return type.getQualifiedSourceName();
- }
-
private boolean enforceTypeVersioning;
private JClassType serviceIntf;
+ {
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.BOOLEAN,
+ ResponseReader.BOOLEAN);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.BYTE,
+ ResponseReader.BYTE);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.CHAR,
+ ResponseReader.CHAR);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.DOUBLE,
+ ResponseReader.DOUBLE);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.FLOAT,
+ ResponseReader.FLOAT);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.INT, ResponseReader.INT);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.LONG,
+ ResponseReader.LONG);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.SHORT,
+ ResponseReader.SHORT);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.VOID,
+ ResponseReader.VOID);
+ }
+
public ProxyCreator(JClassType serviceIntf) {
assert (serviceIntf.isInterface() != null);
this.serviceIntf = serviceIntf;
@@ -89,19 +94,31 @@
*/
public String create(TreeLogger logger, GeneratorContext context)
throws UnableToCompleteException {
- SourceWriter srcWriter = getSourceWriter(logger, context);
+ TypeOracle typeOracle = context.getTypeOracle();
+
+ JClassType serviceAsync = typeOracle.findType(serviceIntf.getQualifiedSourceName()
+ + "Async");
+ if (serviceAsync == null) {
+ logger.branch(TreeLogger.ERROR,
+ "Could not find an asynchronous version for the service interface "
+ + serviceIntf.getQualifiedSourceName(), null);
+ RemoteServiceAsyncValidator.logValidAsyncInterfaceDeclaration(logger,
+ serviceIntf);
+ throw new UnableToCompleteException();
+ }
+
+ SourceWriter srcWriter = getSourceWriter(logger, context, serviceAsync);
if (srcWriter == null) {
return getProxyQualifiedName();
}
- TypeOracle typeOracle = context.getTypeOracle();
-
// Make sure that the async and synchronous versions of the RemoteService
// agree with one another
//
RemoteServiceAsyncValidator rsav = new RemoteServiceAsyncValidator(logger,
typeOracle);
- rsav.validateRemoteServiceAsync(logger, serviceIntf);
+ Map<JMethod, JMethod> syncMethToAsyncMethMap = rsav.validate(logger,
+ serviceIntf, serviceAsync);
// Determine the set of serializable types
SerializableTypeOracleBuilder stob = new SerializableTypeOracleBuilder(
@@ -121,290 +138,36 @@
generateProxyFields(srcWriter, sto, serializationPolicyStrongName);
- generateServiceDefTargetImpl(srcWriter);
+ String typeSerializerName = sto.getTypeSerializerQualifiedName(serviceIntf);
+ generateProxyContructor(srcWriter, sto, serializationPolicyStrongName,
+ typeSerializerName);
- generateProxyMethods(srcWriter, sto);
+ generateProxyMethods(srcWriter, sto, syncMethToAsyncMethMap);
srcWriter.commit(logger);
return getProxyQualifiedName();
}
- /*
- * Given a type emit an expression for calling the correct
- * SerializationStreamReader method which reads the corresponding instance out
- * of the stream.
- */
- protected final void generateDecodeCall(SourceWriter w, JType type) {
- w.print("streamReader.");
- w.print(Shared.getStreamReadMethodNameFor(type) + "()");
- }
-
- /*
- * Given a type emit an expression for calling the correct
- * SerializationStreamWriter method which writes that type into the stream.
- */
- protected void generateEncodeCall(SourceWriter w, JParameter parameter) {
- JType paramType = parameter.getType();
- w.print("streamWriter.");
- w.print(Shared.getStreamWriteMethodNameFor(paramType));
- w.println("(" + parameter.getName() + ");");
- }
-
- /*
- * Calls the __ version to encode.
- */
- private void generateAsynchronousProxyMethod(SourceWriter w, JMethod method) {
-
- JType returnType = method.getReturnType();
- JParameter[] params = method.getParameters();
-
- NameFactory nameFactory = new NameFactory();
-
- for (int i = 0; i < params.length; ++i) {
- nameFactory.addName(params[i].getName());
- }
-
- w.println();
- w.print("public void " + method.getName() + "(");
- int i;
- for (i = 0; i < params.length; i++) {
- JParameter param = params[i];
- w.print((i > 0 ? ", " : "") + getJavaTypeName(param.getType()) + " "
- + param.getName());
- }
-
- w.println((i > 0 ? ", final " : "final ") + AsyncCallback.class.getName()
- + " callback) {");
- w.indent();
- w.println("final "
- + (ClientSerializationStreamReader.class.getName()
- + " streamReader = new "
- + ClientSerializationStreamReader.class.getName() + "(SERIALIZER);"));
- w.println("final "
- + (ClientSerializationStreamWriter.class.getName()
- + " streamWriter = new "
- + ClientSerializationStreamWriter.class.getName() + "(SERIALIZER, GWT.getModuleBaseURL(), SERIALIZATION_POLICY);"));
- w.println("try {");
- w.indent();
- {
- w.print("__" + method.getName() + "(streamWriter");
- for (i = 0; i < params.length; i++) {
- w.print(", " + params[i].getName());
- }
- w.println(");");
- }
- w.outdent();
-
- String exceptionName = nameFactory.createName("e");
- w.println("} catch (" + SerializationException.class.getName() + " "
- + exceptionName + ") {");
- w.indentln("callback.onFailure(" + exceptionName + ");");
- w.indentln("return;");
- w.println("}");
-
- // Generate the async response handler.
- //
- w.println(ResponseTextHandler.class.getName() + " handler = new "
- + ResponseTextHandler.class.getName() + "() {");
- w.indent();
- {
- w.println("public final void onCompletion(String encodedResponse) {");
- w.indent();
- {
- w.println("UncaughtExceptionHandler handler = GWT.getUncaughtExceptionHandler();");
- w.println("if (handler != null)");
- w.indent();
- {
- w.println("onCompletionAndCatch(encodedResponse, handler);");
- }
- w.outdent();
- w.println("else");
- w.indent();
- {
- w.println("onCompletionImpl(encodedResponse);");
- }
- w.outdent();
- }
- w.outdent();
- w.println("}");
-
- w.println("private void onCompletionAndCatch(String encodedResponse, UncaughtExceptionHandler handler) {");
- w.indent();
- {
- w.println("try {");
- w.indent();
- {
- w.println("onCompletionImpl(encodedResponse);");
- }
- w.outdent();
- w.println("} catch (Throwable e) {");
- w.indent();
- {
- w.println("handler.onUncaughtException(e);");
- }
- w.outdent();
- w.println("}");
- }
- w.outdent();
- w.println("}");
-
- w.println("private void onCompletionImpl(String encodedResponse) {");
- w.indent();
- {
- w.println("Object result = null;");
- w.println("Throwable caught = null;");
- w.println("try {");
- w.indent();
- {
- w.println("if (encodedResponse.startsWith(\"//OK\")) {");
- w.indent();
- {
- w.println("streamReader.prepareToRead(encodedResponse.substring(4));");
- w.print("result = ");
-
- JPrimitiveType primitive = returnType.isPrimitive();
- if (primitive == JPrimitiveType.VOID) {
- w.print("null");
- } else {
- if (primitive != null) {
- w.print("new ");
- w.print(getObjectWrapperName(primitive));
- w.print("(");
- generateDecodeCall(w, returnType);
- w.print(")");
- } else {
- generateDecodeCall(w, returnType);
- }
- }
- w.println(";");
- }
- w.outdent();
- w.println("} else if (encodedResponse.startsWith(\"//EX\")) {");
- w.indent();
- {
- w.println("streamReader.prepareToRead(encodedResponse.substring(4));");
- w.println("caught = (Throwable) streamReader.readObject();");
- }
- w.outdent();
- w.println("} else {");
- w.indent();
- {
- w.println("caught = new " + InvocationException.class.getName()
- + "(encodedResponse);");
- }
- w.outdent();
- w.println("}");
- }
- w.outdent();
- w.println("} catch (" + SerializationException.class.getName()
- + " e) {");
- w.indent();
- {
- w.println("caught = new "
- + IncompatibleRemoteServiceException.class.getName() + "();");
- }
- w.outdent();
- w.println("} catch (Throwable e) {");
- w.indent();
- {
- w.println("caught = e;");
- }
- w.outdent();
- w.println("}");
-
- w.println("if (caught == null)");
- w.indent();
- {
- w.println("callback.onSuccess(result);");
- }
- w.outdent();
- w.println("else");
- w.indent();
- {
- w.println("callback.onFailure(caught);");
- }
- w.outdent();
- }
- w.outdent();
- w.println("}");
- }
- w.outdent();
- w.println("};");
-
- // Make the asynchronous invocation.
- //
- w.println("if (!com.google.gwt.user.client.HTTPRequest.asyncPost(getServiceEntryPoint(), streamWriter.toString(), handler))");
- w.indentln("callback.onFailure(new "
- + InvocationException.class.getName()
- + "(\"Unable to initiate the asynchronous service invocation -- check the network connection\"));");
- w.outdent();
-
- w.println("}");
- }
-
/**
- * Generate the code that addresses the service.
+ * Generate the proxy constructor and delegate to the superclass constructor
+ * using the default address for the
+ * {@link com.google.gwt.user.client.rpc.RemoteService RemoteService}.
*/
- private void generateProxyEncode(SourceWriter w,
- SerializableTypeOracle serializableTypeOracle, JMethod method) {
- String methodName = method.getName();
- JParameter[] params = method.getParameters();
- w.println();
- w.print("private void __" + methodName + "("
- + ClientSerializationStreamWriter.class.getName() + " streamWriter");
- for (int i = 0; i < params.length; i++) {
- JParameter param = params[i];
- w.print(", " + getJavaTypeName(param.getType()) + " " + param.getName());
- }
-
- w.println(") throws " + SerializationException.class.getName() + " {");
- w.indent();
-
- // Make sure that we have a service def class name specified.
- //
- w.println("if (getServiceEntryPoint() == null)");
- String className = NoServiceEntryPointSpecifiedException.class.getName();
- className = className.replace('$', '.');
- w.indentln("throw new " + className + "();");
-
- // Generate code to describe just enough meta data for the server to locate
- // the service definition class and resolve the method overload.
- //
- w.println("streamWriter.prepareToWrite();");
-
- if (!shouldEnforceTypeVersioning()) {
- w.println("streamWriter.addFlags("
- + ClientSerializationStreamReader.class.getName()
- + ".SERIALIZATION_STREAM_FLAGS_NO_TYPE_VERSIONING);");
- }
-
- // Write the remote service interface's binary name
- JClassType remoteService = method.getEnclosingType();
- String remoteServiceBinaryName = serializableTypeOracle.getSerializedTypeName(remoteService);
- w.println("streamWriter.writeString(\"" + remoteServiceBinaryName + "\");");
-
- // Write the method name
- w.println("streamWriter.writeString(\"" + methodName + "\");");
-
- // Write the parameter count followed by the parameter values
- w.println("streamWriter.writeInt(" + params.length + ");");
- for (int i = 0; i < params.length; ++i) {
- JParameter param = params[i];
- w.println("streamWriter.writeString(\""
- + serializableTypeOracle.getSerializedTypeName(param.getType())
- + "\");");
- }
-
- // Encode the arguments.
- //
- for (int i = 0; i < params.length; i++) {
- JParameter param = params[i];
- generateEncodeCall(w, param);
- }
-
- w.outdent();
- w.println("}");
+ private void generateProxyContructor(SourceWriter srcWriter,
+ SerializableTypeOracle serializableTypeOracle,
+ String serializationPolicyStrongName, String typeSerializerName) {
+ srcWriter.println("public " + getProxySimpleName() + "() {");
+ srcWriter.indent();
+ srcWriter.println("super(GWT.getModuleBaseURL(),");
+ srcWriter.indent();
+ srcWriter.println(getDefaultServiceDefName() + ",");
+ srcWriter.println("SERIALIZATION_POLICY, ");
+ srcWriter.println("REMOTE_SERVICE_INTERFACE_NAME, ");
+ srcWriter.println("SERIALIZER);");
+ srcWriter.outdent();
+ srcWriter.outdent();
+ srcWriter.println("}");
}
/**
@@ -413,47 +176,146 @@
private void generateProxyFields(SourceWriter srcWriter,
SerializableTypeOracle serializableTypeOracle,
String serializationPolicyStrongName) {
+ // Initialize a field with binary name of the remote service interface
+ srcWriter.println("private static final String REMOTE_SERVICE_INTERFACE_NAME = \""
+ + serializableTypeOracle.getSerializedTypeName(serviceIntf) + "\";");
+ srcWriter.println("private static final String SERIALIZATION_POLICY =\""
+ + serializationPolicyStrongName + "\";");
String typeSerializerName = serializableTypeOracle.getTypeSerializerQualifiedName(serviceIntf);
srcWriter.println("private static final " + typeSerializerName
+ " SERIALIZER = new " + typeSerializerName + "();");
- srcWriter.println("private static final String SERIALIZATION_POLICY =\""
- + serializationPolicyStrongName + "\";");
- }
-
- private void generateProxyMethods(SourceWriter w,
- SerializableTypeOracle serializableTypeOracle) {
-
- JMethod[] methods = serviceIntf.getOverridableMethods();
- for (int i = 0; i < methods.length; ++i) {
- JMethod method = methods[i];
- generateProxyEncode(w, serializableTypeOracle, method);
- generateAsynchronousProxyMethod(w, method);
- }
+ srcWriter.println();
}
/**
- * Implements the ServiceDefTarget interface to allow clients to switch which
- * back-end service definition we send calls to.
+ * Generates the client's asynchronous proxy method.
*/
- private void generateServiceDefTargetImpl(SourceWriter w) {
- String serverDefName = getDefaultServiceDefName();
- if (serverDefName != null) {
- serverDefName = "\"" + serverDefName + "\"";
- } else {
- serverDefName = "null";
+ private void generateProxyMethod(SourceWriter w,
+ SerializableTypeOracle serializableTypeOracle, JMethod syncMethod,
+ JMethod asyncMethod) {
+
+ w.println();
+
+ // Write the method signature
+ JType asyncReturnType = asyncMethod.getReturnType();
+ w.print("public ");
+ w.print(asyncReturnType.getQualifiedSourceName());
+ w.print(" ");
+ w.print(asyncMethod.getName() + "(");
+
+ boolean needsComma = false;
+ boolean needsTryCatchBlock = false;
+ NameFactory nameFactory = new NameFactory();
+ JParameter[] asyncParams = asyncMethod.getParameters();
+ for (int i = 0; i < asyncParams.length; ++i) {
+ JParameter param = asyncParams[i];
+
+ if (needsComma) {
+ w.print(", ");
+ } else {
+ needsComma = true;
+ }
+
+ /*
+ * Ignoring the AsyncCallback parameter, if any method requires a call to
+ * SerializationStreamWriter.writeObject we need a try catch block
+ */
+ JType paramType = param.getType();
+ if (i < asyncParams.length - 1
+ && paramType.isPrimitive() == null
+ && !paramType.getQualifiedSourceName().equals(
+ String.class.getCanonicalName())) {
+ needsTryCatchBlock = true;
+ }
+
+ w.print(paramType.getParameterizedQualifiedSourceName());
+ w.print(" ");
+
+ String paramName = param.getName();
+ nameFactory.addName(paramName);
+ w.print(paramName);
+ }
+
+ w.println(") {");
+
+ w.indent();
+
+ w.print(ClientSerializationStreamWriter.class.getSimpleName());
+ w.print(" ");
+ String streamWriterName = nameFactory.createName("streamWriter");
+ w.println(streamWriterName + " = createStreamWriter();");
+ w.println("// createStreamWriter() prepared the stream and wrote the name of RemoteService");
+ if (needsTryCatchBlock) {
+ w.println("try {");
+ w.indent();
+ }
+
+ if (!shouldEnforceTypeVersioning()) {
+ w.println(streamWriterName + ".addFlags("
+ + ClientSerializationStreamReader.class.getName()
+ + ".SERIALIZATION_STREAM_FLAGS_NO_TYPE_VERSIONING);");
+ }
+
+ // Write the method name
+ w.println(streamWriterName + ".writeString(\"" + syncMethod.getName()
+ + "\");");
+
+ // Write the parameter count followed by the parameter values
+ JParameter[] syncParams = syncMethod.getParameters();
+ w.println(streamWriterName + ".writeInt(" + syncParams.length + ");");
+ for (JParameter param : syncParams) {
+ w.println(streamWriterName + ".writeString(\""
+ + serializableTypeOracle.getSerializedTypeName(param.getType())
+ + "\");");
+ }
+
+ // Encode the arguments.
+ //
+ for (JParameter param : syncParams) {
+ JType paramType = param.getType();
+ w.print(streamWriterName + ".");
+ w.print(Shared.getStreamWriteMethodNameFor(paramType));
+ w.println("(" + param.getName() + ");");
+ }
+
+ JParameter callbackParam = asyncParams[asyncParams.length - 1];
+ String callbackName = callbackParam.getName();
+ if (needsTryCatchBlock) {
+ w.outdent();
+ w.print("} catch (SerializationException ");
+ String exceptionName = nameFactory.createName("ex");
+ w.println(exceptionName + ") {");
+ w.indent();
+ w.println(callbackName + ".onFailure(" + exceptionName + ");");
+ w.outdent();
+ w.println("}");
}
w.println();
- w.println("String fServiceEntryPoint = " + serverDefName + ";");
- w.println();
- w.println("public String getServiceEntryPoint() { return fServiceEntryPoint; }");
- w.println();
- w.println("public void setServiceEntryPoint(String s) { fServiceEntryPoint = s; }");
+ if (asyncReturnType != JPrimitiveType.VOID) {
+ w.print("return ");
+ }
+
+ // Call the doInvoke method to actually send the request.
+ w.print("doInvoke(");
+ JType returnType = syncMethod.getReturnType();
+ w.print("ResponseReader." + getResponseReaderFor(returnType).name());
+ w.print(", " + streamWriterName + ".toString(), ");
+ w.println(callbackName + ");");
+ w.outdent();
+ w.println("}");
}
- private String getAsyncIntfQualifiedName() {
- String asyncIntf = serviceIntf.getQualifiedSourceName() + "Async";
- return asyncIntf;
+ private void generateProxyMethods(SourceWriter w,
+ SerializableTypeOracle serializableTypeOracle,
+ Map<JMethod, JMethod> syncMethToAsyncMethMap) {
+ JMethod[] syncMethods = serviceIntf.getOverridableMethods();
+ for (JMethod syncMethod : syncMethods) {
+ JMethod asyncMethod = syncMethToAsyncMethMap.get(syncMethod);
+ assert (asyncMethod != null);
+
+ generateProxyMethod(w, serializableTypeOracle, syncMethod, asyncMethod);
+ }
}
private String getDefaultServiceDefName() {
@@ -461,32 +323,10 @@
if (metaData.length == 0) {
return null;
}
+
return serviceIntf.getMetaData(ENTRY_POINT_TAG)[0][0];
}
- /*
- * Determine the name of the object wrapper class to instantiate based on the
- * the type of the primitive.
- */
- private String getObjectWrapperName(JPrimitiveType primitive) {
- if (primitive == JPrimitiveType.INT) {
- return "Integer";
- } else if (primitive == JPrimitiveType.CHAR) {
- return "Character";
- }
-
- return Shared.capitalize(primitive.getSimpleSourceName());
- }
-
- private String getPackageName() {
- JPackage pkg = serviceIntf.getPackage();
- if (pkg != null) {
- return pkg.getName();
- }
-
- return "";
- }
-
private String getProxyQualifiedName() {
String[] name = Shared.synthesizeTopLevelClassName(serviceIntf,
PROXY_SUFFIX);
@@ -499,23 +339,43 @@
return name[1];
}
- private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext ctx) {
- PrintWriter printWriter = ctx.tryCreate(logger, getPackageName(),
+ private ResponseReader getResponseReaderFor(JType returnType) {
+ if (returnType.isPrimitive() != null) {
+ return JPRIMITIVETYPE_TO_RESPONSEREADER.get(returnType.isPrimitive());
+ }
+
+ if (returnType.getQualifiedSourceName().equals(
+ String.class.getCanonicalName())) {
+ return ResponseReader.STRING;
+ }
+
+ return ResponseReader.OBJECT;
+ }
+
+ private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext ctx,
+ JClassType serviceAsync) {
+ JPackage serviceIntfPkg = serviceAsync.getPackage();
+ String packageName = serviceIntfPkg == null ? "" : serviceIntfPkg.getName();
+ PrintWriter printWriter = ctx.tryCreate(logger, packageName,
getProxySimpleName());
if (printWriter == null) {
return null;
}
ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(
- getPackageName(), getProxySimpleName());
+ packageName, getProxySimpleName());
- composerFactory.addImport(GWT.class.getName());
- String className = UncaughtExceptionHandler.class.getName();
- className = className.replace('$', '.');
- composerFactory.addImport(className);
+ String[] imports = new String[] {
+ RemoteServiceProxy.class.getCanonicalName(),
+ ClientSerializationStreamWriter.class.getCanonicalName(),
+ GWT.class.getCanonicalName(), ResponseReader.class.getCanonicalName(),
+ SerializationException.class.getCanonicalName()};
+ for (String imp : imports) {
+ composerFactory.addImport(imp);
+ }
- composerFactory.addImplementedInterface(ServiceDefTarget.class.getName());
- composerFactory.addImplementedInterface(getAsyncIntfQualifiedName());
+ composerFactory.setSuperclass(RemoteServiceProxy.class.getSimpleName());
+ composerFactory.addImplementedInterface(serviceAsync.getParameterizedQualifiedSourceName());
return composerFactory.createSourceWriter(ctx, printWriter);
}
diff --git a/user/src/com/google/gwt/user/rebind/rpc/RemoteServiceAsyncValidator.java b/user/src/com/google/gwt/user/rebind/rpc/RemoteServiceAsyncValidator.java
index 3a1d3c6..93f53c0 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/RemoteServiceAsyncValidator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/RemoteServiceAsyncValidator.java
@@ -22,10 +22,13 @@
import com.google.gwt.core.ext.typeinfo.JPackage;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.http.client.Request;
import com.google.gwt.user.client.rpc.AsyncCallback;
+import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
@@ -34,6 +37,47 @@
* {@link com.google.gwt.user.client.rpc.RemoteService RemoteService} interface.
*/
class RemoteServiceAsyncValidator {
+ static void logValidAsyncInterfaceDeclaration(TreeLogger logger,
+ JClassType remoteService) {
+ TreeLogger branch = logger.branch(TreeLogger.INFO,
+ "A valid definition for the asynchronous version of interface '"
+ + remoteService.getQualifiedSourceName() + "' would be:\n", null);
+ branch.log(TreeLogger.ERROR,
+ synthesizeAsynchronousInterfaceDefinition(remoteService), null);
+ }
+
+ private static String computeAsyncMethodSignature(JMethod syncMethod,
+ JClassType asyncCallbackClass) {
+ return computeInternalSignature(syncMethod) + "/"
+ + asyncCallbackClass.getQualifiedSourceName();
+ }
+
+ private static String computeInternalSignature(JMethod method) {
+ StringBuffer sb = new StringBuffer();
+ sb.setLength(0);
+ sb.append(method.getName());
+ JParameter[] params = method.getParameters();
+ for (JParameter param : params) {
+ sb.append("/");
+ JType paramType = param.getType();
+ sb.append(paramType.getErasedType().getQualifiedSourceName());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Builds a map of asynchronous method internal signatures to the
+ * corresponding asynchronous {@link JMethod}.
+ */
+ private static Map<String, JMethod> initializeAsyncMethodMap(
+ JMethod[] asyncMethods) {
+ Map<String, JMethod> sigs = new TreeMap<String, JMethod>();
+ for (JMethod asyncMethod : asyncMethods) {
+ sigs.put(computeInternalSignature(asyncMethod), asyncMethod);
+ }
+ return sigs;
+ }
+
private static String synthesizeAsynchronousInterfaceDefinition(
JClassType serviceIntf) {
StringBuffer sb = new StringBuffer();
@@ -82,14 +126,27 @@
return sb.toString();
}
+ private static void validationFailed(TreeLogger branch,
+ JClassType remoteService) throws UnableToCompleteException {
+ logValidAsyncInterfaceDeclaration(branch, remoteService);
+ throw new UnableToCompleteException();
+ }
+
+ /**
+ * {@link JClassType} for the {@link AsyncCallback} interface.
+ */
private final JClassType asyncCallbackClass;
- private final TypeOracle typeOracle;
+
+ /**
+ * {@link JClassType} for the {@link Request} class.
+ */
+ private final JClassType requestType;
RemoteServiceAsyncValidator(TreeLogger logger, TypeOracle typeOracle)
throws UnableToCompleteException {
- this.typeOracle = typeOracle;
try {
asyncCallbackClass = typeOracle.getType(AsyncCallback.class.getName());
+ requestType = typeOracle.getType(Request.class.getCanonicalName());
} catch (NotFoundException e) {
logger.log(TreeLogger.ERROR, null, e);
throw new UnableToCompleteException();
@@ -97,103 +154,74 @@
}
/**
- * Checks that for there is an asynchronous
- * {@link com.google.gwt.user.client.rpc.RemoteService RemoteService}
- * interface and that it has an asynchronous version of every synchronous
- * method.
+ * Checks that for every method on the synchronous remote service interface
+ * there is a corresponding asynchronous version in the asynchronous version
+ * of the remote service. If the validation succeeds, a map of synchronous to
+ * asynchronous methods is returned.
+ *
+ * @param logger
+ * @param remoteService
+ * @return map of synchronous method to asynchronous method
*
* @throws UnableToCompleteException if the asynchronous
* {@link com.google.gwt.user.client.rpc.RemoteService RemoteService}
* was not found, or if it does not have an asynchronous method
* version of every synchronous one
*/
- public void validateRemoteServiceAsync(TreeLogger logger,
- JClassType remoteService) throws UnableToCompleteException {
+ public Map<JMethod, JMethod> validate(TreeLogger logger,
+ JClassType remoteService, JClassType remoteServiceAsync)
+ throws UnableToCompleteException {
TreeLogger branch = logger.branch(TreeLogger.DEBUG,
"Checking the synchronous interface '"
+ remoteService.getQualifiedSourceName()
+ "' against its asynchronous version '"
- + remoteService.getQualifiedSourceName() + "Async'", null);
- boolean failed = false;
- JClassType serviceAsync = typeOracle.findType(remoteService.getQualifiedSourceName()
- + "Async");
- if (serviceAsync == null) {
- failed = true;
- branch.branch(TreeLogger.ERROR,
- "Could not find an asynchronous version for the service interface "
- + remoteService.getQualifiedSourceName(), null);
- } else {
- JMethod[] asyncMethods = serviceAsync.getOverridableMethods();
- JMethod[] syncMethods = remoteService.getOverridableMethods();
+ + remoteServiceAsync.getQualifiedSourceName() + "'", null);
- if (asyncMethods.length != syncMethods.length) {
- branch.branch(TreeLogger.ERROR, "The asynchronous version of "
- + remoteService.getQualifiedSourceName() + " has "
- + (asyncMethods.length > syncMethods.length ? "more" : "less")
- + " methods than the synchronous version", null);
+ // Sync and async versions must have the same number of methods
+ JMethod[] asyncMethods = remoteServiceAsync.getOverridableMethods();
+ JMethod[] syncMethods = remoteService.getOverridableMethods();
+ if (asyncMethods.length != syncMethods.length) {
+ branch.branch(TreeLogger.ERROR, "The asynchronous version of "
+ + remoteService.getQualifiedSourceName() + " has "
+ + (asyncMethods.length > syncMethods.length ? "more" : "less")
+ + " methods than the synchronous version", null);
+ validationFailed(branch, remoteService);
+ }
+
+ // Check that for every sync method there is a corresponding async method
+ boolean failed = false;
+ Map<String, JMethod> asyncMethodMap = initializeAsyncMethodMap(asyncMethods);
+ Map<JMethod, JMethod> syncMethodToAsyncMethodMap = new HashMap<JMethod, JMethod>();
+ for (JMethod syncMethod : syncMethods) {
+ String asyncSig = computeAsyncMethodSignature(syncMethod,
+ asyncCallbackClass);
+ JMethod asyncMethod = asyncMethodMap.get(asyncSig);
+ if (asyncMethod == null) {
+ branch.branch(TreeLogger.ERROR,
+ "Missing asynchronous version of the synchronous method '"
+ + syncMethod.getReadableDeclaration() + "'", null);
failed = true;
} else {
- Map<String, JMethod> asyncMethodMap = initializeAsyncMethodMap(asyncMethods);
- for (JMethod syncMethod : syncMethods) {
- String asyncSig = computeAsyncMethodSignature(syncMethod);
- JMethod asyncMethod = asyncMethodMap.get(asyncSig);
- if (asyncMethod == null) {
- branch.branch(TreeLogger.ERROR,
- "Missing asynchronous version of the synchronous method '"
- + syncMethod.getReadableDeclaration() + "'", null);
- failed = true;
- } else if (asyncMethod.getReturnType() != JPrimitiveType.VOID) {
- branch.branch(TreeLogger.ERROR,
- "The asynchronous version of the synchronous method '"
- + syncMethod.getReadableDeclaration()
- + "' must have a 'void' return type", null);
- failed = true;
- }
+ // TODO if async param is parameterized make sure that the sync return
+ // type is assignable to the first type argument
+ JType returnType = asyncMethod.getReturnType();
+ if (returnType != JPrimitiveType.VOID && returnType != requestType) {
+ branch.branch(TreeLogger.ERROR,
+ "The asynchronous version of the synchronous method '"
+ + syncMethod.getReadableDeclaration()
+ + "' must have a return type of 'void' or '"
+ + Request.class.getCanonicalName() + "'", null);
+ failed = true;
+ } else {
+ syncMethodToAsyncMethodMap.put(syncMethod, asyncMethod);
}
}
}
if (failed) {
- logValidAsyncInterfaceDeclaration(branch, remoteService);
- throw new UnableToCompleteException();
+ validationFailed(branch, remoteService);
}
- }
- private String computeAsyncMethodSignature(JMethod syncMethod) {
- return computeInternalSignature(syncMethod) + "/"
- + asyncCallbackClass.getQualifiedSourceName();
- }
-
- private String computeInternalSignature(JMethod method) {
- StringBuffer sb = new StringBuffer();
- sb.setLength(0);
- sb.append(method.getName());
- JParameter[] params = method.getParameters();
- for (JParameter param : params) {
- sb.append("/");
- sb.append(param.getType().getQualifiedSourceName());
- }
- return sb.toString();
- }
-
- /**
- * Builds a map of asynchronous method internal signatures to the
- * corresponding asynchronous {@link JMethod}.
- */
- private Map<String, JMethod> initializeAsyncMethodMap(JMethod[] asyncMethods) {
- Map<String, JMethod> sigs = new TreeMap<String, JMethod>();
- for (JMethod asyncMethod : asyncMethods) {
- sigs.put(computeInternalSignature(asyncMethod), asyncMethod);
- }
- return sigs;
- }
-
- private void logValidAsyncInterfaceDeclaration(TreeLogger logger,
- JClassType remoteService) {
- TreeLogger branch = logger.branch(TreeLogger.INFO,
- "A valid definition for the asynchronous version of interface '"
- + remoteService.getQualifiedSourceName() + "' would be:\n", null);
- branch.log(TreeLogger.ERROR,
- synthesizeAsynchronousInterfaceDefinition(remoteService), null);
+ return syncMethodToAsyncMethodMap;
}
}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/user/server/rpc/RPC.java b/user/src/com/google/gwt/user/server/rpc/RPC.java
index 836c81f..20ee0ef 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPC.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPC.java
@@ -160,7 +160,7 @@
* assignable to the requested {@link RemoteService} interface
* </ul>
*/
- public static RPCRequest decodeRequest(String encodedRequest, Class type) {
+ public static RPCRequest decodeRequest(String encodedRequest, Class<?> type) {
return decodeRequest(encodedRequest, type, null);
}
@@ -216,7 +216,7 @@
* assignable to the requested {@link RemoteService} interface
* </ul>
*/
- public static RPCRequest decodeRequest(String encodedRequest, Class type,
+ public static RPCRequest decodeRequest(String encodedRequest, Class<?> type,
SerializationPolicyProvider serializationPolicyProvider) {
if (encodedRequest == null) {
throw new NullPointerException("encodedRequest cannot be null");
@@ -279,22 +279,21 @@
}
}
- Method method = findInterfaceMethod(serviceIntf, serviceMethodName,
- parameterTypes, true);
+ try {
+ Method method = serviceIntf.getMethod(serviceMethodName, parameterTypes);
- if (method == null) {
+ Object[] parameterValues = new Object[parameterTypes.length];
+ for (int i = 0; i < parameterValues.length; i++) {
+ parameterValues[i] = streamReader.deserializeValue(parameterTypes[i]);
+ }
+
+ return new RPCRequest(method, parameterValues, serializationPolicy);
+
+ } catch (NoSuchMethodException e) {
throw new IncompatibleRemoteServiceException(
formatMethodNotFoundErrorMessage(serviceIntf, serviceMethodName,
parameterTypes));
}
-
- Object[] parameterValues = new Object[parameterTypes.length];
- for (int i = 0; i < parameterValues.length; i++) {
- parameterValues[i] = streamReader.deserializeValue(parameterTypes[i]);
- }
-
- return new RPCRequest(method, parameterValues, serializationPolicy);
-
} catch (SerializationException ex) {
throw new IncompatibleRemoteServiceException(ex.getMessage(), ex);
}
@@ -578,29 +577,6 @@
return bufferStr;
}
- /**
- * Find the invoked method on either the specified interface or any super.
- */
- private static Method findInterfaceMethod(Class<?> intf, String methodName,
- Class<?>[] paramTypes, boolean includeInherited) {
- try {
- return intf.getDeclaredMethod(methodName, paramTypes);
- } catch (NoSuchMethodException e) {
- if (includeInherited) {
- Class<?>[] superintfs = intf.getInterfaces();
- for (int i = 0; i < superintfs.length; i++) {
- Method method = findInterfaceMethod(superintfs[i], methodName,
- paramTypes, true);
- if (method != null) {
- return method;
- }
- }
- }
-
- return null;
- }
- }
-
private static String formatIllegalAccessErrorMessage(Object target,
Method serviceMethod) {
StringBuffer sb = new StringBuffer();