Author: johnh
Date: Wed Oct  8 16:36:02 2008
New Revision: 703022

URL: http://svn.apache.org/viewvc?rev=703022&view=rev
Log:
Adding support for JSONP calling of /gadgets/metadata. This closes issue 
SHINDIG-624.

Many thanks to Michael Hermanto for the patch!


Added:
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/RpcServletTest.java
Modified:
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/RpcServlet.java

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/RpcServlet.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/RpcServlet.java?rev=703022&r1=703021&r2=703022&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/RpcServlet.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/RpcServlet.java
 Wed Oct  8 16:36:02 2008
@@ -18,48 +18,77 @@
  */
 package org.apache.shindig.gadgets.servlet;
 
-import org.apache.shindig.common.servlet.InjectedServlet;
-
 import com.google.inject.Inject;
-
 import org.apache.commons.io.IOUtils;
+import org.apache.shindig.common.servlet.InjectedServlet;
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-
-import javax.servlet.ServletInputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.util.regex.Pattern;
 
 /**
  * Handles RPC metadata requests.
  */
 public class RpcServlet extends InjectedServlet {
-  private static final int MAX_REQUEST_SIZE = 1024 * 128;
-  private static final Logger logger
-      = Logger.getLogger("org.apache.shindig.gadgets");
+  static final String GET_REQUEST_REQ_PARAM = "req";
+  static final String GET_REQUEST_CALLBACK_PARAM = "callback";
+  static final Pattern GET_REQUEST_CALLBACK_PATTERN = 
Pattern.compile("[A-Za-z_\\.]+");
+
+  private static final int POST_REQUEST_MAX_SIZE = 1024 * 128;
+  private static final Logger logger = 
Logger.getLogger("org.apache.shindig.gadgets");
 
   private JsonRpcHandler jsonHandler;
+
   @Inject
   public void setJsonRpcHandler(JsonRpcHandler jsonHandler) {
     this.jsonHandler = jsonHandler;
   }
 
+  @Override
+  protected void doGet(HttpServletRequest request, HttpServletResponse 
response)
+      throws IOException {
+    String reqValue;
+    String callbackValue;
+
+    try {
+      reqValue = validateParameterValue(request, GET_REQUEST_REQ_PARAM);
+      callbackValue = validateParameterValue(request, 
GET_REQUEST_CALLBACK_PARAM);
+      if (!GET_REQUEST_CALLBACK_PATTERN.matcher(callbackValue).matches()) {
+        throw new IllegalArgumentException("Wrong format for parameter '" +
+            GET_REQUEST_CALLBACK_PARAM + "' specified. Expected: " +
+            GET_REQUEST_CALLBACK_PATTERN.toString());
+      }
+
+    } catch (IllegalArgumentException e) {
+      response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+      logger.log(Level.INFO, e.getMessage(), e);
+      return;
+    }
+
+    Result result = process(request, response, reqValue.getBytes());
+    response.getWriter().write(result.isSuccess()
+        ? callbackValue + "(" + result.getOutput() + ")"
+        : result.getOutput());
+  }
 
   @Override
-  protected void doPost(HttpServletRequest request,
-      HttpServletResponse response) throws IOException {
+  protected void doPost(HttpServletRequest request, HttpServletResponse 
response)
+      throws IOException {
+
     int length = request.getContentLength();
     if (length <= 0) {
       logger.info("No Content-Length specified.");
       response.setStatus(HttpServletResponse.SC_LENGTH_REQUIRED);
       return;
     }
-    if (length > MAX_REQUEST_SIZE) {
+    if (length > POST_REQUEST_MAX_SIZE) {
       logger.info("Request size too large: " + length);
       response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
       return;
@@ -73,29 +102,66 @@
       return;
     }
 
+    Result result = process(request, response, body);
+    response.getWriter().write(result.getOutput());
+  }
+  
+  private String validateParameterValue(HttpServletRequest request, String 
parameter)
+      throws IllegalArgumentException {
+    String result = request.getParameter(parameter);
+    if (result == null) {
+      throw new IllegalArgumentException("No parameter '" + parameter + "' 
specified.");
+    }
+    return result;
+  }
+  
+  private Result process(HttpServletRequest request, HttpServletResponse 
response, byte[] body) {
     try {
-      String encoding = request.getCharacterEncoding();
-      if (encoding == null) {
-        encoding = "UTF-8";
-      }
+      String encoding = getRequestCharacterEncoding(request);
       JSONObject req = new JSONObject(new String(body, encoding));
-
       JSONObject resp = jsonHandler.process(req);
       response.setStatus(HttpServletResponse.SC_OK);
       response.setContentType("application/json; charset=utf-8");
       response.setHeader("Content-Disposition", "attachment;filename=rpc.txt");
-      response.getWriter().write(resp.toString());
+      return new Result(resp.toString(), true);
     } catch (UnsupportedEncodingException e) {
       response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-      response.getWriter().write("Unsupported input character set");
       logger.log(Level.INFO, e.getMessage(), e);
+      return new Result("Unsupported input character set", false);
     } catch (JSONException e) {
       response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-      response.getWriter().write("Malformed JSON request.");
+      return new Result("Malformed JSON request.", false);
     } catch (RpcException e) {
       response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-      response.getWriter().write(e.getMessage());
       logger.log(Level.INFO, e.getMessage(), e);
+      return new Result(e.getMessage(), false);
     }
   }
+  
+  private String getRequestCharacterEncoding(HttpServletRequest request) {
+    String encoding = request.getCharacterEncoding();
+    if (encoding == null) {
+      encoding = "UTF-8";
+    }
+    return encoding;
+  }
+
+  private static class Result {
+    private final String output;
+    private final boolean success;
+
+    public Result(String output, boolean success) {
+      this.output = output;
+      this.success = success;
+    }
+
+    public String getOutput() {
+      return output;
+    }
+
+    public boolean isSuccess() {
+      return success;
+    }
+  }
+
 }

Added: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/RpcServletTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/RpcServletTest.java?rev=703022&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/RpcServletTest.java
 (added)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/RpcServletTest.java
 Wed Oct  8 16:36:02 2008
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.servlet;
+
+import junit.framework.TestCase;
+import static org.easymock.classextension.EasyMock.*;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Tests for RpcServlet.
+ */
+public class RpcServletTest extends TestCase {
+  private RpcServlet servlet;
+  private JsonRpcHandler handler;
+  
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    servlet = new RpcServlet();
+    handler = createMock(JsonRpcHandler.class);
+    servlet.setJsonRpcHandler(handler);
+  }
+
+  public void testDoGetNormal() throws Exception {
+    HttpServletRequest request = createGetRequest("{\"gadgets\":[]}",
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._");
+    HttpServletResponse response = createHttpResponse("Content-Disposition",
+        "attachment;filename=rpc.txt", "application/json; charset=utf-8",
+        
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._({\"GADGETS\":[]})",
+        HttpServletResponse.SC_OK);
+    JSONObject handlerResponse = new JSONObject("{\"GADGETS\":[]}");
+    expect(handler.process(isA(JSONObject.class))).andReturn(handlerResponse);
+    replay(handler);
+    servlet.doGet(request, response);
+    verify(response);
+  }
+
+  public void testDoGetWithHandlerRpcException() throws Exception {
+    HttpServletRequest request = createGetRequest("{\"gadgets\":[]}", 
"function");
+    HttpServletResponse response = createHttpResponse("rpcExceptionMessage",
+        HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+    expect(handler.process(isA(JSONObject.class))).andThrow(
+        new RpcException("rpcExceptionMessage"));
+    replay(handler);
+    servlet.doGet(request, response);
+    verify(response);
+  }
+
+  public void testDoGetWithHandlerJsonException() throws Exception {
+    HttpServletRequest request = createGetRequest("{\"gadgets\":[]}", 
"function");
+    HttpServletResponse response = createHttpResponse("Malformed JSON 
request.",
+        HttpServletResponse.SC_BAD_REQUEST);
+    expect(handler.process(isA(JSONObject.class))).andThrow(new 
JSONException("json"));
+    replay(handler);
+    servlet.doGet(request, response);
+    verify(response);
+  }
+  
+  public void testDoGetWithMissingReqParam() throws Exception {
+    HttpServletRequest request = createGetRequest(null, "function");
+    HttpServletResponse response = createHttpResponse(null, 
HttpServletResponse.SC_BAD_REQUEST);
+    servlet.doGet(request, response);
+    verify(response);
+  }
+
+  public void testDoGetWithMissingCallbackParam() throws Exception {
+    HttpServletRequest request = createGetRequest("{\"gadgets\":[]}", null);
+    HttpServletResponse response = createHttpResponse(null, 
HttpServletResponse.SC_BAD_REQUEST);
+    servlet.doGet(request, response);
+    verify(response);
+  }
+
+  public void testDoGetWithBadCallbackParamValue() throws Exception {
+    HttpServletRequest request = createGetRequest("{\"gadgets\":[]}", "/'!=");
+    HttpServletResponse response = createHttpResponse(null, 
HttpServletResponse.SC_BAD_REQUEST);
+    servlet.doGet(request, response);
+    verify(response);
+  }
+
+  private HttpServletRequest createGetRequest(String reqParamValue, String 
callbackParamValue) {
+    HttpServletRequest result = createMock(HttpServletRequest.class);
+    expect(result.getMethod()).andReturn("GET").anyTimes();
+    expect(result.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
+    expect(result.getParameter(RpcServlet.GET_REQUEST_REQ_PARAM))
+        .andReturn(reqParamValue).anyTimes();
+    expect(result.getParameter(RpcServlet.GET_REQUEST_CALLBACK_PARAM))
+        .andReturn(callbackParamValue).anyTimes();
+    replay(result);
+    return result;
+  }
+
+  private HttpServletResponse createHttpResponse(String response, int 
httpStatusCode)
+    throws IOException {
+    return createHttpResponse(null, null, null, response, httpStatusCode);
+  }
+
+  private HttpServletResponse createHttpResponse(String header1, String 
header2,
+      String contentType, String response, int httpStatusCode) throws 
IOException {
+    HttpServletResponse result = createMock(HttpServletResponse.class);
+    PrintWriter writer = createMock(PrintWriter.class);
+    if (response != null) {
+      expect(result.getWriter()).andReturn(writer);
+      writer.write(response);
+    }
+    if (header1 != null && header2 != null) {
+      result.setHeader(header1, header2);
+    }
+    if (contentType != null) {
+      result.setContentType(contentType);
+    }
+    result.setStatus(httpStatusCode);
+    replay(result, writer);
+    return result;
+  }
+
+}


Reply via email to