Author: [email protected] Date: Wed Mar 18 17:59:19 2009 New Revision: 5045 Added: trunk/user/src/com/google/gwt/user/client/rpc/RpcRequestBuilder.java (contents, props changed) Modified: trunk/user/src/com/google/gwt/user/client/rpc/ServiceDefTarget.java trunk/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java trunk/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java trunk/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java
Log: Extracts a configuration API for use by RemoteServiceProxy. Patch by: bobv Review by: rjrjr Added: trunk/user/src/com/google/gwt/user/client/rpc/RpcRequestBuilder.java ============================================================================== --- (empty file) +++ trunk/user/src/com/google/gwt/user/client/rpc/RpcRequestBuilder.java Wed Mar 18 17:59:19 2009 @@ -0,0 +1,212 @@ +/* + * 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.RequestBuilder; +import com.google.gwt.http.client.RequestCallback; + +/** + * This class encapsulates the logic necessary to configure a RequestBuilder for + * use with an RPC proxy object. Users who wish to alter the specifics of the + * HTTP requests issued by RPC proxy objects may override the protected + * <code>doXyz</code> methods and pass an instance of the subclass to + * {...@link ServiceDefTarget#setRpcRequestBuilder}. + */ +public class RpcRequestBuilder { + /** + * Used by {...@link #doSetContentType}. + */ + public static final String CONTENT_TYPE_HEADER = "Content-Type"; + + /** + * Used by {...@link #doFinish}. + */ + /* + * NB: Keep in sync with RemoteServiceServlet. + */ + public static final String STRONG_NAME_HEADER = "X-GWT-Permutation"; + + /** + * Not exposed directly to the subclass. + */ + private RequestBuilder builder; + + /** + * Initialize the RpcRequestBuilder. This method must be called before any of + * the other methods in this class may be called. Calling <code>create</code> + * before calling {...@link #finish()} will reset the state of the + * RpcRequestBuilder. + * <p> + * This method delegates to {...@link #doCreate} to instantiate the + * RequestBuilder. + * + * @param serviceEntryPoint The URL entry point + * @return <code>this</code> + * @see ServiceDefTarget#setServiceEntryPoint(String) + */ + public final RpcRequestBuilder create(String serviceEntryPoint) { + builder = doCreate(serviceEntryPoint); + assert builder != null : "doCreate failed to return a RequestBuilder"; + return this; + } + + /** + * This method must be called to return the RequestBuilder that the RPC + * request will be made with. + * <p> + * This method will call {...@link #doFinish} before returning the current + * RequestBuilder. + */ + public final RequestBuilder finish() { + try { + assert builder != null : "Call create() first"; + doFinish(builder); + return builder; + } finally { + builder = null; + } + } + + /** + * Sets the RequestCallback to be used by the RequestBuilder. Delegates to + * {...@link #doSetCallback}. + * + * @param callback the RequestCallback to be used by the RequestBuilder + * @return <code>this</code> + */ + public final RpcRequestBuilder setCallback(RequestCallback callback) { + assert builder != null : "Call create() first"; + doSetCallback(builder, callback); + return this; + } + + /** + * Sets the MIME content type to be used by the RequestBuilder. Delegates to + * {...@link #doSetContentType}. + * + * @param contentType the MIME content type to be used in the request + * @return <code>this</code> + */ + public final RpcRequestBuilder setContentType(String contentType) { + assert builder != null : "Call create() first"; + doSetContentType(builder, contentType); + return this; + } + + /** + * Sets the request data to be sent in the request. Delegates to + * {...@link #doSetRequestData}. + * + * @param data the data to send + * @return <code>this</code> + */ + public final RpcRequestBuilder setRequestData(String data) { + assert builder != null : "Call create() first"; + doSetRequestData(builder, data); + return this; + } + + /** + * Sets the request id of the request. Delegates to {...@link #doSetRequestId}. + * + * @param id the issue number of the request + * @return <code>this</code> + */ + public final RpcRequestBuilder setRequestId(int id) { + assert builder != null : "Call create() first"; + doSetRequestId(builder, id); + return this; + } + + /** + * Called by {...@link #create} to instantiate the RequestBuilder object. + * <p> + * The default implementation creates a <code>POST</code> RequestBuilder with + * the given entry point. + * + * @param serviceEntryPoint the URL to which the request should be issued + * @return the RequestBuilder that should be ultimately passed to the + * RpcRequestBuilder's caller. + */ + protected RequestBuilder doCreate(String serviceEntryPoint) { + return new RequestBuilder(RequestBuilder.POST, serviceEntryPoint); + } + + /** + * Called by {...@link #finish()} prior to returning the RequestBuilder to the + * caller. + * <p> + * The default implementation sets the {...@value #STRONG_NAME_HEADER} header to + * the value returned by {...@link GWT#getPermutationStrongName()}. + * + * @param rb The RequestBuilder that is currently being configured + */ + protected void doFinish(RequestBuilder rb) { + rb.setHeader(STRONG_NAME_HEADER, GWT.getPermutationStrongName()); + } + + /** + * Called by {...@link #setCallback}. + * <p> + * The default implementation calls + * {...@link RequestBuilder#setCallback(RequestCallback)}. + * + * @param rb the RequestBuilder that is currently being configured + * @param callback the user-provided callback + */ + protected void doSetCallback(RequestBuilder rb, RequestCallback callback) { + rb.setCallback(callback); + } + + /** + * Called by {...@link #setContentType}. + * <p> + * The default implementation sets the {...@value #CONTENT_TYPE_HEADER} header to + * the value specified by <code>contentType</code> by calling + * {...@link RequestBuilder#setHeader(String, String)}. + * + * @param rb the RequestBuilder that is currently being configured + * @param contentType the desired MIME type of the request's contents + */ + protected void doSetContentType(RequestBuilder rb, String contentType) { + rb.setHeader(CONTENT_TYPE_HEADER, contentType); + } + + /** + * Called by {...@link #setRequestData}. + * <p> + * The default implementation invokes + * {...@link RequestBuilder#setRequestData(String)}. + * + * @param rb the RequestBuilder that is currently being configured + * @param data the data to send + */ + protected void doSetRequestData(RequestBuilder rb, String data) { + rb.setRequestData(data); + } + + /** + * Called by {...@link #setRequestId}. + * <p> + * The default implementation is a no-op. + * + * @param rb the RequestBuilder that is currently being configured + * @param id the request's issue id + */ + protected void doSetRequestId(RequestBuilder rb, int id) { + } +} Modified: trunk/user/src/com/google/gwt/user/client/rpc/ServiceDefTarget.java ============================================================================== --- trunk/user/src/com/google/gwt/user/client/rpc/ServiceDefTarget.java (original) +++ trunk/user/src/com/google/gwt/user/client/rpc/ServiceDefTarget.java Wed Mar 18 17:59:19 2009 @@ -18,8 +18,8 @@ /** * An interface implemented by client-side RPC proxy objects. Cast the object * returned from {...@link com.google.gwt.core.client.GWT#create(Class)} on a - * {...@link RemoteService} should be cast to this interface to initialize the - * target URL for the remote service. + * {...@link RemoteService} to this interface to initialize the target URL for the + * remote service. */ public interface ServiceDefTarget { @@ -41,6 +41,14 @@ * @return the last value passed to {...@link #setServiceEntryPoint(String)} */ String getServiceEntryPoint(); + + /** + * Sets the RpcRequestBuilder that should be used by the service + * implementation. This method can be called if customized request behavior is + * desired. Calling this method with a null value will reset any custom + * behavior to the default implementation. + */ + void setRpcRequestBuilder(RpcRequestBuilder builder); /** * Sets the URL of a service implementation. Modified: trunk/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java ============================================================================== --- trunk/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java (original) +++ trunk/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java Wed Mar 18 17:59:19 2009 @@ -15,13 +15,13 @@ */ package com.google.gwt.user.client.rpc.impl; -import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; 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.RpcRequestBuilder; import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.client.rpc.SerializationStreamFactory; import com.google.gwt.user.client.rpc.ServiceDefTarget; @@ -37,9 +37,9 @@ ServiceDefTarget { /** - * NB: Keep in sync with RemoteServiceServlet. + * The content type to be used in HTTP requests. */ - private static final String STRONG_NAME_HEADER = "X-GWT-Permutation"; + private static final String RPC_CONTENT_TYPE = "text/x-gwt-rpc; charset=utf-8"; /** * A global id to track any given request. @@ -56,9 +56,6 @@ /** * Indicates if RPC statistics should be gathered. */ - /** - * Indicates if RPC statistics should be gathered. - */ public static native boolean isStatsAvailable() /*-{ return !!$stats; }-*/; @@ -71,7 +68,8 @@ return $stats(data); }-*/; - public static native JavaScriptObject timeStat(String method, int count, String eventType) /*-{ + public static native JavaScriptObject timeStat(String method, int count, + String eventType) /*-{ return { moduleName: @com.google.gwt.core.client.GWT::getModuleName()(), subSystem: 'rpc', @@ -86,17 +84,21 @@ return requestId++; } + /** + * @deprecated Use {...@link RpcRequestBuilder} instead. + */ + @Deprecated protected static int getRequestId() { return requestId; } /** - * Return <code>true</code> if the encoded response contains a value - * returned by the method invocation. + * 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 + * @return <code>true</code> if the encoded response contains a value returned + * by the method invocation */ static boolean isReturnValue(String encodedResponse) { return encodedResponse.startsWith("//OK"); @@ -135,11 +137,13 @@ private final String moduleBaseURL; /** - * URL of the - * {...@link com.google.gwt.user.client.rpc.RemoteService RemoteService}. + * URL of the {...@link com.google.gwt.user.client.rpc.RemoteService + * RemoteService}. */ private String remoteServiceURL; + private RpcRequestBuilder rpcRequestBuilder; + /** * The name of the serialization policy file specified during construction. */ @@ -169,13 +173,12 @@ } /** - * Returns a - * {...@link com.google.gwt.user.client.rpc.SerializationStreamReader SerializationStreamReader} - * that is ready for reading. + * 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 + * @return {...@link com.google.gwt.user.client.rpc.SerializationStreamReader + * SerializationStreamReader} that is ready for reading * @throws SerializationException */ public ClientSerializationStreamReader createStreamReader(String encoded) @@ -187,14 +190,14 @@ } /** - * 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. + * 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 + * @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 @@ -213,6 +216,10 @@ return remoteServiceURL; } + public void setRpcRequestBuilder(RpcRequestBuilder builder) { + this.rpcRequestBuilder = builder; + } + /** * @see ServiceDefTarget#setServiceEntryPoint(String) */ @@ -221,7 +228,8 @@ } /** - * Performs a remote service method invocation. + * Performs a remote service method invocation. This method is called by + * generated proxy classes. * * @param <T> return type for the AsyncCallback * @param responseReader instance used to read the return value of the @@ -249,7 +257,7 @@ } finally { if (RemoteServiceProxy.isStatsAvailable() && RemoteServiceProxy.stats(RemoteServiceProxy.bytesStat(methodName, - invocationCount, requestData.length(), "requestSent"))) { + invocationCount, requestData.length(), "requestSent"))) { } } return null; @@ -303,13 +311,19 @@ RequestCallbackAdapter<T> responseHandler = new RequestCallbackAdapter<T>( this, methodName, invocationCount, callback, responseReader); - RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, - getServiceEntryPoint()); + ensureRpcRequestBuilder(); - rb.setHeader("Content-Type", "text/x-gwt-rpc; charset=utf-8"); - rb.setHeader(STRONG_NAME_HEADER, GWT.getPermutationStrongName()); - rb.setCallback(responseHandler); - rb.setRequestData(requestData); - return rb; + rpcRequestBuilder.create(getServiceEntryPoint()); + rpcRequestBuilder.setCallback(responseHandler); + rpcRequestBuilder.setContentType(RPC_CONTENT_TYPE); + rpcRequestBuilder.setRequestData(requestData); + rpcRequestBuilder.setRequestId(invocationCount); + return rpcRequestBuilder.finish(); + } + + private void ensureRpcRequestBuilder() { + if (rpcRequestBuilder == null) { + rpcRequestBuilder = new RpcRequestBuilder(); + } } } Modified: trunk/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java ============================================================================== --- trunk/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java (original) +++ trunk/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java Wed Mar 18 17:59:19 2009 @@ -41,9 +41,12 @@ SerializationPolicyProvider { /** + * Used by {...@link #getPermutationStrongName()}. + */ + /* * NB: Keep in sync with RemoteServiceProxy. */ - private static final String STRONG_NAME_HEADER = "X-GWT-Permutation"; + protected static final String STRONG_NAME_HEADER = "X-GWT-Permutation"; private final ThreadLocal<HttpServletRequest> perThreadRequest = new ThreadLocal<HttpServletRequest>(); Modified: trunk/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java ============================================================================== --- trunk/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java (original) +++ trunk/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java Wed Mar 18 17:59:19 2009 @@ -18,6 +18,7 @@ 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.RequestCallback; import com.google.gwt.http.client.RequestException; import com.google.gwt.http.client.Response; import com.google.gwt.junit.client.GWTTestCase; @@ -36,6 +37,60 @@ * </p> */ public class RemoteServiceServletTest extends GWTTestCase { + private static class MyRpcRequestBuilder extends RpcRequestBuilder { + private boolean doCreate; + private boolean doFinish; + private boolean doSetCallback; + private boolean doSetContentType; + private boolean doSetRequestData; + private boolean doSetRequestId; + + public void check() { + assertTrue("doCreate", doCreate); + assertTrue("doFinish", doFinish); + assertTrue("doSetCallback", doSetCallback); + assertTrue("doSetContentType", doSetContentType); + assertTrue("doSetRequestData", doSetRequestData); + assertTrue("doSetRequestId", doSetRequestId); + } + + @Override + protected RequestBuilder doCreate(String serviceEntryPoint) { + doCreate = true; + return super.doCreate(serviceEntryPoint); + } + + @Override + protected void doFinish(RequestBuilder rb) { + doFinish = true; + super.doFinish(rb); + } + + @Override + protected void doSetCallback(RequestBuilder rb, RequestCallback callback) { + doSetCallback = true; + super.doSetCallback(rb, callback); + } + + @Override + protected void doSetContentType(RequestBuilder rb, String contentType) { + doSetContentType = true; + super.doSetContentType(rb, contentType); + } + + @Override + protected void doSetRequestData(RequestBuilder rb, String data) { + doSetRequestData = true; + super.doSetRequestData(rb, data); + } + + @Override + protected void doSetRequestId(RequestBuilder rb, int id) { + doSetRequestId = true; + super.doSetRequestId(rb, id); + } + } + private static final int TEST_DELAY = 10000; protected static RemoteServiceServletTestServiceAsync getAsyncService() { @@ -117,6 +172,27 @@ finishTest(); } }); + }; + + /** + * Ensure that each doFoo method is called. + */ + public void testRpcRequestBuilder() { + final MyRpcRequestBuilder builder = new MyRpcRequestBuilder(); + RemoteServiceServletTestServiceAsync service = getAsyncService(); + ((ServiceDefTarget) service).setRpcRequestBuilder(builder); + + delayTestFinish(TEST_DELAY); + service.test(new AsyncCallback<Void>() { + public void onFailure(Throwable caught) { + TestSetValidator.rethrowException(caught); + } + + public void onSuccess(Void result) { + builder.check(); + finishTest(); + } + }); } public void testServiceInterfaceLocation() { --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
