You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by et...@apache.org on 2008/02/09 15:07:24 UTC

svn commit: r620115 - in /incubator/shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/ main/java/org/apache/shindig/gadgets/http/ main/java/org/apache/shindig/util/ test/java/org/apache/shindig/gadgets/

Author: etnu
Date: Sat Feb  9 06:07:23 2008
New Revision: 620115

URL: http://svn.apache.org/viewvc?rev=620115&view=rev
Log:
Implemented ideas discussed on SHINDIG-46 with some additional changes. Cleaned up file loading and xml parsing routines to eliminate redundant code. Added qualifiers to test fixtures that were causing periodic build breakages.


Added:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/InputStreamConsumer.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/RemoteContentTest.java
Removed:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Utf8InputStream.java
Modified:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecParser.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleParser.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleSubstituter.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContent.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SpecParserException.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderingServlet.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/RpcServlet.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/EasyMockTestCase.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecParserTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecTestFixture.java

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java Sat Feb  9 06:07:23 2008
@@ -322,10 +322,9 @@
         return;
       }
 
-      byte[] xml = null;
+      RemoteContent xml = null;
       try {
-        xml = fetcher.fetch(
-            gadgetId.getURI().toURL(), wc.context.getOptions()).getByteArray();
+        xml = fetcher.fetch(gadgetId.getURI().toURL(), wc.context.getOptions());
       } catch (MalformedURLException e) {
         throw new GadgetException(
             GadgetException.Code.FAILED_TO_RETRIEVE_CONTENT,
@@ -333,7 +332,7 @@
       }
 
       GadgetSpecParser specParser = new GadgetSpecParser();
-      GadgetSpec spec = specParser.parse(gadgetId, xml);
+      GadgetSpec spec = specParser.parse(gadgetId, xml.getResponseAsString());
       wc.gadget = new Gadget(gadgetId, spec, prefs);
 
       // This isn't a separate job because if it is we'd just need another

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecParser.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecParser.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecParser.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecParser.java Sat Feb  9 06:07:23 2008
@@ -23,6 +23,7 @@
 import org.xml.sax.SAXException;
 
 import java.io.IOException;
+import java.io.StringReader;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -47,28 +48,28 @@
    * content.
    *
    * @param id Gadget.ID object that resulted in the provided data
-   * @param xml The raw input xml
+   * @param xml The raw input xml.
    * @return A new GadgetSpec
    * @throws SpecParserException If {@code data} does not represent a valid
    * {@code GadgetSpec}
    */
-  public GadgetSpec parse(GadgetView.ID id, byte[] xml)
+  public GadgetSpec parse(GadgetView.ID id, String xml)
       throws SpecParserException {
-    if (null == xml || xml.length == 0) {
-      throw new SpecParserException("Empty XML document.");
+    if (xml.length() == 0) {
+      throw new SpecParserException(GadgetException.Code.EMPTY_XML_DOCUMENT);
     }
 
     Document doc;
     try {
       DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-      InputSource is = new InputSource(new Utf8InputStream(xml));
+      InputSource is = new InputSource(new StringReader(xml));
       doc = factory.newDocumentBuilder().parse(is);
     } catch (SAXException e) {
-      throw new SpecParserException("Malformed XML document.");
+      throw new SpecParserException(e.getMessage());
     } catch (ParserConfigurationException e) {
-      throw new SpecParserException("Malformed XML document.");
+      throw new SpecParserException(e.getMessage());
     } catch (IOException e) {
-      throw new SpecParserException("Malformed XML document.");
+      throw new SpecParserException(e.getMessage());
     }
 
     ParsedGadgetSpec spec = new ParsedGadgetSpec();

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java Sat Feb  9 06:07:23 2008
@@ -13,14 +13,13 @@
  */
 package org.apache.shindig.gadgets;
 
+import org.apache.shindig.util.InputStreamConsumer;
 import org.w3c.dom.Document;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -186,14 +185,18 @@
    * @param path Location of the resource list.
    * @return A list of resources from the list.
    */
-  private String[] readResourceList(String path) {
+  private String[] readResourceList(String path) throws IOException {
     ClassLoader cl = JsFeatureLoader.class.getClassLoader();
     InputStream is = cl.getResourceAsStream(path);
     if (is == null) {
       logger.warning("Unable to locate resource: " + path);
       return new String[0];
     } else {
-      String names = new String(load(is));
+      String names = InputStreamConsumer.readToString(is);
+      if (names == null) {
+        logger.warning("Unable to load resource: " + path);
+        return new String[0];
+      }
       return names.split("\n");
     }
   }
@@ -214,10 +217,9 @@
       ClassLoader cl = JsFeatureLoader.class.getClassLoader();
       InputStream is = cl.getResourceAsStream(name);
       if (is != null) {
-        byte[] content = load(is);
         int lastSlash = name.lastIndexOf('/');
         String base = (lastSlash == -1) ? name : name.substring(0, lastSlash + 1);
-        feature = parse(content, base, true);
+        feature = parse(is, base, true);
       }
     } catch (GadgetException ge) {
       logger.warning("Failed to load resource: " + name);
@@ -239,8 +241,8 @@
     ParsedFeature feature = null;
     if (file.canRead()) {
       try {
-        byte[] content = load(new FileInputStream(file));
-        feature = parse(content, file.getParent() + '/', false);
+        feature = parse(
+            new FileInputStream(file), file.getParent() + '/', false);
       } catch (IOException e) {
         logger.warning("Error reading file: " + file.getAbsolutePath());
       } catch (GadgetException ge) {
@@ -282,22 +284,18 @@
 
   /**
    * Parses the input into a dom tree.
-   * @param xml
+   * @param is
    * @param path The path the file was loaded from.
    * @param isResource True if the file was a resource.
    * @return A dom tree representing the feature.
    * @throws GadgetException
    */
-  private ParsedFeature parse(byte[] xml, String path, boolean isResource)
+  private ParsedFeature parse(InputStream is, String path, boolean isResource)
       throws GadgetException {
-    if (null == xml || xml.length == 0) {
-      throw new GadgetException(GadgetException.Code.EMPTY_XML_DOCUMENT);
-    }
 
     Document doc;
     try {
       DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-      InputSource is = new InputSource(new Utf8InputStream(xml));
       doc = factory.newDocumentBuilder().parse(is);
     } catch (SAXException e) {
       throw new GadgetException(GadgetException.Code.MALFORMED_XML_DOCUMENT, e);
@@ -387,26 +385,6 @@
         }
       }
     }
-  }
-
-  /**
-   * Loads content from the specified stream.
-   * @param is
-   * @return The contents of the file.
-   */
-  private byte[] load(InputStream is) {
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-    byte[] buf = new byte[8192];
-    int read;
-    try {
-      while ((read = is.read(buf)) > 0) {
-        baos.write(buf, 0, read);
-      }
-    } catch (IOException e) {
-      return new byte[0];
-    }
-
-    return baos.toByteArray();
   }
 }
 

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java Sat Feb  9 06:07:23 2008
@@ -13,12 +13,12 @@
  */
 package org.apache.shindig.gadgets;
 
-import java.io.ByteArrayOutputStream;
+import org.apache.shindig.util.InputStreamConsumer;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
 import java.util.logging.Logger;
 
 /**
@@ -130,12 +130,11 @@
     FileInputStream fis = null;
     try {
       fis = new FileInputStream(fileName);
+      return InputStreamConsumer.readToString(fis);
     } catch (IOException e) {
       throw new RuntimeException(
           String.format("Error reading file %s", fileName), e);
     }
-
-    return loadFromInputStream(fis, fileName, "file");
   }
 
   /**
@@ -144,44 +143,21 @@
    * @return The contents of the named resource.
    */
   private static String loadResource(String name) {
-     InputStream stream =
-         JsLibrary.class.getClassLoader().getResourceAsStream(name);
-     if (stream == null) {
+     try {
+       InputStream stream =
+            JsLibrary.class.getClassLoader().getResourceAsStream(name);
+       if (stream == null) {
+         throw new RuntimeException(
+             String.format("Could not find resource %s", name));
+       }
+       return InputStreamConsumer.readToString(stream);
+     } catch (IOException e) {
        throw new RuntimeException(
            String.format("Could not find resource %s", name));
      }
-     return loadFromInputStream(stream, name, "resource");
   }
 
-  /**
-   * Loads content from the given input stream.
-   * @param is
-   * @param name
-   * @param type
-   * @return The contents of the stream.
-   */
-  private static String loadFromInputStream(InputStream is, String name,
-                                            String type) {
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-    byte[] buf = new byte[8192];
-    int read = 0;
-    try {
-      while ((read = is.read(buf)) > 0) {
-        baos.write(buf, 0, read);
-      }
-    } catch (IOException e) {
-      throw new RuntimeException(
-          String.format("Error reading %s %s", type, name), e);
-    }
 
-    String ret = null;
-    try {
-      ret = new String(baos.toByteArray(), "UTF8");
-    } catch (UnsupportedEncodingException e) {
-      throw new RuntimeException("Unexpected error: UTF8 encoding unsupported");
-    }
-    return ret;
-  }
 
   /**
    * @param type

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleParser.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleParser.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleParser.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleParser.java Sat Feb  9 06:07:23 2008
@@ -21,6 +21,7 @@
 import org.xml.sax.SAXException;
 
 import java.io.IOException;
+import java.io.StringReader;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -52,15 +53,15 @@
    * @return Message bundle resulting from the parse
    * @throws GadgetException If the bundle is empty or malformed
    */
-  public MessageBundle parse(byte[] xml) throws GadgetException {
-    if (null == xml || xml.length == 0) {
+  public MessageBundle parse(String xml) throws GadgetException {
+    if (xml.length() == 0) {
       throw new GadgetException(GadgetException.Code.EMPTY_XML_DOCUMENT);
     }
 
     Document doc;
     try {
       DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-      InputSource is = new InputSource(new Utf8InputStream(xml));
+      InputSource is = new InputSource(new StringReader(xml));
       doc = factory.newDocumentBuilder().parse(is);
     } catch (SAXException e) {
       throw new GadgetException(GadgetException.Code.MALFORMED_XML_DOCUMENT);

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleSubstituter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleSubstituter.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleSubstituter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleSubstituter.java Sat Feb  9 06:07:23 2008
@@ -82,20 +82,18 @@
         // We definitely need a bundle, now we need to fetch it.
         bundle = context.getMessageBundleCache().get(uri.toString());
         if (bundle == null) {
-          byte[] data = null;
+          RemoteContent data = null;
           try {
-            data = context.getHttpFetcher().fetch(
-                uri.toURL(), context.getOptions()).getByteArray();
+            data = context.getHttpFetcher().fetch(uri.toURL(),
+                                                  context.getOptions());
           } catch (MalformedURLException e) {
             throw new GadgetException(
                 GadgetException.Code.FAILED_TO_RETRIEVE_CONTENT,
                 String.format("Malformed message bundle URL: %s",
                               uri.toString()));
           }
-          if (data.length > 0) {
-            bundle = parser.parse(data);
-            context.getMessageBundleCache().put(uri.toString(), bundle);
-          }
+          bundle = parser.parse(data.getResponseAsString());
+          context.getMessageBundleCache().put(uri.toString(), bundle);
         }
       }
     }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContent.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContent.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContent.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContent.java Sat Feb  9 06:07:23 2008
@@ -13,6 +13,7 @@
  */
 package org.apache.shindig.gadgets;
 
+import java.io.UnsupportedEncodingException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -23,20 +24,27 @@
  * Represents the results of an HTTP content retrieval operation.
  */
 public class RemoteContent {
-  private int httpStatusCode = -1;
-  private byte[] resultBody = new byte[0];
-  private Map<String, List<String>> headers;
+  private final int httpStatusCode;
+  private static final String DEFAULT_ENCODING = "UTF-8";
+  private final String encoding;
+
+  // Used to lazily convert to a string representation of the input.
+  private String responseString = null;
+  private final byte[] responseBytes;
+  private final Map<String, List<String>> headers;
 
   /**
    * @param httpStatusCode
    * @param resultBody
    * @param headers May be null.
    */
-  public RemoteContent(int httpStatusCode, byte[] resultBody,
+  public RemoteContent(int httpStatusCode, byte[] responseBytes,
                        Map<String, List<String>> headers) {
     this.httpStatusCode = httpStatusCode;
-    if (resultBody != null) {
-      this.resultBody = resultBody;
+    if (responseBytes == null) {
+      this.responseBytes = new byte[0];
+    } else {
+      this.responseBytes = responseBytes;
     }
 
     Map<String, List<String>> tempHeaders = new HashMap<String, List<String>>();
@@ -52,6 +60,26 @@
     }
 
     this.headers = Collections.unmodifiableMap(tempHeaders);
+    this.encoding = detectEncoding();
+  }
+
+  /**
+   * Attempts to determine the encoding of the body. If it can't be determined,
+   * we use DEFAULT_ENCODING instead.
+   * @return The detected encoding or DEFAULT_ENCODING.
+   */
+  private String detectEncoding() {
+    String contentType = getHeader("Content-Type");
+    if (contentType != null) {
+      String[] parts = contentType.split(";");
+      if (parts.length == 2) {
+        int offset = parts[1].indexOf("charset=");
+        if (offset != -1) {
+          return parts[1].substring(offset + 8);
+        }
+      }
+    }
+    return DEFAULT_ENCODING;
   }
 
   public int getHttpStatusCode() {
@@ -59,7 +87,35 @@
   }
 
   public byte[] getByteArray() {
-    return resultBody;
+    return responseBytes;
+  }
+
+  public String getEncoding() {
+    return encoding;
+  }
+
+  /**
+   * Attempts to convert the response body to a string using the Content-Type
+   * header. If no Content-Type header is specified (or it doesn't include an
+   * encoding), we will assume it is UTF-8.
+   *
+   * @return The body as a string.
+   */
+  public String getResponseAsString() {
+    if (responseString == null) {
+      try {
+        String response = new String(responseBytes, encoding);
+        // Strip BOM.
+        if (response.length() > 0 && response.codePointAt(0) == 0xFEFF) {
+          responseString = response.substring(1);
+        } else {
+          responseString = response;
+        }
+      } catch (UnsupportedEncodingException e) {
+        responseString = "Unable to convert from encoding: " + encoding;
+      }
+    }
+    return responseString;
   }
 
   /**

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SpecParserException.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SpecParserException.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SpecParserException.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SpecParserException.java Sat Feb  9 06:07:23 2008
@@ -17,21 +17,14 @@
  * Exceptions for Gadget Spec parsing.
  */
 public class SpecParserException extends GadgetException {
-  private String message;
-
   /**
    * @param message
    */
   public SpecParserException(String message) {
-    super(GadgetException.Code.MALFORMED_XML_DOCUMENT);
-    this.message = message;
+    super(GadgetException.Code.MALFORMED_XML_DOCUMENT, message);
   }
 
-  /**
-   * @return The message for this exception.
-   */
-  @Override
-  public String getMessage() {
-    return message;
+  public SpecParserException(GadgetException.Code code) {
+    super(code);
   }
 }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderingServlet.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderingServlet.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderingServlet.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/GadgetRenderingServlet.java Sat Feb  9 06:07:23 2008
@@ -231,7 +231,7 @@
     markup.append("<script>gadgets.util.runOnLoadHandlers();</script>");
     markup.append("</body></html>");
 
-    resp.getOutputStream().print(markup.toString());
+    resp.getWriter().print(markup.toString());
   }
 
   private void outputUrlGadget(Gadget gadget,

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java Sat Feb  9 06:07:23 2008
@@ -73,10 +73,11 @@
       String output;
       try {
         // Use raw param as key as URL may have to be decoded
-        String json = new JSONObject().put(request.getParameter("url"), new JSONObject()
-            .put("body", new String(results.getByteArray()))
-            .put("rc", results.getHttpStatusCode())
-            ).toString();
+        JSONObject resp = new JSONObject()
+            .put("body", results.getResponseAsString())
+            .put("rc", results.getHttpStatusCode());
+        String json = new JSONObject()
+            .put(request.getParameter("url"), new JSONObject()).toString();
         output = UNPARSEABLE_CRUFT + json;
       } catch (JSONException e) {
         output = "";
@@ -153,7 +154,7 @@
    * @return A URL object of the URL
    * @throws ServletException if the URL fails security checks or is malformed.
    */
-  private URL extractAndValidateUrl(HttpServletRequest request)                                                                 
+  private URL extractAndValidateUrl(HttpServletRequest request)
       throws ServletException {
     String url = request.getParameter("url");
     if (url == null) {
@@ -164,8 +165,8 @@
       URI origin = new URI(request.getParameter("url"));
       if (origin.getScheme() == null) {
         // No scheme, assume it was double-encoded.
-        origin = new URI(
-            URLDecoder.decode(request.getParameter("url"), request.getCharacterEncoding()));
+        origin = new URI(URLDecoder.decode(request.getParameter("url"),
+                         request.getCharacterEncoding()));
         if (origin.getScheme() == null) {
           throw new ServletException("Invalid URL " + origin.toString());
         }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/RpcServlet.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/RpcServlet.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/RpcServlet.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/RpcServlet.java Sat Feb  9 06:07:23 2008
@@ -82,6 +82,8 @@
       JsonRpcRequest req = new JsonRpcRequest(postBody);
       JSONObject out = req.process(state);
       response.setStatus(HttpServletResponse.SC_OK);
+      response.setContentType("application/json; charset=utf-8");
+      response.setHeader("Content-Disposition", "attachment;filename=rpc.txt");
       response.getWriter().write(out.toString());
     } catch (UnsupportedEncodingException e) {
       response.setStatus(HttpServletResponse.SC_BAD_REQUEST);

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/InputStreamConsumer.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/InputStreamConsumer.java?rev=620115&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/InputStreamConsumer.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/util/InputStreamConsumer.java Sat Feb  9 06:07:23 2008
@@ -0,0 +1,63 @@
+/*
+ * 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 org.apache.shindig.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Used to consume an entire input stream. Don't use this for network
+ * streams or any other stream that doesn't have a known length. This is
+ * intended for reading resources from jars and the local file system only.
+ */
+public class InputStreamConsumer {
+
+  /**
+   * Loads content and returns it as a raw byte array.
+   * @param is
+   * @return The contents of the stream.
+   * @throws IOException on stream reading error.
+   */
+  public static byte[] readToByteArray(InputStream is) throws IOException {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    byte[] buf = new byte[8192];
+    int read = 0;
+
+    while ((read = is.read(buf)) > 0) {
+      baos.write(buf, 0, read);
+    }
+
+    return baos.toByteArray();
+  }
+
+  /**
+   * Loads content from the given input stream as a UTF-8-encoded string.
+   *
+   * @param is
+   * @return The contents of the stream.
+   * @throws IOException on stream reading error.
+   */
+  public static String readToString(InputStream is) throws IOException {
+    byte[] bytes = readToByteArray(is);
+    try {
+      return new String(bytes, "UTF-8");
+    } catch (UnsupportedEncodingException e) {
+      // UTF-8 is required by the Java spec.
+      throw new RuntimeException("UTF-8 not supported!", e);
+    }
+  }
+}

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/EasyMockTestCase.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/EasyMockTestCase.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/EasyMockTestCase.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/EasyMockTestCase.java Sat Feb  9 06:07:23 2008
@@ -1,19 +1,19 @@
-/*
- * $Id$
+/**
+ * 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
  *
- * Copyright 2007 The Apache Software Foundation
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
- * 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.
+ * 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;
 

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecParserTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecParserTest.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecParserTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecParserTest.java Sat Feb  9 06:07:23 2008
@@ -41,11 +41,11 @@
     BasicGadgetId id = new BasicGadgetId();
     id.uri = new URI("http://example.org/text.xml");
     id.moduleId = 1;
-    byte[] xml = ("<?xml version=\"1.0\"?>" +
+    String xml = "<?xml version=\"1.0\"?>" +
                  "<Module>" +
                  "<ModulePrefs title=\"Hello, world!\"/>" +
                  "<Content type=\"html\">Hello!</Content>" +
-                 "</Module>").getBytes();
+                 "</Module>";
     GadgetSpec spec = parser.parse(id, xml);
 
     assertEquals("Hello!", spec.getContentData());
@@ -56,7 +56,7 @@
     BasicGadgetId id = new BasicGadgetId();
     id.uri = new URI("http://example.org/text.xml");
     id.moduleId = 1;
-    byte[] xml = ("<?xml version=\"1.0\"?>" +
+    String xml = "<?xml version=\"1.0\"?>" +
                  "<Module>" +
                  "<ModulePrefs title=\"Test Enum\">" +
                  "<UserPref name=\"test\" datatype=\"enum\">" +
@@ -65,7 +65,7 @@
                  "</UserPref>" +
                  "</ModulePrefs>" +
                  "<Content type=\"html\">Hello!</Content>" +
-                 "</Module>").getBytes();
+                 "</Module>";
     GadgetSpec spec = parser.parse(id, xml);
 
     List<GadgetSpec.UserPref> prefs = spec.getUserPrefs();

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecTestFixture.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecTestFixture.java?rev=620115&r1=620114&r2=620115&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecTestFixture.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecTestFixture.java Sat Feb  9 06:07:23 2008
@@ -1,19 +1,19 @@
-/*
- * $Id$
- *
- * Copyright 2007 The Apache Software Foundation
- *
- * 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
+/**
+ * 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
+ * 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.
+ * 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;
 
@@ -29,22 +29,22 @@
  * Contains useful static objects for testing classes that deal with GadgetSpecs.
  */
 public class GadgetSpecTestFixture {
-  static final Locale EN_US_LOCALE = new Locale("en", "US");
+  public static final Locale EN_US_LOCALE = new Locale("en", "US");
 
   private static final String DATETIME_TITLE = "Hello, World!";
   private static final String DATETIME_CONTENT = "Goodbye, World!";
 
-  static final String DATETIME_URI_STRING = "http://www.google.com/ig/modules/datetime.xml";
-  static final URI DATETIME_URI;
-  static final int DATETIME_MODULE_ID = 1;
-  static final GadgetView.ID DATETIME_ID;
-  static final String DATETIME_XML = "<?xml version=\"1.0\"?>" +
+  public static final String DATETIME_URI_STRING = "http://www.google.com/ig/modules/datetime.xml";
+  public static final URI DATETIME_URI;
+  public static final int DATETIME_MODULE_ID = 1;
+  public static final GadgetView.ID DATETIME_ID;
+  public static final String DATETIME_XML = "<?xml version=\"1.0\"?>" +
                                      "<Module>" +
                                      "  <ModulePrefs title=\"" + DATETIME_TITLE + "\"/>" +
                                      "  <Content type=\"html\">" + DATETIME_CONTENT + "</Content>" +
                                      "</Module>";
 
-  static final GadgetSpec DATETIME_SPEC =
+  public static final GadgetSpec DATETIME_SPEC =
       new GadgetSpec() {
         public GadgetSpec copy() {
           throw new UnsupportedOperationException();

Added: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/RemoteContentTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/RemoteContentTest.java?rev=620115&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/RemoteContentTest.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/RemoteContentTest.java Sat Feb  9 06:07:23 2008
@@ -0,0 +1,89 @@
+/**
+ * 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;
+
+import junit.framework.TestCase;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class RemoteContentTest extends TestCase {
+  private Map<String, List<String>> headers;
+
+  @Override
+  public void setUp() {
+    headers = new HashMap<String, List<String>>();
+  }
+
+  private void addHeader(String name, String value) {
+    java.util.List<String> existing = headers.get(name);
+    if (existing == null) {
+      existing = new LinkedList<String>();
+      headers.put(name, existing);
+    }
+    existing.add(value);
+  }
+
+  public void testGetEncoding() throws Exception {
+    addHeader("Content-Type", "text/plain; charset=TEST-CHARACTER-SET");
+    RemoteContent content = new RemoteContent(200, new byte[0], headers);
+    assertEquals("TEST-CHARACTER-SET", content.getEncoding());
+  }
+
+  public void testEncodingDetectionUtf8WithBom() throws Exception {
+    // Input is UTF-8 with BOM.
+    byte[] data = new byte[] {
+      (byte)0xEF, (byte)0xBB, (byte)0xBF, 'h', 'e', 'l', 'l', 'o'
+    };
+    addHeader("Content-Type", "text/plain; charset=UTF-8");
+    RemoteContent content = new RemoteContent(200, data, headers);
+    assertEquals("hello", content.getResponseAsString());
+  }
+
+  public void testEncodingDetectionLatin1() throws Exception {
+    // Input is a basic latin-1 string with 1 non-UTF8 compatible char.
+    byte[] data = new byte[] {
+      'h', (byte)0xE9, 'l', 'l', 'o'
+    };
+    addHeader("Content-Type", "text/plain; charset=iso-8859-1");
+    RemoteContent content = new RemoteContent(200, data, headers);
+    assertEquals("h\u00E9llo", content.getResponseAsString());
+  }
+
+  public void testEncodingDetectionBig5() throws Exception {
+    byte[] data = new byte[] {
+      (byte)0xa7, (byte)0x41, (byte)0xa6, (byte)0x6e
+    };
+    addHeader("Content-Type", "text/plain; charset=BIG5");
+    RemoteContent content = new RemoteContent(200, data, headers);
+    String resp = content.getResponseAsString();
+    assertEquals("\u4F60\u597D", content.getResponseAsString());
+  }
+
+  public void testPreserveBinaryData() {
+    byte[] data = new byte[] {
+        (byte)0x00, (byte)0xDE, (byte)0xEA, (byte)0xDB, (byte)0xEE, (byte)0xF0
+    };
+    addHeader("Content-Type", "application/octet-stream");
+    RemoteContent content = new RemoteContent(200, data, headers);
+    assertEquals(data, content.getByteArray());
+  }
+}