Fixes issue 1059. The problem is that all of the detected version incompatibilities between a RemoteService client and its corresponding server get converted into an InvocationException. This is a problem because the client code does not know what caused the InvocationException and the only thing that it can do is retry.
This patch introduces an IncompatbileRemoteServiceException which is only passed to the client's AsyncCallback.onFailure(Throwable) method when:
* The requested RemoteService cannot be located via Class.forName(String) on the server.
* The requested RemoteService interface is not implemented by the RemoteServiceServlet instance which is configured to process the request.
* The requested service method is not defined or inherited by the requested RemoteService interface.
* One of the types used in the RemoteService method invocation of the RemoteService method has had fields added or removed.
* The client receives a type from the server which it cannot deserialize.
The idea is that whenever a client receives an IncompatibleRemoteServiceException it should do what ever cleanup it can and eventually cause the browser to be refreshed.
Patch by: mmendez
Review by: jgw, rjellinghaus
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1099 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/javadoc/com/google/gwt/examples/rpc/server/AdvancedExample.java b/user/javadoc/com/google/gwt/examples/rpc/server/AdvancedExample.java
index 632acc6..a274c8f 100644
--- a/user/javadoc/com/google/gwt/examples/rpc/server/AdvancedExample.java
+++ b/user/javadoc/com/google/gwt/examples/rpc/server/AdvancedExample.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.examples.rpc.server;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
@@ -42,56 +43,60 @@
String payload = readPayloadAsUtf8(httpRequest);
try {
- RPCRequest rpcRequest = RPC.decodeRequest(payload);
-
- Object targetInstance = getInstanceToHandleRequest(httpRequest,
- rpcRequest);
-
- Method targetMethod = maybeMapRequestedMethod(targetInstance,
- rpcRequest.getMethod());
-
- Object[] targetParameters = maybeMapParameters(rpcRequest.getParameters());
-
try {
- Object result = targetMethod.invoke(targetInstance, targetParameters);
-
- result = maybeMapResult(rpcRequest.getMethod(), result);
-
- /*
- * Encode the object that will be given to the client code's
- * AsyncCallback::onSuccess(Object) method.
- */
- String encodedResult = RPC.encodeResponseForSuccess(
- rpcRequest.getMethod(), result);
-
- sendResponseForSuccess(httpResponse, encodedResult);
- } catch (IllegalArgumentException e) {
- SecurityException securityException = new SecurityException(
- "Blocked attempt to invoke method " + targetMethod);
- securityException.initCause(e);
- throw securityException;
- } catch (IllegalAccessException e) {
- SecurityException securityException = new SecurityException(
- "Blocked attempt to access inaccessible method "
- + targetMethod
- + (targetInstance != null ? " on target " + targetInstance : ""));
- securityException.initCause(e);
- throw securityException;
- } catch (InvocationTargetException e) {
- Throwable cause = e.getCause();
-
- Throwable mappedThrowable = maybeMapThrowable(cause,
+ RPCRequest rpcRequest = RPC.decodeRequest(payload);
+
+ Object targetInstance = getInstanceToHandleRequest(httpRequest,
+ rpcRequest);
+
+ Method targetMethod = maybeMapRequestedMethod(targetInstance,
rpcRequest.getMethod());
-
- /*
- * Encode the exception that will be passed back to the client's client
- * code's AsyncCallback::onFailure(Throwable) method.
- */
- String failurePayload = RPC.encodeResponseForFailure(
- rpcRequest.getMethod(), mappedThrowable);
-
- sendResponseForFailure(httpResponse, failurePayload);
- }
+
+ Object[] targetParameters = maybeMapParameters(rpcRequest.getParameters());
+
+ try {
+ Object result = targetMethod.invoke(targetInstance, targetParameters);
+
+ result = maybeMapResult(rpcRequest.getMethod(), result);
+
+ /*
+ * Encode the object that will be given to the client code's
+ * AsyncCallback::onSuccess(Object) method.
+ */
+ String encodedResult = RPC.encodeResponseForSuccess(
+ rpcRequest.getMethod(), result);
+
+ sendResponseForSuccess(httpResponse, encodedResult);
+ } catch (IllegalArgumentException e) {
+ SecurityException securityException = new SecurityException(
+ "Blocked attempt to invoke method " + targetMethod);
+ securityException.initCause(e);
+ throw securityException;
+ } catch (IllegalAccessException e) {
+ SecurityException securityException = new SecurityException(
+ "Blocked attempt to access inaccessible method "
+ + targetMethod
+ + (targetInstance != null ? " on target " + targetInstance : ""));
+ securityException.initCause(e);
+ throw securityException;
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+
+ Throwable mappedThrowable = maybeMapThrowable(cause,
+ rpcRequest.getMethod());
+
+ /*
+ * Encode the exception that will be passed back to the client's client
+ * code's AsyncCallback::onFailure(Throwable) method.
+ */
+ String failurePayload = RPC.encodeResponseForFailure(
+ rpcRequest.getMethod(), mappedThrowable);
+
+ sendResponseForFailure(httpResponse, failurePayload);
+ }
+ } catch (IncompatibleRemoteServiceException e) {
+ sendResponseForFailure(httpResponse, RPC.encodeResponseForFailure(null, e));
+ }
} catch (Throwable e) {
/*
* Return a generic error which will be passed to the client code's
diff --git a/user/javadoc/com/google/gwt/examples/rpc/server/CanonicalExample.java b/user/javadoc/com/google/gwt/examples/rpc/server/CanonicalExample.java
index 1c8c38c..1cebcf1 100644
--- a/user/javadoc/com/google/gwt/examples/rpc/server/CanonicalExample.java
+++ b/user/javadoc/com/google/gwt/examples/rpc/server/CanonicalExample.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.examples.rpc.server;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
@@ -29,9 +30,12 @@
* that encodes either the method return or an exception thrown by it.
*/
public String processCall(String payload) throws SerializationException {
- RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass());
-
- return RPC.invokeAndEncodeResponse(this, rpcRequest.getMethod(),
- rpcRequest.getParameters());
+ try {
+ RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass());
+ return RPC.invokeAndEncodeResponse(this, rpcRequest.getMethod(),
+ rpcRequest.getParameters());
+ } catch (IncompatibleRemoteServiceException ex) {
+ return RPC.encodeResponseForFailure(null, ex);
+ }
}
}
diff --git a/user/src/com/google/gwt/user/client/rpc/AsyncCallback.java b/user/src/com/google/gwt/user/client/rpc/AsyncCallback.java
index dbb1096..2f05dda 100644
--- a/user/src/com/google/gwt/user/client/rpc/AsyncCallback.java
+++ b/user/src/com/google/gwt/user/client/rpc/AsyncCallback.java
@@ -64,6 +64,9 @@
* // Convenient way to find out which exception was thrown.
* try {
* throw caught;
+ * } catch (IncompatibleRemoteServiceException e) {
+ * // this client is not compatible with the server; cleanup and refresh the
+ * // browser
* } catch (InvocationException e) {
* // the call didn't complete cleanly
* } catch (ShapeException e) {
@@ -83,6 +86,17 @@
/**
* Called when an asynchronous call fails to complete normally.
+ * {@link IncompatibleRemoteServiceException}s, {@link InvocationException}s,
+ * or checked exceptions thrown by the service method are examples of the type
+ * of failures that can be passed to this method.
+ *
+ * <p>
+ * If <code>caught</code> is an instance of an
+ * {@link IncompatibleRemoteServiceException} the application should try to
+ * get into a state where a browser refresh can be safely done.
+ * </p>
+ *
+ * @param caught failure encountered while executing a remote procedure call
*/
void onFailure(Throwable caught);
diff --git a/user/src/com/google/gwt/user/client/rpc/IncompatibleRemoteServiceException.java b/user/src/com/google/gwt/user/client/rpc/IncompatibleRemoteServiceException.java
new file mode 100644
index 0000000..384a16a
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/IncompatibleRemoteServiceException.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+/**
+ * Exception that will be passed to the
+ * {@link AsyncCallback#onFailure(Throwable)} method when an incompatibility is
+ * detected between a {@link RemoteService} client and its corresponding
+ * {@link RemoteService} server.
+ *
+ * <p>
+ * The correct response to receiving an instance of this exception in the
+ * {@link AsyncCallback#onFailure(Throwable)} method is to get the application
+ * into a state where a browser refresh can be done.
+ * </p>
+ *
+ * <p>
+ * This exception can be caused by the following problems:
+ * <ul>
+ * <li>The requested {@link RemoteService} cannot be located via
+ * {@link Class#forName(String)} on the server.</li>
+ * <li>The requested {@link RemoteService} interface is not implemented by the
+ * {@link com.google.gwt.user.server.rpc.RemoteServiceServlet RemoteServiceServlet}
+ * instance which is configured to process the request.</li>
+ * <li>The requested service method is not defined or inherited by the
+ * requested {@link RemoteService} interface.</li>
+ * <li>One of the types used in the {@link RemoteService} method invocation has
+ * had fields added or removed.</li>
+ * <li>The client code receives a type from the server which it cannot
+ * deserialize.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>
+ * Note that on the client, the {@link #getCause()} always return
+ * <code>null</code>.
+ * </p>
+ */
+public final class IncompatibleRemoteServiceException extends RuntimeException
+ implements IsSerializable {
+ /**
+ * Constructor used by RPC serialization. Note that the client side code will
+ * always get a generic error message.
+ */
+ public IncompatibleRemoteServiceException() {
+ super(
+ "This application is out of date, please click the refresh button on your browser");
+ }
+
+ /**
+ * Constructs an instance with the specified message.
+ */
+ public IncompatibleRemoteServiceException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs an instance with the specified message and cause.
+ */
+ public IncompatibleRemoteServiceException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
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 3559feb..e2f11c0 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -30,6 +30,7 @@
import com.google.gwt.dev.generator.NameFactory;
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;
@@ -179,8 +180,7 @@
String exceptionName = nameFactory.createName("e");
w.println("} catch (" + SerializationException.class.getName() + " "
+ exceptionName + ") {");
- w.indentln("callback.onFailure(new " + InvocationException.class.getName()
- + "(" + exceptionName + ".getMessage()));");
+ w.indentln("callback.onFailure(" + exceptionName + ");");
w.indentln("return;");
w.println("}");
@@ -278,6 +278,12 @@
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();
{
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
index f8e4761..10498a6 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -28,6 +28,7 @@
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.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
@@ -458,11 +459,14 @@
streamWriterClass = typeOracle.getType(SerializationStreamWriter.class.getName());
// String is always serializable
- MetaTypeInfo mti = getMetaTypeInfo(stringClass);
- mti.setSerializable(true);
+ MetaTypeInfo stringMti = getMetaTypeInfo(stringClass);
+ stringMti.setSerializable(true);
+
+ // IncompatibleRemoteServiceException is always serializable
+ MetaTypeInfo incompatibleRemoteServiceExceptionMti = getMetaTypeInfo(typeOracle.getType(IncompatibleRemoteServiceException.class.getName()));
+ incompatibleRemoteServiceExceptionMti.setSerializable(true);
remoteServiceAsyncValidator = new RemoteServiceAsyncValidator(typeOracle);
-
} catch (NotFoundException e) {
rootLogger.log(TreeLogger.ERROR, null, e);
throw new UnableToCompleteException();
@@ -1001,4 +1005,4 @@
}
}
}
-}
\ 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 acb36ff..f575400 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPC.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPC.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.user.server.rpc;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.impl.ServerSerializableTypeOracle;
@@ -109,20 +110,21 @@
* the service method
* @return an {@link RPCRequest} instance
*
- * @throws SerializationException if the encodedRequest contents cannot be
- * deserialized
- * @throws SecurityException if any of the following conditions apply:
+ * @throws IncompatibleRemoteServiceException if any of the following
+ * conditions apply:
* <ul>
+ * <li>if the types in the encoded request cannot be deserialized</li>
* <li><code>RPC.class.getClassLoader()</code> cannot load the
- * service interface requested in the encoded request</li>
+ * service interface requested in the encodedRequest</li>
* <li>the requested interface is not assignable to
* {@link RemoteService}</li>
- * <li>the service method requested in the encoded request is not a
+ * <li>the service method requested in the encodedRequest is not a
* member of the requested service interface</li>
+ * <li>the type parameter is not <code>null</code> and is not
+ * assignable to the requested {@link RemoteService} interface
* </ul>
*/
- public static RPCRequest decodeRequest(String encodedRequest)
- throws SerializationException {
+ public static RPCRequest decodeRequest(String encodedRequest) {
return decodeRequest(encodedRequest, null);
}
@@ -149,10 +151,10 @@
*
* @throws NullPointerException if the encodedRequest is <code>null</code>
* @throws IllegalArgumentException if the encodedRequest is an empty string
- * @throws SerializationException if the types in the encoded request cannot
- * be deserialized
- * @throws SecurityException if any of the following conditions apply:
+ * @throws IncompatibleRemoteServiceException if any of the following
+ * conditions apply:
* <ul>
+ * <li>if the types in the encoded request cannot be deserialized</li>
* <li><code>RPC.class.getClassLoader()</code> cannot load the
* service interface requested in the encodedRequest</li>
* <li>the requested interface is not assignable to
@@ -163,8 +165,7 @@
* assignable to the requested {@link RemoteService} interface
* </ul>
*/
- public static RPCRequest decodeRequest(String encodedRequest, Class type)
- throws SerializationException {
+ public static RPCRequest decodeRequest(String encodedRequest, Class type) {
if (encodedRequest == null) {
throw new NullPointerException("encodedRequest cannot be null");
}
@@ -173,102 +174,103 @@
throw new IllegalArgumentException("encodedRequest cannot be empty");
}
- ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader(
- serializableTypeOracle);
- streamReader.prepareToRead(encodedRequest);
-
- String serviceIntfName = streamReader.readString();
-
- if (type != null) {
- if (!implementsInterface(type, serviceIntfName)) {
- // The service does not implement the requested interface
- throw new SecurityException("Blocked attempt to access interface '"
- + serviceIntfName + "', which is not implemented by '"
- + printTypeName(type)
- + "'; this is either misconfiguration or a hack attempt");
- }
- }
-
- Class serviceIntf;
try {
- serviceIntf = getClassFromSerializedName(serviceIntfName);
- if (!RemoteService.class.isAssignableFrom(serviceIntf)) {
- // The requested interface is not a RemoteService interface
- throw new SecurityException(
- "Blocked attempt to access interface '"
- + printTypeName(serviceIntf)
- + "', which doesn't extend RemoteService; this is either misconfiguration or a hack attempt");
+ ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader(
+ serializableTypeOracle);
+ streamReader.prepareToRead(encodedRequest);
+
+ String serviceIntfName = streamReader.readString();
+
+ if (type != null) {
+ if (!implementsInterface(type, serviceIntfName)) {
+ // The service does not implement the requested interface
+ throw new IncompatibleRemoteServiceException(
+ "Blocked attempt to access interface '" + serviceIntfName
+ + "', which is not implemented by '" + printTypeName(type)
+ + "'; this is either misconfiguration or a hack attempt");
+ }
}
- } catch (ClassNotFoundException e) {
- SecurityException securityException = new SecurityException(
- "Could not locate requested interface '" + serviceIntfName
- + "' in default classloader");
- securityException.initCause(e);
- throw securityException;
- }
- String serviceMethodName = streamReader.readString();
-
- int paramCount = streamReader.readInt();
- Class[] parameterTypes = new Class[paramCount];
-
- for (int i = 0; i < parameterTypes.length; i++) {
- String paramClassName = streamReader.readString();
+ Class serviceIntf;
try {
- parameterTypes[i] = getClassFromSerializedName(paramClassName);
+ serviceIntf = getClassFromSerializedName(serviceIntfName);
+ if (!RemoteService.class.isAssignableFrom(serviceIntf)) {
+ // The requested interface is not a RemoteService interface
+ throw new IncompatibleRemoteServiceException(
+ "Blocked attempt to access interface '"
+ + printTypeName(serviceIntf)
+ + "', which doesn't extend RemoteService; this is either misconfiguration or a hack attempt");
+ }
} catch (ClassNotFoundException e) {
- throw new SerializationException("Unknown parameter " + i + " type '"
- + paramClassName + "'", e);
+ throw new IncompatibleRemoteServiceException(
+ "Could not locate requested interface '" + serviceIntfName
+ + "' in default classloader", e);
}
+
+ String serviceMethodName = streamReader.readString();
+
+ int paramCount = streamReader.readInt();
+ Class[] parameterTypes = new Class[paramCount];
+
+ for (int i = 0; i < parameterTypes.length; i++) {
+ String paramClassName = streamReader.readString();
+ try {
+ parameterTypes[i] = getClassFromSerializedName(paramClassName);
+ } catch (ClassNotFoundException e) {
+ throw new IncompatibleRemoteServiceException("Parameter " + i
+ + " of is of an unknown type '" + paramClassName + "'", e);
+ }
+ }
+
+ Method method = findInterfaceMethod(serviceIntf, serviceMethodName,
+ parameterTypes, true);
+
+ if (method == null) {
+ 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);
+
+ } catch (SerializationException ex) {
+ throw new IncompatibleRemoteServiceException(ex.getMessage(), ex);
}
-
- Method method = findInterfaceMethod(serviceIntf, serviceMethodName,
- parameterTypes, true);
-
- if (method == null) {
- throw new SecurityException(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);
}
/**
- * Returns a string that encodes an exception. It is an error to try to encode
- * an exception that is not in the method's list of checked exceptions.
+ * Returns a string that encodes an exception. If method is not
+ * <code>null</code>, it is an error if the exception is not in the
+ * method's list of checked exceptions.
*
- * @param serviceMethod the method that threw the exception
+ * @param serviceMethod the method that threw the exception, maybe
+ * <code>null</code>
* @param cause the {@link Throwable} that was thrown
- * @return a string that encodes the exception, if the exception was expected
+ * @return a string that encodes the exception
*
- * @throws NullPointerException if either the serviceMethod or the cause are
- * <code>null</code>
+ * @throws NullPointerException if the the cause is <code>null</code>
* @throws SerializationException if the result cannot be serialized
* @throws UnexpectedException if the result was an unexpected exception (a
* checked exception not declared in the serviceMethod's signature)
*/
public static String encodeResponseForFailure(Method serviceMethod,
Throwable cause) throws SerializationException {
- if (serviceMethod == null) {
- throw new NullPointerException("serviceMethod cannot be null");
- }
-
if (cause == null) {
throw new NullPointerException("cause cannot be null");
}
- if (RPC.isExpectedException(serviceMethod, cause)) {
- return encodeResponse(cause.getClass(), cause, true);
- } else {
+ if (serviceMethod != null && !RPC.isExpectedException(serviceMethod, cause)) {
throw new UnexpectedException("Service method '"
+ getSourceRepresentation(serviceMethod)
+ "' threw an unexpected exception: " + cause.toString(), cause);
}
+
+ return encodeResponse(cause.getClass(), cause, true);
}
/**
@@ -677,5 +679,6 @@
* Static classes have no constructability.
*/
private RPC() {
+ // Not instantiable
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
index ce65ebb..0986296 100644
--- a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.user.server.rpc;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import java.io.ByteArrayOutputStream;
@@ -206,19 +207,23 @@
* This is public so that it can be unit tested easily without HTTP.
*
* @param payload the UTF-8 request payload
- * @throws SerializationException if cannot deserialize the request, or cannot
- * serialize the response
- * @throws SecurityException if there is a problem locating or invoking the
- * service method on the destination object
+ * @return a string which encodes either the method's return, a checked
+ * exception thrown by the method, or an
+ * {@link IncompatibleRemoteServiceException}
+ * @throws SerializationException if we cannot serialize the response
* @throws UnexpectedException if the invocation throws a checked exception
* that is not declared in the service method's signature
* @throws RuntimeException if the service method throws an unchecked
* exception (the exception will be the one thrown by the service)
*/
public String processCall(String payload) throws SerializationException {
- RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass());
- return RPC.invokeAndEncodeResponse(this, rpcRequest.getMethod(),
- rpcRequest.getParameters());
+ try {
+ RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass());
+ return RPC.invokeAndEncodeResponse(this, rpcRequest.getMethod(),
+ rpcRequest.getParameters());
+ } catch (IncompatibleRemoteServiceException ex) {
+ return RPC.encodeResponseForFailure(null, ex);
+ }
}
/**
@@ -236,8 +241,8 @@
* Note that if the desired behavior is to both send the GENERIC_FAILURE_MSG
* response AND to rethrow the exception, then this method should first send
* the GENERIC_FAILURE_MSG response itself (using getThreadLocalResponse), and
- * then rethrow the exception. Rethrowing the exception will cause it to escape
- * into the servlet container.
+ * then rethrow the exception. Rethrowing the exception will cause it to
+ * escape into the servlet container.
*
* @param e the exception which was thrown
*/
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
index 510adc9..4339697 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
@@ -284,8 +284,8 @@
String clientTypeSignature = serializedInstRef.getSignature();
if (clientTypeSignature.length() == 0) {
if (shouldEnforceTypeVersioning()) {
- // TODO(mmendez): add a more descriptive error message here
- throw new SerializationException();
+ throw new SerializationException("Missing type signature for "
+ + instanceClass.getName());
}
return;
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTest.java b/user/test/com/google/gwt/user/server/rpc/RPCTest.java
index 08cb883..2b968b0 100644
--- a/user/test/com/google/gwt/user/server/rpc/RPCTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RPCTest.java
@@ -15,9 +15,9 @@
*/
package com.google.gwt.user.server.rpc;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.SerializableException;
-import com.google.gwt.user.client.rpc.SerializationException;
import junit.framework.TestCase;
@@ -84,8 +84,6 @@
fail("Expected NullPointerException");
} catch (NullPointerException e) {
// expected to get here
- } catch (SerializationException e) {
- fail(e.getMessage());
}
// Case 2
@@ -94,15 +92,14 @@
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected to get here
- } catch (SerializationException e) {
- fail(e.getMessage());
}
// Case 3
try {
RPCRequest request = RPC.decodeRequest(VALID_ENCODED_REQUEST);
- } catch (SerializationException e) {
- e.printStackTrace();
+ } catch (Throwable e) {
+ // not expected to get here
+ fail(e.getClass().getName() + " should not have been thrown by RPC.decodeRequest(String)");
}
}
@@ -132,8 +129,6 @@
fail("Expected NullPointerException");
} catch (NullPointerException e) {
// expected to get here
- } catch (SerializationException e) {
- fail(e.getMessage());
}
// Case 2
@@ -142,48 +137,30 @@
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected to get here
- } catch (SerializationException e) {
- fail(e.getMessage());
}
// Case 3
- try {
- RPCRequest request = RPC.decodeRequest(VALID_ENCODED_REQUEST, null);
- assertEquals(A.class.getMethod("method2", null), request.getMethod());
- assertTrue(request.getParameters().length == 0);
- } catch (SerializationException e) {
- e.printStackTrace();
- }
+ RPCRequest request;
+ request = RPC.decodeRequest(VALID_ENCODED_REQUEST, null);
+ assertEquals(A.class.getMethod("method2", null), request.getMethod());
+ assertTrue(request.getParameters().length == 0);
// Case 4
- try {
- RPCRequest request = RPC.decodeRequest(VALID_ENCODED_REQUEST, A.class);
- assertEquals(A.class.getMethod("method2", null), request.getMethod());
- assertTrue(request.getParameters().length == 0);
- } catch (SerializationException e) {
- e.printStackTrace();
- }
+ request = RPC.decodeRequest(VALID_ENCODED_REQUEST, A.class);
+ assertEquals(A.class.getMethod("method2", null), request.getMethod());
+ assertTrue(request.getParameters().length == 0);
// Case 5
try {
- RPCRequest request = RPC.decodeRequest(INVALID_INTERFACE_REQUEST, B.class);
- } catch (SecurityException e) {
+ request = RPC.decodeRequest(INVALID_INTERFACE_REQUEST, B.class);
+ } catch (IncompatibleRemoteServiceException e) {
// should get here
- System.out.println(e.getMessage());
- } catch (Throwable e) {
- e.printStackTrace();
- fail(e.getMessage());
}
-
// Case 6
try {
- RPCRequest request = RPC.decodeRequest(INVALID_METHOD_REQUEST, A.class);
- } catch (SecurityException e) {
+ request = RPC.decodeRequest(INVALID_METHOD_REQUEST, A.class);
+ } catch (IncompatibleRemoteServiceException e) {
// should get here
- System.out.println(e.getMessage());
- } catch (Throwable e) {
- e.printStackTrace();
- fail(e.getMessage());
}
}
@@ -203,9 +180,6 @@
// Case 1
try {
RPC.encodeResponseForFailure(null, new Throwable());
- fail("Expected NullPointerException");
- } catch (NullPointerException e) {
- // expected to get here
} catch (Throwable e) {
fail(e.getMessage());
}
@@ -234,7 +208,6 @@
fail("Expected UnexpectedException");
} catch (UnexpectedException e) {
// expected to get here
- System.out.println(e.getMessage());
} catch (Throwable e) {
fail(e.getMessage());
}
@@ -294,7 +267,6 @@
RPC.encodeResponseForSuccess(A_method2, new SerializableException());
} catch (IllegalArgumentException e) {
// expected to get here
- System.out.println(e.getMessage());
} catch (Throwable e) {
e.printStackTrace();
fail(e.getMessage());
@@ -347,7 +319,6 @@
}, A_method1, null);
} catch (SecurityException e) {
// expected to get here
- System.out.println(e.getMessage());
} catch (Throwable e) {
e.printStackTrace();
fail(e.getMessage());
@@ -369,7 +340,6 @@
}, A_method1, new Integer[] {new Integer(1)});
} catch (SecurityException e) {
// expected to get here
- System.out.println(e.getMessage());
} catch (Throwable e) {
e.printStackTrace();
fail(e.getMessage());
@@ -392,7 +362,6 @@
}, A_method1, null);
} catch (UnexpectedException e) {
// expected to get here
- System.out.println(e.getMessage());
} catch (Throwable e) {
e.printStackTrace();
fail(e.getMessage());