1. Updates all requests to use POST instead of GET.
2. Use JSON to encode requests instead of a custom solution.

Patch by: amitmanjhi
Review by: rjrjr (desk review and TBR)


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7762 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/requestfactory/client/gen/ClientRequestObject.java b/bikeshed/src/com/google/gwt/requestfactory/client/gen/ClientRequestObject.java
new file mode 100644
index 0000000..6f247f6
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/requestfactory/client/gen/ClientRequestObject.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010 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.requestfactory.client.gen;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+import java.util.Map;
+
+/**
+ * A convenience class to convert a Map<String, String> to a Json string on the
+ * client side.
+ */
+public class ClientRequestObject {
+
+  public static String getRequestString(Map<String, String> requestData) {
+    ClientRequestObject requestObject = new ClientRequestObject();
+    requestObject.init();
+    for (String key : requestData.keySet()) {
+      requestObject.put(key, requestData.get(key));
+    }
+    return requestObject.toJsonString();
+  }
+
+  private JavaScriptObject map;
+
+  ClientRequestObject() {
+    init();
+  }
+
+  private native void init()/*-{
+    this.@com.google.gwt.requestfactory.client.gen.ClientRequestObject::map = {};
+  }-*/;
+
+  private native void put(String key, String value)/*-{
+    this.@com.google.gwt.requestfactory.client.gen.ClientRequestObject::map[key] = value;
+  }-*/;
+
+  private native String toJsonString()/*-{
+    return JSON.stringify(this.@com.google.gwt.requestfactory.client.gen.ClientRequestObject::map);
+  }-*/;
+}
diff --git a/bikeshed/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java b/bikeshed/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
index 3cc12b4..d8f3122 100644
--- a/bikeshed/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
+++ b/bikeshed/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
@@ -20,8 +20,10 @@
 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.requestfactory.client.gen.ClientRequestObject;
 import com.google.gwt.requestfactory.shared.RequestFactory;
 import com.google.gwt.requestfactory.shared.SyncRequest;
+import com.google.gwt.requestfactory.shared.impl.RequestDataManager;
 import com.google.gwt.sample.expenses.gen.MethodName;
 import com.google.gwt.user.client.ui.HasValue;
 import com.google.gwt.user.client.ui.HasValueList;
