An async RPC method can now be declared to return a RequestBuilder, in which case a fully-baked RequestBuilder is returned, but no send is performed. The caller can adjust the RequestBuilder and then call "send" to actually perform the call.
Suggested by: scottb, mmendez
Patch by: bobv
Review by: scottb, bruce
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2329 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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
index d05eaec..5e32c56 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java
@@ -228,33 +228,91 @@
*
* @return a {@link Request} object that can be used to track the request
*/
- @SuppressWarnings("unused")
protected <T> Request doInvoke(ResponseReader responseReader,
String methodName, int invocationCount, String requestData,
AsyncCallback<T> callback) {
+ RequestBuilder rb = doPrepareRequestBuilderImpl(responseReader, methodName,
+ invocationCount, requestData, callback);
+
+ try {
+ return rb.send();
+ } catch (RequestException ex) {
+ InvocationException iex = new InvocationException(
+ "Unable to initiate the asynchronous service invocation -- check the network connection",
+ ex);
+ callback.onFailure(iex);
+ } finally {
+ if (RemoteServiceProxy.isStatsAvailable()
+ && RemoteServiceProxy.stats(methodName + ":" + invocationCount
+ + ":requestSent", RemoteServiceProxy.bytesStat(methodName,
+ invocationCount, requestData.length()))) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Configures a RequestBuilder to send an RPC request when the RequestBuilder
+ * is intended to be returned through the asynchronous proxy interface.
+ *
+ * @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 RequestBuilder object that is ready to have its
+ * {@link RequestBuilder#send()} method invoked.
+ */
+ protected <T> RequestBuilder doPrepareRequestBuilder(
+ ResponseReader responseReader, String methodName, int invocationCount,
+ String requestData, AsyncCallback<T> callback) {
+
+ RequestBuilder rb = doPrepareRequestBuilderImpl(responseReader, methodName,
+ invocationCount, requestData, callback);
+
+ // We'll record when the request was configured...
+ if (RemoteServiceProxy.isStatsAvailable()
+ && RemoteServiceProxy.stats(methodName + ":" + invocationCount
+ + ":requestPrepared", RemoteServiceProxy.bytesStat(methodName,
+ invocationCount, requestData.length()))) {
+ }
+
+ return rb;
+ }
+
+ /**
+ * Configures a RequestBuilder to send an RPC request.
+ *
+ * @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 RequestBuilder object that is ready to have its
+ * {@link RequestBuilder#send()} method invoked.
+ */
+ private <T> RequestBuilder doPrepareRequestBuilderImpl(
+ ResponseReader responseReader, String methodName, int invocationCount,
+ String requestData, AsyncCallback<T> callback) {
+
if (getServiceEntryPoint() == null) {
throw new NoServiceEntryPointSpecifiedException();
}
RequestCallbackAdapter<T> responseHandler = new RequestCallbackAdapter<T>(
this, methodName, invocationCount, callback, responseReader);
+
RequestBuilder rb = new RequestBuilder(RequestBuilder.POST,
getServiceEntryPoint());
- rb.setHeader("Content-Type", "text/x-gwt-rpc; charset=utf-8");
- 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);
- } finally {
- boolean toss = isStatsAvailable()
- && stats(methodName + ":" + invocationCount + ":requestSent",
- bytesStat(methodName, invocationCount, requestData.length()));
- }
- return null;
+ rb.setHeader("Content-Type", "text/x-gwt-rpc; charset=utf-8");
+ rb.setCallback(responseHandler);
+ rb.setRequestData(requestData);
+ return rb;
}
}
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 8d35f8a..c70fd15 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -28,6 +28,8 @@
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.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader;
@@ -312,12 +314,24 @@
+ getProxySimpleName() + "." + syncMethod.getName()
+ "\", getRequestId()));");
- if (asyncReturnType != JPrimitiveType.VOID) {
- w.print("return ");
+ /*
+ * Depending on the return type for the async method, return a
+ * RequestBuilder, a Request, or nothing at all.
+ */
+ if (asyncReturnType == JPrimitiveType.VOID) {
+ w.print("doInvoke(");
+ } else if (asyncReturnType.getQualifiedSourceName().equals(
+ RequestBuilder.class.getName())) {
+ w.print("return doPrepareRequestBuilder(");
+ } else if (asyncReturnType.getQualifiedSourceName().equals(
+ Request.class.getName())) {
+ w.print("return doInvoke(");
+ } else {
+ // This method should have been caught by RemoteServiceAsyncValidator
+ throw new RuntimeException("Unhandled return type "
+ + asyncReturnType.getQualifiedSourceName());
}
- // Call the doInvoke method to actually send the request.
- w.print("doInvoke(");
JType returnType = syncMethod.getReturnType();
w.print("ResponseReader." + getResponseReaderFor(returnType).name());
w.print(", \"" + getProxySimpleName() + "." + syncMethod.getName()
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 2fa9811..655ee0e 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/RemoteServiceAsyncValidator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/RemoteServiceAsyncValidator.java
@@ -26,6 +26,7 @@
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.http.client.RequestBuilder;
import com.google.gwt.user.client.rpc.AsyncCallback;
import java.util.HashMap;
@@ -145,6 +146,11 @@
private final JClassType asyncCallbackClass;
/**
+ * {@link JClassType} for the {@link RequestBuilder} class.
+ */
+ private final JClassType requestBuilderType;
+
+ /**
* {@link JClassType} for the {@link Request} class.
*/
private final JClassType requestType;
@@ -154,6 +160,7 @@
try {
asyncCallbackClass = typeOracle.getType(AsyncCallback.class.getName());
requestType = typeOracle.getType(Request.class.getCanonicalName());
+ requestBuilderType = typeOracle.getType(RequestBuilder.class.getCanonicalName());
} catch (NotFoundException e) {
logger.log(TreeLogger.ERROR, null, e);
throw new UnableToCompleteException();
@@ -212,12 +219,14 @@
// 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) {
+ if (returnType != JPrimitiveType.VOID && returnType != requestType
+ && returnType != requestBuilderType) {
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);
+ + Request.class.getCanonicalName() + "' or '"
+ + RequestBuilder.class.getCanonicalName() + "'", null);
failed = true;
} else {
syncMethodToAsyncMethodMap.put(syncMethod, asyncMethod);
@@ -231,4 +240,4 @@
return syncMethodToAsyncMethodMap;
}
-}
\ No newline at end of file
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java b/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java
index 05bec77..6058f4c 100644
--- a/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java
+++ b/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java
@@ -17,6 +17,8 @@
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;
/**
@@ -33,8 +35,7 @@
* </p>
*/
public class RemoteServiceServletTest extends GWTTestCase {
- private static final int TEST_DELAY = Integer.MAX_VALUE;
- private Request req;
+ private static final int TEST_DELAY = 10000;
private static RemoteServiceServletTestServiceAsync getAsyncService() {
RemoteServiceServletTestServiceAsync service = (RemoteServiceServletTestServiceAsync) GWT.create(RemoteServiceServletTestService.class);
@@ -45,22 +46,46 @@
return service;
}
+ private Request req;
+
public String getModuleName() {
return "com.google.gwt.user.RPCSuite";
}
+ public void testManualSend() throws RequestException {
+ RemoteServiceServletTestServiceAsync service = getAsyncService();
+
+ delayTestFinish(TEST_DELAY);
+
+ RequestBuilder builder = service.testExpectCustomHeader(new AsyncCallback<Void>() {
+
+ public void onFailure(Throwable caught) {
+ TestSetValidator.rethrowException(caught);
+ }
+
+ public void onSuccess(Void result) {
+ assertTrue(!req.isPending());
+ finishTest();
+ }
+ });
+
+ builder.setHeader("X-Custom-Header", "true");
+ req = builder.send();
+ assertTrue(req.isPending());
+ }
+
public void testServiceInterfaceLocation() {
RemoteServiceServletTestServiceAsync service = getAsyncService();
delayTestFinish(TEST_DELAY);
- req = service.test(new AsyncCallback<Object>() {
+ req = service.test(new AsyncCallback<Void>() {
public void onFailure(Throwable caught) {
TestSetValidator.rethrowException(caught);
}
- public void onSuccess(Object result) {
+ public void onSuccess(Void result) {
assertTrue(!req.isPending());
finishTest();
}
diff --git a/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestService.java b/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestService.java
index 093241e..e55e36f 100644
--- a/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestService.java
+++ b/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestService.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -20,4 +20,6 @@
*/
public interface RemoteServiceServletTestService extends RemoteService {
void test();
+
+ void testExpectCustomHeader();
}
diff --git a/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestServiceAsync.java
index 0576df3..154e091 100644
--- a/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestServiceAsync.java
+++ b/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestServiceAsync.java
@@ -16,10 +16,13 @@
package com.google.gwt.user.client.rpc;
import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
/**
* TODO: document me.
*/
public interface RemoteServiceServletTestServiceAsync {
- Request test(AsyncCallback callback);
+ Request test(AsyncCallback<Void> callback);
+
+ RequestBuilder testExpectCustomHeader(AsyncCallback<Void> callback);
}
diff --git a/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTestServiceImplBase.java b/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTestServiceImplBase.java
index 384eb27..39e1934 100644
--- a/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTestServiceImplBase.java
+++ b/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTestServiceImplBase.java
@@ -17,6 +17,8 @@
import com.google.gwt.user.client.rpc.RemoteServiceServletTestService;
+import javax.servlet.http.HttpServletRequest;
+
/**
* TODO: document me.
*/
@@ -25,4 +27,11 @@
public void test() {
}
+
+ public void testExpectCustomHeader() {
+ HttpServletRequest req = getThreadLocalRequest();
+ if (!Boolean.parseBoolean(req.getHeader("X-Custom-Header"))) {
+ throw new RuntimeException("Missing header");
+ }
+ }
}