Cherry-pick merge of r5023.



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5024 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/http/client/Request.java b/user/src/com/google/gwt/http/client/Request.java
index fd0ea9d..70c13c4 100644
--- a/user/src/com/google/gwt/http/client/Request.java
+++ b/user/src/com/google/gwt/http/client/Request.java
@@ -148,6 +148,16 @@
    * and set this field to null.
    */
   private XMLHttpRequest xmlHttpRequest;
+  
+  /**
+   * Only used for building a
+   * {@link com.google.gwt.user.client.rpc.impl.FailedRequest}.
+   */
+  protected Request() {
+    timeoutMillis = 0;
+    xmlHttpRequest = null;
+    timer = null;
+  }
 
   /**
    * Constructs an instance of the Request object.
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/FailedRequest.java b/user/src/com/google/gwt/user/client/rpc/impl/FailedRequest.java
new file mode 100644
index 0000000..de15658
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/impl/FailedRequest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2009 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;
+
+/**
+ * A {@link Request} that is already canceled at the moment it is created.
+ */
+public class FailedRequest extends Request {
+  public FailedRequest() {
+    super();
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/FailingRequestBuilder.java b/user/src/com/google/gwt/user/client/rpc/impl/FailingRequestBuilder.java
new file mode 100644
index 0000000..9227677
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/impl/FailingRequestBuilder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009 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.RequestCallback;
+import com.google.gwt.http.client.RequestException;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/**
+ * A {@link RequestBuilder} that always immediately fails.
+ */
+public class FailingRequestBuilder extends RequestBuilder {
+  private final Throwable cause;
+  private final AsyncCallback<?> rpcCallback;
+
+  public FailingRequestBuilder(Throwable cause, AsyncCallback<?> rpcCallback) {
+    super(GET, "(bogus)");
+    this.cause = cause;
+    this.rpcCallback = rpcCallback;
+  }
+
+  @Override
+  public Request send() throws RequestException {
+    rpcCallback.onFailure(cause);
+    return new FailedRequest();
+  }
+
+  @Override
+  public Request sendRequest(String requestData, RequestCallback callback)
+      throws RequestException {
+    // This method would not normally be called
+    throw new RequestException(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 0449c7e..273ef04 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -39,6 +39,8 @@
 import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
 import com.google.gwt.user.client.rpc.SerializationException;
 import com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter;
+import com.google.gwt.user.client.rpc.impl.FailedRequest;
+import com.google.gwt.user.client.rpc.impl.FailingRequestBuilder;
 import com.google.gwt.user.client.rpc.impl.RemoteServiceProxy;
 import com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.ResponseReader;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
@@ -485,7 +487,25 @@
       String exceptionName = nameFactory.createName("ex");
       w.println(exceptionName + ") {");
       w.indent();
-      w.println(callbackName + ".onFailure(" + exceptionName + ");");
+      if (!asyncReturnType.getQualifiedSourceName().equals(
+          RequestBuilder.class.getName())) {
+        /*
+         * If the method returns void or Request, signal the serialization error
+         * immediately. If the method returns RequestBuilder, the error will be
+         * signaled whenever RequestBuilder.send() is invoked.
+         */
+        w.println(callbackName + ".onFailure(" + exceptionName + ");");
+      }
+      if (asyncReturnType.getQualifiedSourceName().equals(
+          RequestBuilder.class.getName())) {
+        w.println("return new " + FailingRequestBuilder.class.getName() + "("
+            + exceptionName + ", " + callbackName + ");");
+      } else if (asyncReturnType.getQualifiedSourceName().equals(
+          Request.class.getName())) {
+        w.println("return new " + FailedRequest.class.getName() + "();");
+      } else {
+        assert asyncReturnType == JPrimitiveType.VOID;
+      }
       w.outdent();
       w.println("}");
     }
diff --git a/user/test/com/google/gwt/user/RPCSuite.gwt.xml b/user/test/com/google/gwt/user/RPCSuite.gwt.xml
index 53c0c51..2360e11 100644
--- a/user/test/com/google/gwt/user/RPCSuite.gwt.xml
+++ b/user/test/com/google/gwt/user/RPCSuite.gwt.xml
@@ -18,7 +18,7 @@
   <define-property name='rpc.enforceTypeVersioning' values="true, false" />
   <set-property name='rpc.enforceTypeVersioning' value='true' />
 
-  <servlet path='/echo' class='com.google.gwt.user.server.rpc.EchoServiceImpl' />
+  <servlet path='/echo' class='com.google.gwt.user.server.rpc.MixedSerializableEchoServiceImpl' />
   <servlet path='/collections'
     class='com.google.gwt.user.server.rpc.CollectionsTestServiceImpl' />
   <servlet path='/customfieldserializers'
diff --git a/user/test/com/google/gwt/user/RPCSuite.java b/user/test/com/google/gwt/user/RPCSuite.java
index b32b50f..4ded336 100644
--- a/user/test/com/google/gwt/user/RPCSuite.java
+++ b/user/test/com/google/gwt/user/RPCSuite.java
@@ -23,10 +23,13 @@
 import com.google.gwt.user.client.rpc.CustomFieldSerializerTestWithTypeObfuscation;
 import com.google.gwt.user.client.rpc.EnumsTest;
 import com.google.gwt.user.client.rpc.EnumsTestWithTypeObfuscation;
+import com.google.gwt.user.client.rpc.FailedRequestTest;
+import com.google.gwt.user.client.rpc.FailingRequestBuilderTest;
 import com.google.gwt.user.client.rpc.InheritanceTest;
 import com.google.gwt.user.client.rpc.InheritanceTestWithTypeObfuscation;
 import com.google.gwt.user.client.rpc.ObjectGraphTest;
 import com.google.gwt.user.client.rpc.ObjectGraphTestWithTypeObfuscation;
+import com.google.gwt.user.client.rpc.RunTimeSerializationErrorsTest;
 import com.google.gwt.user.client.rpc.UnicodeEscapingTest;
 import com.google.gwt.user.client.rpc.UnicodeEscapingTestWithTypeObfuscation;
 import com.google.gwt.user.client.rpc.ValueTypesTest;
@@ -70,6 +73,8 @@
     suite.addTestSuite(SerializationPolicyLoaderTest.class);
     suite.addTestSuite(RPCServletUtilsTest.class);
     suite.addTestSuite(RPCRequestTest.class);
+    suite.addTestSuite(FailedRequestTest.class);
+    suite.addTestSuite(FailingRequestBuilderTest.class);
 
     // GWTTestCases
     suite.addTestSuite(ValueTypesTest.class);
@@ -80,6 +85,7 @@
     suite.addTestSuite(ObjectGraphTest.class);
     suite.addTestSuite(com.google.gwt.user.client.rpc.RemoteServiceServletTest.class);
     suite.addTestSuite(UnicodeEscapingTest.class);
+    suite.addTestSuite(RunTimeSerializationErrorsTest.class);
 
     // This test turns on the type-elision feature of RPC
     suite.addTestSuite(ValueTypesTestWithTypeObfuscation.class);
diff --git a/user/test/com/google/gwt/user/client/rpc/FailedRequestTest.java b/user/test/com/google/gwt/user/client/rpc/FailedRequestTest.java
new file mode 100644
index 0000000..2691c34
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/FailedRequestTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2009 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;
+
+import com.google.gwt.http.client.Request;
+import com.google.gwt.user.client.rpc.impl.FailedRequest;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the {@link com.google.gwt.user.client.rpc.impl.FailedRequest} class.
+ */
+public class FailedRequestTest extends TestCase {
+  public void testBasics() {
+    Request failedRequest = new FailedRequest();
+    assertFalse(failedRequest.isPending());
+    failedRequest.cancel();
+    assertFalse(failedRequest.isPending());
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/FailingRequestBuilderTest.java b/user/test/com/google/gwt/user/client/rpc/FailingRequestBuilderTest.java
new file mode 100644
index 0000000..effa813
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/FailingRequestBuilderTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009 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;
+
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.RequestException;
+import com.google.gwt.user.client.rpc.impl.FailingRequestBuilder;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the {@link FailingRequestBuilder} class.
+ */
+public class FailingRequestBuilderTest extends TestCase {
+  public void testBasics() throws RequestException {
+    final boolean[] callbackCalled = new boolean[] {false};
+
+    RequestBuilder rb = new FailingRequestBuilder(new SerializationException(),
+        new AsyncCallback<Void>() {
+          public void onFailure(Throwable caught) {
+            assertFalse(callbackCalled[0]);
+            callbackCalled[0] = true;
+          }
+
+          public void onSuccess(Void result) {
+            fail("expected this to fail");
+          }
+        });
+
+    rb.send();
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/MixedSerializable.java b/user/test/com/google/gwt/user/client/rpc/MixedSerializable.java
new file mode 100644
index 0000000..b7c016f
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/MixedSerializable.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2009 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;
+
+import java.io.Serializable;
+
+/**
+ * A class with both serializable and non-serializable subclasses.
+ */
+public abstract class MixedSerializable {
+  /**
+   * Subclass that is not serializable.
+   */
+  public static class NonSerializableSub extends MixedSerializable {
+  }
+
+  /**
+   * Subclass that is serializable.
+   */
+  public static class SerializableSub extends MixedSerializable implements
+      Serializable {
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/MixedSerializableEchoService.java b/user/test/com/google/gwt/user/client/rpc/MixedSerializableEchoService.java
new file mode 100644
index 0000000..0193d9d
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/MixedSerializableEchoService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2009 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;
+
+/**
+ * A service whose methods send types across the wire that have both
+ * serializable and unserializable subclasses.
+ */
+public interface MixedSerializableEchoService extends RemoteService {
+  MixedSerializable echoVoid(MixedSerializable mixed);
+
+  MixedSerializable echoRequest(MixedSerializable mixed);
+
+  MixedSerializable echoRequestBuilder(MixedSerializable mixed);
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/MixedSerializableEchoServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/MixedSerializableEchoServiceAsync.java
new file mode 100644
index 0000000..287ce59
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/MixedSerializableEchoServiceAsync.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2009 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;
+
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
+
+/**
+ * The async interface for {@link MixedSerializableEchoService}.
+ */
+public interface MixedSerializableEchoServiceAsync {
+  void echoVoid(MixedSerializable mixed,
+      AsyncCallback<MixedSerializable> callback);
+
+  Request echoRequest(MixedSerializable mixed,
+      AsyncCallback<MixedSerializable> callback);
+
+  RequestBuilder echoRequestBuilder(MixedSerializable mixed,
+      AsyncCallback<MixedSerializable> callback);
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/RunTimeSerializationErrorsTest.java b/user/test/com/google/gwt/user/client/rpc/RunTimeSerializationErrorsTest.java
new file mode 100644
index 0000000..33e6adb
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/RunTimeSerializationErrorsTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2009 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;
+
+import com.google.gwt.core.client.GWT;
+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.junit.client.GWTTestCase;
+
+/**
+ * Tests run-time serialization errors for GWT RPC.
+ */
+public class RunTimeSerializationErrorsTest extends GWTTestCase {
+  private static final int TIMEOUT = 20000;
+
+  public static MixedSerializableEchoServiceAsync getService() {
+    MixedSerializableEchoServiceAsync service = GWT.create(MixedSerializableEchoService.class);
+
+    ((ServiceDefTarget) service).setServiceEntryPoint(GWT.getModuleBaseURL()
+        + "echo");
+
+    return service;
+  }
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.RPCSuite";
+  }
+
+  public void testBadSerialization1() {
+    delayTestFinish(TIMEOUT);
+    getService().echoVoid(new MixedSerializable.NonSerializableSub(),
+        new AsyncCallback<MixedSerializable>() {
+          public void onFailure(Throwable caught) {
+            finishTest();
+          }
+
+          public void onSuccess(MixedSerializable result) {
+            fail("RPC request should have failed");
+          }
+        });
+  }
+
+  public void testBadSerialization2() {
+    final boolean[] callbackFired = new boolean[] {false};
+
+    Request req = getService().echoRequest(
+        new MixedSerializable.NonSerializableSub(),
+        new AsyncCallback<MixedSerializable>() {
+          public void onFailure(Throwable caught) {
+            callbackFired[0] = true;
+          }
+
+          public void onSuccess(MixedSerializable result) {
+            fail("RPC request should have failed");
+          }
+        });
+
+    assertTrue(callbackFired[0]); // should have happened synchronously
+    assertFalse(req.isPending());
+    req.cancel();
+  }
+
+  public void testBadSerialization3() throws RequestException {
+    final boolean[] callbackFired = new boolean[] {false};
+
+    RequestBuilder rb = getService().echoRequestBuilder(
+        new MixedSerializable.NonSerializableSub(),
+        new AsyncCallback<MixedSerializable>() {
+          public void onFailure(Throwable caught) {
+            assertFalse("callback fired twice", callbackFired[0]);
+            callbackFired[0] = true;
+          }
+
+          public void onSuccess(MixedSerializable result) {
+            fail("RPC request should have failed");
+          }
+        });
+
+    assertFalse(callbackFired[0]); // should fail when send() is called
+    rb.send();
+    assertTrue(callbackFired[0]); // should have happened now
+  }
+
+  public void testGoodSerialization1() {
+    delayTestFinish(TIMEOUT);
+    getService().echoVoid(new MixedSerializable.SerializableSub(),
+        new AsyncCallback<MixedSerializable>() {
+          public void onFailure(Throwable caught) {
+            fail(caught.toString());
+          }
+
+          public void onSuccess(MixedSerializable result) {
+            finishTest();
+          }
+        });
+  }
+
+  public void testGoodSerialization2() {
+    delayTestFinish(TIMEOUT);
+    getService().echoRequest(new MixedSerializable.SerializableSub(),
+        new AsyncCallback<MixedSerializable>() {
+          public void onFailure(Throwable caught) {
+            fail(caught.toString());
+          }
+
+          public void onSuccess(MixedSerializable result) {
+            finishTest();
+          }
+        });
+  }
+
+  public void testGoodSerialization3() {
+    delayTestFinish(TIMEOUT);
+    getService().echoVoid(new MixedSerializable.SerializableSub(),
+        new AsyncCallback<MixedSerializable>() {
+          public void onFailure(Throwable caught) {
+            fail(caught.toString());
+          }
+
+          public void onSuccess(MixedSerializable result) {
+            finishTest();
+          }
+        });
+  }
+}
\ No newline at end of file
diff --git a/user/test/com/google/gwt/user/server/rpc/MixedSerializableEchoServiceImpl.java b/user/test/com/google/gwt/user/server/rpc/MixedSerializableEchoServiceImpl.java
new file mode 100644
index 0000000..18de78c
--- /dev/null
+++ b/user/test/com/google/gwt/user/server/rpc/MixedSerializableEchoServiceImpl.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2009 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.server.rpc;
+
+import com.google.gwt.user.client.rpc.MixedSerializable;
+import com.google.gwt.user.client.rpc.MixedSerializableEchoService;
+
+/**
+ * Servlet used by the
+ * {@link com.google.gwt.user.client.rpc.RunTimeSerializationErrorsTest}.
+ */
+public class MixedSerializableEchoServiceImpl extends RemoteServiceServlet
+    implements MixedSerializableEchoService {
+  public MixedSerializable echoVoid(MixedSerializable mixed) {
+    return mixed;
+  }
+
+  public MixedSerializable echoRequest(MixedSerializable mixed) {
+    return mixed;
+  }
+
+  public MixedSerializable echoRequestBuilder(MixedSerializable mixed) {
+    return mixed;
+  }
+}