@@ -39,7 +41,7 @@
  */
 public class RequestFactoryJsonImpl implements RequestFactory,
     RequestFactory.Service {
-  
+
   private final ValueStore valueStore = new ValueStore() {
 
     public void addValidation() {
@@ -59,13 +61,13 @@
         T propertyOwner, Set<Property<T, ?>> properties) {
       throw new UnsupportedOperationException();
     }
-    
+
   };
 
-  @SuppressWarnings("deprecation")
   public void fire(final RequestObject requestObject) {
-    RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
-        requestObject.getRequestUrl());
+    RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
+        RequestFactory.URL);
+    builder.setRequestData(requestObject.getRequestData());
     builder.setCallback(new RequestCallback() {
 
       public void onError(Request request, Throwable exception) {
@@ -106,7 +108,7 @@
       public void fire() {
 
         RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
-            "/expenses/data?methodName=" + MethodName.SYNC.name());
+            "/expenses/data");
 
         StringBuilder requestData = new StringBuilder("[");
         boolean first = true;
@@ -121,7 +123,8 @@
         }
         requestData.append("]");
 
-        builder.setRequestData(requestData.toString());
+        builder.setRequestData(ClientRequestObject.getRequestString(RequestDataManager.getRequestMap(
+            MethodName.SYNC, null, requestData.toString())));
         builder.setCallback(new RequestCallback() {
 
           public void onError(Request request, Throwable exception) {
diff --git a/bikeshed/src/com/google/gwt/requestfactory/shared/RequestFactory.java b/bikeshed/src/com/google/gwt/requestfactory/shared/RequestFactory.java
index ac379ef..b582bd8 100644
--- a/bikeshed/src/com/google/gwt/requestfactory/shared/RequestFactory.java
+++ b/bikeshed/src/com/google/gwt/requestfactory/shared/RequestFactory.java
@@ -25,18 +25,15 @@
  */
 public interface RequestFactory {
 
+  String URL = "/expenses/data";
+
   /**
    * Implemented by the request objects created by this factory.
    */
   interface RequestObject {
     void fire();
 
-    String getRequestData(String data);
-
-    /**
-     * @deprecated Here only until we can move everything into the post data
-     */
-    String getRequestUrl();
+    String getRequestData();
 
     void handleResponseText(String responseText);
   }
diff --git a/bikeshed/src/com/google/gwt/requestfactory/shared/impl/UrlParameterManager.java b/bikeshed/src/com/google/gwt/requestfactory/shared/impl/RequestDataManager.java
similarity index 61%
rename from bikeshed/src/com/google/gwt/requestfactory/shared/impl/UrlParameterManager.java
rename to bikeshed/src/com/google/gwt/requestfactory/shared/impl/RequestDataManager.java
index fbec5c8..044f522 100644
--- a/bikeshed/src/com/google/gwt/requestfactory/shared/impl/UrlParameterManager.java
+++ b/bikeshed/src/com/google/gwt/requestfactory/shared/impl/RequestDataManager.java
@@ -15,19 +15,25 @@
  */
 package com.google.gwt.requestfactory.shared.impl;
 
+import com.google.gwt.sample.expenses.gen.MethodName;
+
+import java.util.HashMap;
 import java.util.Map;
 
 /**
- * An utitlity class to manage the encoding and decoding of parameters.
- *
+ * An utitlity class to manage the encoding and decoding of parameters and
+ * methodNames.
+ * 
  * TODO: add appropriate unit tests.
  */
-public class UrlParameterManager {
+public class RequestDataManager {
 
-  private static final String TOKEN = "param";
+  public static final String CONTENT_TOKEN = "contentData";
+  public static final String METHOD_TOKEN = "methodName";
+  public static final String PARAM_TOKEN = "param";
 
-  public static Object[] getObjectsFromFragment(
-      Map<String, String[]> parameterMap, Class<?> parameterClasses[]) {
+  public static Object[] getObjectsFromParameterMap(
+      Map<String, String> parameterMap, Class<?> parameterClasses[]) {
     assert parameterClasses != null;
     Object args[] = new Object[parameterClasses.length];
     for (int i = 0; i < parameterClasses.length; i++) {
@@ -38,23 +44,23 @@
   }
 
   /**
-   * Returns the string that encodes the values. The string has a leading &.
+   * Returns the string that encodes the request data.
    * 
-   * @param values
-   * @return
    */
-  public static String getUrlFragment(Object values[]) {
-    assert values != null;
-    StringBuffer fragment = new StringBuffer();
-    for (int i = 0; i < values.length; i++) {
-      Object value = values[i];
-      fragment.append("&");
-      fragment.append(TOKEN);
-      fragment.append(i);
-      fragment.append("=");
-      fragment.append(value.toString());
+  public static Map<String, String> getRequestMap(MethodName methodName,
+      Object values[], String content) {
+    Map<String, String> requestMap = new HashMap<String, String>();
+    requestMap.put(METHOD_TOKEN, methodName.name());
+    if (values != null) {
+      for (int i = 0; i < values.length; i++) {
+        Object value = values[i];
+        requestMap.put(PARAM_TOKEN + i, value.toString());
+      }
     }
-    return fragment.toString();
+    if (content != null) {
+      requestMap.put(CONTENT_TOKEN, content);
+    }
+    return requestMap;
   }
 
   /**
@@ -62,10 +68,7 @@
    * 
    */
   private static Object encodeParameterValue(String parameterType,
-      String parameterValues[]) {
-    assert parameterValues != null;
-    assert parameterValues.length == 1;
-    String parameterValue = parameterValues[0];
+      String parameterValue) {
     if ("java.lang.String".equals(parameterType)) {
       return parameterValue;
     }
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gen/EmployeeRequestImpl.java b/bikeshed/src/com/google/gwt/sample/expenses/gen/EmployeeRequestImpl.java
index 247e48a..fc4986b 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gen/EmployeeRequestImpl.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gen/EmployeeRequestImpl.java
@@ -15,9 +15,11 @@
  */
 package com.google.gwt.sample.expenses.gen;
 
+import com.google.gwt.requestfactory.client.gen.ClientRequestObject;
 import com.google.gwt.requestfactory.client.impl.AbstractListJsonRequestObject;
 import com.google.gwt.requestfactory.shared.EntityListRequest;
 import com.google.gwt.requestfactory.shared.RequestFactory.Service;
+import com.google.gwt.requestfactory.shared.impl.RequestDataManager;
 import com.google.gwt.sample.expenses.shared.EmployeeKey;
 import com.google.gwt.sample.expenses.shared.ExpenseRequestFactory;
 import com.google.gwt.valuestore.shared.ValueStore;
@@ -55,15 +57,9 @@
 
   public EntityListRequest<EmployeeKey> findAllEmployees() {
     return new Request() {
-      public String getRequestData(String data) {
-        // TODO Dear Amit: your code here
-        throw new UnsupportedOperationException();
-      }
-
-      @SuppressWarnings("deprecation")
-      public String getRequestUrl() {
-        return "/expenses/data?methodName="
-            + MethodName.FIND_ALL_EMPLOYEES.name();
+      public String getRequestData() {
+        return ClientRequestObject.getRequestString(RequestDataManager.getRequestMap(MethodName.FIND_ALL_EMPLOYEES,
+            null, null));
       }
     };
   }
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gen/ReportRequestImpl.java b/bikeshed/src/com/google/gwt/sample/expenses/gen/ReportRequestImpl.java
index cbb43c0..fc7d872 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gen/ReportRequestImpl.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gen/ReportRequestImpl.java
@@ -15,10 +15,11 @@
  */
 package com.google.gwt.sample.expenses.gen;
 
+import com.google.gwt.requestfactory.client.gen.ClientRequestObject;
 import com.google.gwt.requestfactory.client.impl.AbstractListJsonRequestObject;
 import com.google.gwt.requestfactory.shared.EntityListRequest;
 import com.google.gwt.requestfactory.shared.RequestFactory.Service;
-import com.google.gwt.requestfactory.shared.impl.UrlParameterManager;
+import com.google.gwt.requestfactory.shared.impl.RequestDataManager;
 import com.google.gwt.sample.expenses.shared.EmployeeKey;
 import com.google.gwt.sample.expenses.shared.ExpenseRequestFactory;
 import com.google.gwt.sample.expenses.shared.ReportKey;
@@ -57,32 +58,20 @@
 
   public EntityListRequest<ReportKey> findAllReports() {
     return new Request() {
-      public String getRequestData(String data) {
-        // TODO Dear Amit: your code here
-        throw new UnsupportedOperationException();
+      public String getRequestData() {
+        return ClientRequestObject.getRequestString(RequestDataManager.getRequestMap(
+            MethodName.FIND_ALL_REPORTS, null, null));
       }
 
-      @SuppressWarnings("deprecation")
-      public String getRequestUrl() {
-        return "/expenses/data?methodName="
-            + MethodName.FIND_ALL_REPORTS.name();
-      }
     };
   };
 
   public EntityListRequest<ReportKey> findReportsByEmployee(
       final ValueRef<EmployeeKey, String> id) {
     return new Request() {
-      public String getRequestData(String data) {
-        // TODO Dear Amit: your code here
-        throw new UnsupportedOperationException();
-      }
-
-      @SuppressWarnings("deprecation")
-      public String getRequestUrl() {
-        return "/expenses/data?methodName="
-            + MethodName.FIND_REPORTS_BY_EMPLOYEE.name()
-            + UrlParameterManager.getUrlFragment(new Object[] {id.get()});
+      public String getRequestData() {
+        return ClientRequestObject.getRequestString(RequestDataManager.getRequestMap(
+            MethodName.FIND_REPORTS_BY_EMPLOYEE, new Object[] {id.get()}, null));
       }
     };
   }
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java b/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java
index fcc185b..ace0c01 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java
@@ -16,7 +16,7 @@
 package com.google.gwt.sample.expenses.server;
 
 import com.google.gwt.requestfactory.shared.EntityKey;
-import com.google.gwt.requestfactory.shared.impl.UrlParameterManager;
+import com.google.gwt.requestfactory.shared.impl.RequestDataManager;
 import com.google.gwt.sample.expenses.gen.MethodName;
 import com.google.gwt.sample.expenses.server.domain.Report;
 import com.google.gwt.sample.expenses.server.domain.Storage;
@@ -33,7 +33,9 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -56,48 +58,61 @@
     }
   }
 
-  @SuppressWarnings("unchecked")
   @Override
-  protected void doGet(HttpServletRequest request, HttpServletResponse response)
+  protected void doPost(HttpServletRequest request, HttpServletResponse response)
       throws IOException {
 
-    response.setStatus(HttpServletResponse.SC_OK);
-    PrintWriter writer = response.getWriter();
-    MethodName operation = getMethodName(request.getParameter("methodName"));
+    MethodName methodName = null;
     try {
-      Class<?> classOperation = Class.forName("com.google.gwt.sample.expenses.server.domain."
-          + operation.getClassName());
-      Method methodOperation = null;
-      // TODO: check if method names must be unique in a class.
-      for (Method method : classOperation.getDeclaredMethods()) {
-        if (method.getName().equals(operation.getMethodName())) {
-          methodOperation = method;
+      response.setStatus(HttpServletResponse.SC_OK);
+      JSONObject topLevelJsonObject = new JSONObject(getContent(request));
+      methodName = getMethodName(topLevelJsonObject.getString(RequestDataManager.METHOD_TOKEN));
+      PrintWriter writer = response.getWriter();
+      switch (methodName) {
+        case FIND_ALL_EMPLOYEES:
+        case FIND_ALL_REPORTS:
+        case FIND_EMPLOYEE:
+        case FIND_REPORTS_BY_EMPLOYEE:
+          Class<?> classOperation = Class.forName("com.google.gwt.sample.expenses.server.domain."
+              + methodName.getClassName());
+          Method methodOperation = null;
+          // TODO: check if method names must be unique in a class.
+          for (Method method : classOperation.getDeclaredMethods()) {
+            if (method.getName().equals(methodName.getMethodName())) {
+              methodOperation = method;
+              break;
+            }
+          }
+          if (methodOperation == null) {
+            throw new IllegalArgumentException("unable to find "
+                + methodName.getMethodName() + " in " + classOperation);
+          }
+          if (!Modifier.isStatic(methodOperation.getModifiers())) {
+            throw new IllegalArgumentException("the "
+                + methodOperation.getName() + " is not static");
+          }
+          Object args[] = RequestDataManager.getObjectsFromParameterMap(
+              getParameterMap(topLevelJsonObject), methodOperation.getParameterTypes());
+          Object resultList = methodOperation.invoke(null, args);
+          if (!(resultList instanceof List)) {
+            throw new IllegalArgumentException("return value not a list "
+                + resultList);
+          }
+          JSONArray jsonArray = getJsonArray((List<?>) resultList);
+          writer.print(jsonArray.toString());
           break;
-        }
+        case SYNC:
+          sync(topLevelJsonObject.getString(RequestDataManager.CONTENT_TOKEN), writer);
+          break;
+        default:
+          System.err.println("POST: unknown method " + methodName);
+          break;
       }
-      if (methodOperation == null) {
-        throw new IllegalArgumentException("unable to find "
-            + operation.getMethodName() + " in " + classOperation);
-      }
-      if (!Modifier.isStatic(methodOperation.getModifiers())) {
-        throw new IllegalArgumentException("the " + methodOperation.getName()
-            + " is not static");
-      }
-      Map<String, String[]> parameterMap = request.getParameterMap();
-      Object args[] = UrlParameterManager.getObjectsFromFragment(parameterMap,
-          methodOperation.getParameterTypes());
-      Object resultList = methodOperation.invoke(null, args);
-      if (!(resultList instanceof List)) {
-        throw new IllegalArgumentException("return value not a list "
-            + resultList);
-      }
-      JSONArray jsonArray = getJsonArray((List<?>) resultList);
-      writer.print(jsonArray.toString());
       writer.flush();
       // TODO: clean exception handling code below.
     } catch (ClassNotFoundException e) {
       throw new IllegalArgumentException("unable to load the class: "
-          + operation);
+          + methodName);
     } catch (IllegalAccessException e) {
       throw new IllegalArgumentException(e);
     } catch (InvocationTargetException e) {
@@ -108,25 +123,19 @@
       throw new IllegalArgumentException(e);
     } catch (NoSuchMethodException e) {
       throw new IllegalArgumentException(e);
-    } 
+    }
   }
 
-  @Override
-  protected void doPost(HttpServletRequest request, HttpServletResponse response)
-      throws IOException {
-
-    response.setStatus(HttpServletResponse.SC_OK);
-    MethodName methodName = getMethodName(request.getParameter("methodName"));
-    PrintWriter writer = response.getWriter();
-    switch (methodName) {
-      case SYNC:
-        sync(request, writer);
-        break;
-      default:
-        System.err.println("POST: unknown method " + methodName);
-        break;
+  private String getContent(HttpServletRequest request) throws IOException {
+    int contentLength = request.getContentLength();
+    byte contentBytes[] = new byte[contentLength];
+    BufferedInputStream bis = new BufferedInputStream(request.getInputStream());
+    int readBytes = 0;
+    while (bis.read(contentBytes, readBytes, contentLength - readBytes) > 0) {
+      // read the contents
     }
-    writer.flush();
+    // TODO: encoding issues?
+    return new String(contentBytes);
   }
 
   /**
@@ -198,6 +207,23 @@
   }
 
   /**
+   * @param jsonObject
+   * @return
+   * @throws JSONException
+   */
+  private Map<String, String> getParameterMap(JSONObject jsonObject) throws JSONException {
+    Map<String, String> parameterMap = new HashMap<String, String>();
+    Iterator keys = jsonObject.keys();
+    while (keys.hasNext()) {
+      String key = keys.next().toString();
+      if (key.startsWith(RequestDataManager.PARAM_TOKEN)) {
+        parameterMap.put(key, jsonObject.getString(key));
+      }
+    }
+    return parameterMap;
+  }
+
+  /**
    * @param entityElement
    * @param property
    * @return
@@ -232,21 +258,10 @@
   }
 
   /**
-   * @param request
-   * @param writer
    * @throws IOException
    */
-  private void sync(HttpServletRequest request, PrintWriter writer)
-      throws IOException {
-    int contentLength = request.getContentLength();
-    byte contentBytes[] = new byte[contentLength];
-    BufferedInputStream bis = new BufferedInputStream(request.getInputStream());
-    int readBytes = 0;
-    while (bis.read(contentBytes, readBytes, contentLength - readBytes) > 0) {
-      // read the contents
-    }
-    // TODO: encoding issues?
-    String content = new String(contentBytes);
+  private void sync(String content, PrintWriter writer) throws IOException {
+
     try {
       JSONArray reportArray = new JSONArray(content);
       int length = reportArray.length();