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
-~----------~----~----~----~------~----~------~--~---

Reply via email to