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/08 00:18:34 UTC

svn commit: r619684 - in /incubator/shindig/trunk: features/core/ java/gadgets/ java/gadgets/src/main/java/org/apache/shindig/gadgets/ java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ java/gadgets/src/main/webapp/WEB-INF/ java/gadgets/src/te...

Author: etnu
Date: Thu Feb  7 15:18:30 2008
New Revision: 619684

URL: http://svn.apache.org/viewvc?rev=619684&view=rev
Log:
- Merged in changes from SHINDIG-51 and prepared code for merging SHINDIG-35
- Fixed caching behavior in JsServlet; files will now be cached indefinitely and a cache busting token must be appended to get the new version.
- Added placeholder for RpcServlet for SHINDIG-25.
- Fixed static files path in pom.xml to be compatible with the jar-based build (no longer places all files into the root; now files are in /gadgets/files/). This may
  require updating some of the static files, but currently examples all seem to work.
- Cleaned up a spec deviation in gadgets.io (did not properly handle POST_DATA) and collapsed "enums" to their simplest form.


Added:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/CrossServletState.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultCrossServletState.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/RpcServlet.java
Modified:
    incubator/shindig/trunk/features/core/io.js
    incubator/shindig/trunk/java/gadgets/pom.xml
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java
    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/http/GadgetRenderingServlet.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/JsServlet.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/ProxyServlet.java
    incubator/shindig/trunk/java/gadgets/src/main/webapp/WEB-INF/web.xml
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java

Modified: incubator/shindig/trunk/features/core/io.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/core/io.js?rev=619684&r1=619683&r2=619684&view=diff
==============================================================================
--- incubator/shindig/trunk/features/core/io.js (original)
+++ incubator/shindig/trunk/features/core/io.js Thu Feb  7 15:18:30 2008
@@ -82,37 +82,37 @@
       case "FEED":
         resp.data = gadgets.json.parse(resp.text);
         if (!resp.data) {
-          resp.errors = ["failed to parse JSON"];
+          resp.errors.push("failed to parse JSON");
           resp.data = null;
         }
         break;
-     case "DOM":
-      var dom;
-      if (window.ActiveXObject) {
-        dom = new ActiveXObject("Microsoft.XMLDOM");
-        dom.async = false;
-        dom.validateOnParse = false;
-        dom.resolveExternals = false;
-        if (!dom.loadXML(resp.text)) {
-          resp.errors = ["failed to parse XML"];
+      case "DOM":
+        var dom;
+        if (window.ActiveXObject) {
+          dom = new ActiveXObject("Microsoft.XMLDOM");
+          dom.async = false;
+          dom.validateOnParse = false;
+          dom.resolveExternals = false;
+          if (!dom.loadXML(resp.text)) {
+            resp.errors.push("failed to parse XML");
+          } else {
+            resp.data = dom;
+          }
         } else {
-          resp.data = dom;
+          var parser = new DOMParser();
+          dom = parser.parseFromString(resp.text, "text/xml");
+          if ("parsererror" == dom.documentElement.nodeName) {
+            resp.errors.push("failed to parse XML");
+          } else {
+            resp.data = dom;
+          }
         }
-      } else {
-        var parser = new DOMParser();
-        dom = parser.parseFromString(resp.text, "text/xml");
-        if ("parsererror" == dom.documentElement.nodeName) {
-          resp.errors = ["failed to parse XML"];
-        } else {
-          resp.data = dom;
-        }
-      }
-      break;
-    default:
-      resp.data = resp.text;
-      break;
-   }
-   callback(resp);
+        break;
+      default:
+        resp.data = resp.text;
+        break;
+    }
+    callback(resp);
   }
 
   return /** @scope gadgets.io */ {
@@ -145,10 +145,8 @@
           encodeURIComponent(url));
 
       // Check if authorization is requested
-      if (opt_params && opt_params.AUTHORIZATION &&
-          gadgets.io.AuthorizationType[opt_params.AUTHORIZATION.toUpperCase()]
-              != gadgets.io.AuthorizationType.NONE) {
-        newUrl += "&authz=" + opt_params.AUTHORIZATION.toLowerCase();
+      if (params.AUTHORIZATION && params.AUTHORIZATION !== "NONE") {
+        newUrl += "&authz=" + params.AUTHORIZATION.toLowerCase();
         // Add the security-token if available
         if (gadgets.util.getUrlParameters()["st"]) {
           newUrl += "&st=" + gadgets.util.getUrlParameters()["st"];
@@ -160,10 +158,11 @@
         xhr.onreadystatechange = gadgets.util.makeClosure(null,
             processResponse, url, callback, params, xhr);
       }
-      if (params.METHOD == "POST") {
-        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
-        if (params.postData) {
-          xhr.send("postData=" + encodeURIComponent(params.postData));
+      if (params.METHOD === "POST") {
+        xhr.setRequestHeader('Content-Type',
+            'application/x-www-form-urlencoded');
+        if (params.POST_DATA) {
+          xhr.send("postData=" + encodeURIComponent(params.POST_DATA));
         } else {
           xhr.send("postData=");
         }
@@ -225,189 +224,25 @@
   };
 }();
 
-// TODO: This can all be removed after the spec is published. This is only
-// here to satisfy documentation requirements.
-
-/**
- * @static
- * @class
- * Used by the
- * <a href="gadgets.io.html#makeRequest">
- * <code>gadgets.io.makeRequest()</code></a> method.
- * @name gadgets.io.RequestParameters
- */
-gadgets.io.RequestParameters = {
-  /**
-   * The method to use when fetching content from the URL;
-   * defaults to <code>MethodType.GET</a></code>.
-   * Specified as a
-   * <a href="gadgets.io.MethodType.html">MethodType</a>.
-   *
-   * @member gadgets.io.RequestParameters
-   */
-   METHOD : 'METHOD',
-
-  /**
-   * The type of content that lives at the URL;
-   * defaults to <code>ContentType.HTML</code>.
-   * Specified as a
-   * <a href="gadgets.io.ContentType.html">
-   * ContentType</a>.
-   *
-   * @member gadgets.io.RequestParameters
-   */
-  CONTENT_TYPE : "CONTENT_TYPE",
-
-  /**
-   * The data to send to the URL using the POST method;
-   * defaults to null.
-   * Specified as a <code>String</code>.
-   *
-   * @member gadgets.io.RequestParameters
-   */
-  POST_DATA : "POST_DATA",
-
-  /**
-   * The HTTP headers to send to the URL;
-   * defaults to null.
-   * Specified as a <code>Map.&lt;String,String&gt;</code>.
-   *
-   * @member gadgets.io.RequestParameters
-   */
-  HEADERS : "HEADERS",
-
-  /**
-   * The type of authentication to use when fetching the content;
-   * defaults to <code>AuthorizationType.NONE</code>.
-   * Specified as an
-   * <a href="gadgets.io.AuthorizationType.html">
-   * AuthorizationType</a>.
-   *
-   * @member gadgets.io.RequestParameters
-   */
-  AUTHORIZATION : 'AUTHORIZATION',
-
-
-  /**
-   * If the content is a feed, the number of entries to fetch;
-   * defaults to 3.
-   * Specified as a <code>Number</code>.
-   *
-   * @member gadgets.io.RequestParameters
-   */
-  NUM_ENTRIES : 'NUM_ENTRIES',
-
-  /**
-   * If the content is a feed, whether to fetch summaries for that feed;
-   * defaults to false.
-   * Specified as a <code>Boolean</code>.
-   *
-   * @member gadgets.io.RequestParameters
-   */
-  GET_SUMMARIES : 'GET_SUMMARIES'
-};
-
-
-/**
- * @static
- * @class
- * Used by
- * <a href="gadgets.io.RequestParameters.html">
- * RequestParameters</a>.
- * @name gadgets.io.MethodType
- */
-gadgets.io.MethodType = {
-  /**
-   * The default type.
-   * @member gadgets.io.MethodType
-   */
-  GET : 'GET',
-
-  /**
-   * Not supported by all containers.
-   * @member gadgets.io.MethodType
-   */
-  POST : 'POST',
-
-  /**
-   * Not supported by all containers.
-   * @member gadgets.io.MethodType
-   */
-  PUT : 'PUT',
-
-  /**
-   * Not supported by all containers.
-   * @member gadgets.io.MethodType
-   */
-  DELETE : 'DELETE',
-
-  /**
-   * Not supported by all containers.
-   * @member gadgets.io.MethodType
-   */
-  HEAD : 'HEAD'
-};
-
-
-/**
- * @static
- * @class
- * Used by
- * <a href="gadgets.io.RequestParameters.html">
- * RequestParameters</a>.
- * @name gadgets.io.ContentType
- */
-gadgets.io.ContentType = {
-  /**
-   * Returns text; used for fetching HTML.
-   * @member gadgets.io.ContentType
-   */
-  TEXT : 'TEXT',
-
-  /**
-   * Returns a DOM object; used for fetching XML.
-   * @member gadgets.io.ContentType
-   */
-  DOM : 'DOM',
-
-  /**
-   * Returns a JSON object.
-   * @member gadgets.io.ContentType
-   */
-  JSON : 'JSON',
-
-  /**
-   * Returns a JSON representation of a feed.
-   * @member gadgets.io.ContentType
-   */
-  FEED : 'FEED'
-};
-
-
-/**
- * @static
- * @class
- * Used by
- * <a href="gadgets.io.RequestParameters.html">
- * RequestParameters</a>.
- * @name gadgets.io.AuthorizationType
- */
-gadgets.io.AuthorizationType = {
-  /**
-   * No authorization.
-   * @member gadgets.io.AuthorizationType
-   */
-  NONE : 'NONE',
-
-  /**
-   * The request will be signed by the container.
-   * @member gadgets.io.AuthorizationType
-   */
-  SIGNED : 'SIGNED',
-
-  /**
-   * The container will use full authentication.
-   * @member gadgets.io.AuthorizationType
-   */
-  AUTHENTICATED : 'AUTHENTICATED'
-};
+gadgets.io.RequestParameters = gadgets.util.makeEnum([
+  "METHOD",
+  "CONTENT_TYPE",
+  "POST_DATA",
+  "HEADERS",
+  "AUTHORIZATION",
+  "NUM_ENTRIES",
+  "GET_SUMMARIES"
+]);
+
+// PUT, DELETE, and HEAD not supported currently.
+gadgets.io.MethodType = gadgets.util.makeEnum([
+  "GET", "POST", "PUT", "DELETE", "HEAD"
+]);
+
+gadgets.io.ContentType = gadgets.util.makeEnum([
+  "TEXT", "DOM", "JSON", "FEED"
+]);
+
+gadgets.io.AuthorizationType = gadgets.util.makeEnum([
+  "NONE", "SIGNED", "AUTHENTICATED"
+]);
\ No newline at end of file

Modified: incubator/shindig/trunk/java/gadgets/pom.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/pom.xml?rev=619684&r1=619683&r2=619684&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/pom.xml (original)
+++ incubator/shindig/trunk/java/gadgets/pom.xml Thu Feb  7 15:18:30 2008
@@ -28,6 +28,7 @@
             <resource>
               <!-- this is relative to the pom.xml directory -->
               <directory>../../javascript/</directory>
+              <targetPath>files</targetPath>
               <includes>
                 <include>**/*.*</include>
               </includes>

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java?rev=619684&r1=619683&r2=619684&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java Thu Feb  7 15:18:30 2008
@@ -146,6 +146,13 @@
   }
 
   /**
+   * @return All registered features.
+   */
+  public Map<String, Entry> getAllFeatures() {
+    return Collections.unmodifiableMap(features);
+  }
+
+  /**
    * Attempts to retrieve all the {@code GadgetFeature} classes specified
    * in the {@code needed} list. Those that are found are returned in
    * {@code resultsFound}, while the names of those that are missing are

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=619684&r1=619683&r2=619684&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 Thu Feb  7 15:18:30 2008
@@ -22,39 +22,21 @@
 import java.net.MalformedURLException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.Collections;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CompletionService;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorCompletionService;
 import java.util.concurrent.Future;
-import java.util.logging.Logger;
-import java.util.logging.Level;
 
 public class GadgetServer {
-  private final GadgetServerConfig config;
-
-  private static final Logger logger
-      = Logger.getLogger("org.apache.shindig.gadgets");
-
-  /**
-   * Creates a GadgetServer without a config.
-   *
-   * @deprecated Replaced by {@link #GadgetServer(GadgetServerConfigReader)}.
-   * @param executor
-   */
-  @Deprecated
-  public GadgetServer(Executor executor) {
-    config = new GadgetServerConfig();
-    config.setExecutor(executor);
-  }
+  private final GadgetServerConfigReader config;
 
   /**
    * Creates a GadgetServer using the provided configuration.
@@ -72,51 +54,11 @@
     Check.notNull(configuration.getContentFetcher(),
         "ContentFetcher is required.");
 
-    config = new GadgetServerConfig();
+    config = new GadgetServerConfigReader();
     config.copyFrom(configuration);
   }
 
   /**
-   * @deprecated Replaced by {@link #GadgetServer(GadgetServerConfigReader)}.
-   */
-  @Deprecated
-  public void setSpecCache(GadgetDataCache<GadgetSpec> specCache) {
-    config.setSpecCache(specCache);
-  }
-
-  /**
-   * @deprecated Replaced by {@link #GadgetServer(GadgetServerConfigReader)}.
-   */
-  @Deprecated
-  public void setMessageBundleCache(GadgetDataCache<MessageBundle> cache) {
-    config.setMessageBundleCache(cache);
-  }
-
-  /**
-   * @deprecated Replaced by {@link #GadgetServer(GadgetServerConfigReader)}.
-   */
-  @Deprecated
-  public void setContentFetcher(RemoteContentFetcher fetcher) {
-    config.setContentFetcher(fetcher);
-  }
-
-  /**
-   * @deprecated Replaced by {@link #GadgetServer(GadgetServerConfigReader)}.
-   */
-  @Deprecated
-  public void setGadgetFeatureRegistry(GadgetFeatureRegistry registry) {
-    config.setFeatureRegistry(registry);
-  }
-
-  /**
-   * @deprecated Replaced by {@link #GadgetServer(GadgetServerConfigReader)}.
-   */
-  @Deprecated
-  public void setGadgetBlacklist(GadgetBlacklist gadgetBlacklist) {
-    config.setGadgetBlacklist(gadgetBlacklist);
-  }
-
-  /**
    * @return A read-only view of the server's configuration.
    */
   public GadgetServerConfigReader getConfig() {
@@ -140,22 +82,6 @@
                               RenderingContext rctx,
                               ProcessingOptions options)
       throws GadgetProcessException {
-    // TODO: Remove dep checks when GadgetServer(Executor) is removed.
-    if (config.getSpecCache() == null) {
-      throw new GadgetProcessException(GadgetException.Code.MISSING_SPEC_CACHE);
-    }
-    if (config.getMessageBundleCache() == null ) {
-      throw new GadgetProcessException(
-          GadgetException.Code.MISSING_MESSAGE_BUNDLE_CACHE);
-    }
-    if (config.getContentFetcher() == null) {
-      throw new GadgetProcessException(
-          GadgetException.Code.MISSING_REMOTE_OBJECT_FETCHER);
-    }
-    if (config.getFeatureRegistry() == null) {
-      throw new GadgetProcessException(
-          GadgetException.Code.MISSING_FEATURE_REGISTRY);
-    }
 
     // Queue/tree of all jobs to be run for successful processing
     GadgetContext gc = new GadgetContext(config.getContentFetcher(),
@@ -242,8 +168,6 @@
       }
 
       if (gadgetException != null) {
-        logger.log(Level.SEVERE, gadgetException.getCode().toString(), gadgetException);
-        
         // Add to list of all exceptions caught, clear jobs, and continue
         // to aggressively catch as many exceptions as possible. Since
         // tasks are running anyway, we may as well get their results in
@@ -320,7 +244,8 @@
 
     private WorkflowContext(GadgetContext context) {
       this.context = context;
-      this.depsDone = Collections.synchronizedSet(new HashSet<WorkflowDependency>());
+      this.depsDone
+          = Collections.synchronizedSet(new HashSet<WorkflowDependency>());
       this.jobsToRun = new WorkflowJobList(this);
     }
   }
@@ -431,8 +356,9 @@
       Map<String, GadgetSpec.FeatureSpec> requires = wc.gadget.getRequires();
       Set<String> needed = new HashSet<String>(requires.size());
       Set<String> optionalNames = new HashSet<String>();
-        
-      for (Map.Entry<String, GadgetSpec.FeatureSpec> entry : requires.entrySet()) {
+
+      for (Map.Entry<String, GadgetSpec.FeatureSpec> entry :
+        requires.entrySet()) {
         needed.add(entry.getKey());
         if (entry.getValue().isOptional()) {
           optionalNames.add(entry.getKey());

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/CrossServletState.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/CrossServletState.java?rev=619684&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/CrossServletState.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/CrossServletState.java Thu Feb  7 15:18:30 2008
@@ -0,0 +1,107 @@
+/*
+ * 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.http;
+
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetServer;
+import org.apache.shindig.gadgets.GadgetSigner;
+
+import java.util.logging.Logger;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Loads shared configuration and creates appropriate class instances for
+ * sharing amongst servlets.
+ */
+public abstract class CrossServletState {
+  private static final Logger logger
+      = Logger.getLogger("org.apache.shindig.gadgets");
+  /**
+   * @param config
+   * @return A CrossServletState appropriate for the given ServletConfig
+   * @throws ServletException
+   */
+  public static synchronized CrossServletState get(ServletConfig config)
+      throws ServletException {
+    ServletContext context = config.getServletContext();
+    // Check to see if the context has already been created. If it has,
+    // just return.
+    CrossServletState state
+        = (CrossServletState)context.getAttribute("servlet-data");
+
+    if (state == null) {
+      String dataClass = context.getInitParameter("servlet-data-class");
+      if (dataClass  == null) {
+        throw new ServletException("servlet-data-class is missing.");
+      }
+      logger.info("Loading CrossServletState: " + dataClass);
+      try {
+        state  = (CrossServletState)Class.forName(dataClass).newInstance();
+        state.init(context);
+        context.setAttribute("servlet-data", state);
+      } catch (InstantiationException ie) {
+        throw new ServletException(ie);
+      } catch (IllegalAccessException iae) {
+        throw new ServletException(iae);
+      } catch (ClassNotFoundException cnfe) {
+        throw new ServletException(cnfe);
+      }
+    }
+    return state;
+  }
+
+  /**
+   * @return A GadgetServer instance that is fully configured and
+   * ready to be used to process gadgets.
+   */
+  public abstract GadgetServer getGadgetServer();
+
+  /**
+   * @param req The request that a signing token is needed for.
+   * @return A unique GadgetSigner for the request
+   */
+  public abstract GadgetSigner getGadgetSigner(HttpServletRequest req);
+
+  /**
+   * Constructs a url for retrieving javascript for the given
+   * set of features.
+   *
+   * @param features
+   * @return The url to retrieve the appropriate JS.
+   */
+  public abstract String getJsUrl(String[] features);
+
+  /**
+   * Constructs a url for generating an iframe for the given gadget.
+   * This only applies for RPC calls that must generate an iframe.
+   */
+  public abstract String getIframeUrl(Gadget gadget,  HttpServletRequest req);
+
+  /**
+   * Initializes this handler using the provided implementation.
+   * @param context
+   * @throws ServletException
+   */
+  protected abstract void init(ServletContext context) throws ServletException;
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultCrossServletState.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultCrossServletState.java?rev=619684&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultCrossServletState.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultCrossServletState.java Thu Feb  7 15:18:30 2008
@@ -0,0 +1,192 @@
+/*
+ * 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.http;
+
+import org.apache.shindig.gadgets.BasicGadgetDataCache;
+import org.apache.shindig.gadgets.BasicGadgetSigner;
+import org.apache.shindig.gadgets.BasicRemoteContentFetcher;
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.GadgetFeatureFactory;
+import org.apache.shindig.gadgets.GadgetFeatureRegistry;
+import org.apache.shindig.gadgets.GadgetServer;
+import org.apache.shindig.gadgets.GadgetServerConfig;
+import org.apache.shindig.gadgets.GadgetSigner;
+import org.apache.shindig.gadgets.GadgetSpec;
+import org.apache.shindig.gadgets.JsLibrary;
+import org.apache.shindig.gadgets.JsLibraryFeatureFactory;
+import org.apache.shindig.gadgets.MessageBundle;
+import org.apache.shindig.gadgets.RenderingContext;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.concurrent.Executors;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * A handler which uses all of the basic versions of classes.
+ */
+public class DefaultCrossServletState extends CrossServletState {
+
+  private GadgetServer gadgetServer;
+  private GadgetSigner gadgetSigner;
+  private String jsPath;
+  private String iframePath;
+  private String jsCacheParam;
+
+  private static final String DEFAULT_JS_PREFIX = "/gadgets/js/";
+  private static final String DEFAULT_IFRAME_PREFIX  = "/gadgets/ifr?";
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public GadgetServer getGadgetServer() {
+    return gadgetServer;
+  }
+
+  /**
+   * {@inheritDoc}
+   * Just returns the same gadget signer no matter the request.
+   */
+  @Override
+  public GadgetSigner getGadgetSigner(HttpServletRequest req) {
+    return gadgetSigner;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String getIframeUrl(Gadget gadget, HttpServletRequest req) {
+    // We don't have any meaningful data in the current request anyway, so
+    // we'll just do this statically.
+    StringBuilder buf = new StringBuilder();
+    try {
+      buf.append(iframePath)
+          .append("url=")
+          .append(
+              URLEncoder.encode(gadget.getId().getURI().toString(),"UTF-8"));
+    } catch (UnsupportedEncodingException e) {
+      throw new RuntimeException("UTF-8 Not supported!", e);
+    }
+
+    // TODO: extract user prefs, current view, etc. from <req>. Currently
+    // consumers of the response are on their own for this.
+    return buf.toString();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String getJsUrl(String[] features) {
+    StringBuilder buf = new StringBuilder();
+    buf.append(jsPath);
+    if (features == null || features.length == 0) {
+      buf.append("core");
+    } else {
+      boolean firstDone = false;
+      for (String feature : features) {
+        if (firstDone) {
+          buf.append(":");
+        } else {
+          firstDone = true;
+        }
+        buf.append(feature);
+      }
+    }
+    buf.append(".js?v=").append(jsCacheParam);
+    return buf.toString();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void init(ServletContext context) throws ServletException {
+    jsPath = context.getInitParameter("js-service-path");
+    if (jsPath == null) {
+      jsPath = DEFAULT_JS_PREFIX;
+    }
+
+    iframePath = context.getInitParameter("iframe-path");
+    if (iframePath == null) {
+      iframePath = DEFAULT_IFRAME_PREFIX;
+    }
+
+    // features could be null, but that would probably be a bad idea.
+    String features = context.getInitParameter("features");
+    try {
+      gadgetSigner = new BasicGadgetSigner();
+      GadgetFeatureRegistry registry = new GadgetFeatureRegistry(features);
+      GadgetServerConfig config =  new GadgetServerConfig()
+          .setExecutor(Executors.newCachedThreadPool())
+          .setMessageBundleCache(new BasicGadgetDataCache<MessageBundle>())
+          .setSpecCache(new BasicGadgetDataCache<GadgetSpec>())
+          .setContentFetcher(new BasicRemoteContentFetcher(1024 * 1024))
+          .setFeatureRegistry(registry);
+      gadgetServer = new GadgetServer(config);
+
+      // Grab all static javascript, concatenate it together, and generate
+      // an md5. This becomes the cache busting suffix for javascript files.
+      StringBuilder jsBuf = new StringBuilder();
+
+      for (Map.Entry<String, GadgetFeatureRegistry.Entry> entry :
+          registry.getAllFeatures().entrySet()) {
+        GadgetFeatureFactory factory = entry.getValue().getFeature();
+        if (factory instanceof JsLibraryFeatureFactory) {
+          JsLibraryFeatureFactory lib = (JsLibraryFeatureFactory)factory;
+          for (RenderingContext rc : RenderingContext.values()) {
+            for (JsLibrary library : lib.getLibraries(rc)) {
+              jsBuf.append(library.getContent());
+            }
+          }
+        }
+      }
+      MessageDigest md;
+      try {
+        md = MessageDigest.getInstance("MD5");
+      } catch (NoSuchAlgorithmException noMD5) {
+        try {
+          md = MessageDigest.getInstance("SHA");
+        } catch (NoSuchAlgorithmException noSha) {
+          throw new ServletException("No suitable MessageDigest found!");
+        }
+      }
+      byte[] hash = md.digest(jsBuf.toString().getBytes());
+      // Convert to hex. This might be a waste of bytes (32) -- could be
+      // replaced with a base64 implementation.
+      StringBuffer hexString = new StringBuffer();
+      for (byte b : hash) {
+        hexString.append(Integer.toHexString(0xFF & b));
+      }
+      jsCacheParam = hexString.toString();
+    } catch (GadgetException e) {
+      throw new ServletException(e);
+    }
+  }
+}

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=619684&r1=619683&r2=619684&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 Thu Feb  7 15:18:30 2008
@@ -13,18 +13,13 @@
  */
 package org.apache.shindig.gadgets.http;
 
-import org.apache.shindig.gadgets.BasicGadgetDataCache;
-import org.apache.shindig.gadgets.BasicRemoteContentFetcher;
 import org.apache.shindig.gadgets.Gadget;
 import org.apache.shindig.gadgets.GadgetContentFilter;
 import org.apache.shindig.gadgets.GadgetException;
-import org.apache.shindig.gadgets.GadgetFeatureRegistry;
 import org.apache.shindig.gadgets.GadgetServer;
-import org.apache.shindig.gadgets.GadgetServerConfig;
 import org.apache.shindig.gadgets.GadgetSpec;
 import org.apache.shindig.gadgets.GadgetView;
 import org.apache.shindig.gadgets.JsLibrary;
-import org.apache.shindig.gadgets.MessageBundle;
 import org.apache.shindig.gadgets.ProcessingOptions;
 import org.apache.shindig.gadgets.RenderingContext;
 import org.apache.shindig.gadgets.UserPrefs;
@@ -39,11 +34,11 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -52,63 +47,17 @@
  * Servlet for rendering Gadgets, typically in an IFRAME.
  */
 public class GadgetRenderingServlet extends HttpServlet {
-  private final GadgetServer gadgetServer;
-  private final GadgetServerConfig serverConfig;
-  private String jsServicePath;
+  private CrossServletState servletState;
   private static final String CAJA_PARAM = "caja";
   private static final String USERPREF_PARAM_PREFIX = "up_";
   private static final String LIBS_PARAM_NAME = "libs";
-  private static final String JS_FILE_SUFFIX = ".js";
-  public static final String DEFAULT_JS_SERVICE_PATH = "js/";
+  private static final Logger logger
+      = Logger.getLogger("org.apache.shindig.gadgets");
 
-  /**
-   * Creates a {@code GadgetRenderingServlet} with default executor,
-   * caches, etc.
-   *
-   * Note that features aren't loaded until init() is called.
-   *
-   * @throws GadgetException If something went wrong during configuration.
-   */
-  public GadgetRenderingServlet() throws GadgetException {
-    serverConfig = new GadgetServerConfig()
-        .setExecutor(Executors.newCachedThreadPool())
-        .setMessageBundleCache(new BasicGadgetDataCache<MessageBundle>())
-        .setSpecCache(new BasicGadgetDataCache<GadgetSpec>())
-        .setContentFetcher(new BasicRemoteContentFetcher(1024 * 1024))
-        .setFeatureRegistry(new GadgetFeatureRegistry(null));
-    gadgetServer = new GadgetServer(serverConfig);
-  }
-
-  /**
-   * Creates a servlet using a pre-configured server. Using this method
-   * will cause init to ignore feature loading parameters.
-   * @param server
-   */
-  public GadgetRenderingServlet(GadgetServer server) {
-    gadgetServer = server;
-    // Set this to null to indicate that all configuration has been done
-    // custom.
-    serverConfig = null;
-  }
 
   @Override
-  public void init(ServletConfig config) {
-    ServletContext context = config.getServletContext();
-    String jsPath = context.getInitParameter("js-service-path");
-    if (jsPath == null) {
-      jsPath = DEFAULT_JS_SERVICE_PATH;
-    }
-    jsServicePath = jsPath;
-    if (serverConfig != null) {
-      // Using the default server.
-      String features = context.getInitParameter("features");
-      try {
-        serverConfig.getFeatureRegistry().registerFeatures(features);
-      } catch (GadgetException e) {
-        e.printStackTrace();
-        System.exit(1);
-      }
-    }
+  public void init(ServletConfig config) throws ServletException {
+    servletState = CrossServletState.get(config);
   }
 
   @Override
@@ -153,11 +102,9 @@
     String view = req.getParameter("view");
     view = (view == null || view.length() == 0) ? GadgetSpec.DEFAULT_VIEW : view;
     try {
-      gadget = gadgetServer.processGadget(gadgetId,
-                                          getPrefsFromRequest(req),
-                                          context.getLocale(),
-                                          RenderingContext.GADGET,
-                                          options);
+      gadget = servletState.getGadgetServer().processGadget(gadgetId,
+          getPrefsFromRequest(req), context.getLocale(),
+          RenderingContext.GADGET, options);
       outputGadget(gadget, view, options, contentFilters, resp);
     } catch (GadgetServer.GadgetProcessException e) {
       outputErrors(e, resp);
@@ -188,12 +135,6 @@
     case URL:
       outputUrlGadget(gadget, options, resp);
       break;
-    // default makes no sense here, as this is an enum, we want to insure that
-    // we cover all cases of the enum, so leave it out.
-//    default:
-//      resp.sendError(HttpServletResponse.SC_BAD_REQUEST,
-//                     "Unexpected reror: unknown gadget type");
-//      break;
     }
   }
 
@@ -218,6 +159,7 @@
 
     StringBuilder markup = new StringBuilder();
     markup.append("<html><head>");
+    // TODO: This is so wrong.
     markup.append("<style type=\"text/css\">" +
                   "body,td,div,span,p{font-family:arial,sans-serif;}" +
                   "a {color:#0000cc;}a:visited {color:#551a8b;}" +
@@ -228,6 +170,7 @@
     StringBuilder externJs = new StringBuilder();
     StringBuilder inlineJs = new StringBuilder();
     String externFmt = "<script src=\"%s\"></script>\n";
+    String forcedLibs = options.getForcedJsLibs();
 
     for (JsLibrary library : gadget.getJsLibraries()) {
       JsLibrary.Type type = library.getType();
@@ -239,18 +182,17 @@
         inlineJs.append(library.getContent()).append('\n');
       } else {
         // FILE or RESOURCE
-        if (options.getForcedJsLibs() == null) {
+        if (forcedLibs == null) {
           inlineJs.append(library.getContent()).append('\n');
         } // otherwise it was already included by options.forceJsLibs.
       }
     }
 
     // Forced libs first.
-    if (options.getForcedJsLibs() != null) {
-      markup.append(String.format(externFmt,
-                                  DEFAULT_JS_SERVICE_PATH +
-                                  options.getForcedJsLibs() +
-                                  JS_FILE_SUFFIX));
+
+    if (forcedLibs != null) {
+      String[] libs = forcedLibs.split(":");
+      markup.append(String.format(externFmt, servletState.getJsUrl(libs)));
     }
 
     if (inlineJs.length() > 0) {
@@ -280,7 +222,7 @@
         }
       }
     }
-    
+
     if (gadgetExceptions.size() > 0) {
       throw new GadgetServer.GadgetProcessException(gadgetExceptions);
     }
@@ -294,28 +236,31 @@
 
   private void outputUrlGadget(Gadget gadget,
       ProcessingOptions options, HttpServletResponse resp) throws IOException {
-    // UserPrefs portion of query string to tack on
     // TODO: generalize this as injectedArgs on Gadget object
+
+    // Preserve existing query string parameters.
+    URI redirURI = gadget.getContentHref();
+    StringBuilder query = new StringBuilder(redirURI.getQuery());
+
     // TODO: userprefs on the fragment rather than query string
-    String prefsQuery = getPrefsQueryString(gadget.getUserPrefValues());
-    String libsQuery = null;
+    query.append(getPrefsQueryString(gadget.getUserPrefValues()));
 
-    if (options.getForcedJsLibs() == null) {
-      libsQuery = getLibsQueryString(gadget.getRequires().keySet());
+    String[] libs;
+    String forcedLibs = options.getForcedJsLibs();
+    if (forcedLibs == null) {
+      libs = (String[])gadget.getRequires().keySet().toArray();
     } else {
-      libsQuery = DEFAULT_JS_SERVICE_PATH +
-                  options.getForcedJsLibs() +
-                  JS_FILE_SUFFIX;
+      libs = forcedLibs.split(":");
     }
+    appendLibsToQuery(libs, query);
 
-    URI redirURI = gadget.getContentHref();
     try {
       redirURI = new URI(redirURI.getScheme(),
                          redirURI.getUserInfo(),
                          redirURI.getHost(),
                          redirURI.getPort(),
                          redirURI.getPath(),
-                         redirURI.getQuery() + prefsQuery + libsQuery,
+                         query.toString(),
                          redirURI.getFragment());
     } catch (URISyntaxException e) {
       // Not really ever going to happen; input values are already OK.
@@ -337,6 +282,10 @@
       markup.append(' ');
       markup.append(error.getMessage());
       markup.append('\n');
+
+      // Log the errors here for now. We might want different severity levels
+      // for different error codes.
+      logger.log(Level.INFO, "Failed to render gadget", error);
     }
     markup.append("</pre>");
     markup.append("</body></html>");
@@ -363,9 +312,9 @@
       buf.append('&');
       try {
         buf.append(USERPREF_PARAM_PREFIX)
-               .append(URLEncoder.encode(prefEntry.getKey(), "UTF8"))
-               .append('=')
-               .append(URLEncoder.encode(prefEntry.getValue(), "UTF8"));
+           .append(URLEncoder.encode(prefEntry.getKey(), "UTF8"))
+           .append("=")
+           .append(URLEncoder.encode(prefEntry.getValue(), "UTF8"));
       } catch (UnsupportedEncodingException e) {
         // If UTF8 is somehow not supported, we may as well bail.
         // Not a whole lot we can do without such support.
@@ -375,26 +324,16 @@
     return buf.toString();
   }
 
-  private String getLibsQueryString(Set<String> features) {
-    StringBuilder buf = new StringBuilder();
-    buf.append('&').append(LIBS_PARAM_NAME).append('=');
-    buf.append(jsServicePath);
-    if (features.size() == 0) {
-      buf.append("core");
-    } else {
-      boolean first = true;
-      for (String feature : features) {
-        if (first) {
-          first = false;
-        } else {
-          buf.append(':');
-        }
-        buf.append(feature);
-      }
-    }
-    buf.append(JS_FILE_SUFFIX);
-
-    return buf.toString();
+  /**
+   * Appends libs to the query string.
+   * @param libs
+   * @param query
+   */
+  private void appendLibsToQuery(String[] libs, StringBuilder query) {
+    query.append("&")
+         .append(LIBS_PARAM_NAME)
+         .append("=")
+         .append(servletState.getJsUrl(libs));
   }
 
   /**

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/JsServlet.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/JsServlet.java?rev=619684&r1=619683&r2=619684&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/JsServlet.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/JsServlet.java Thu Feb  7 15:18:30 2008
@@ -13,7 +13,6 @@
  */
 package org.apache.shindig.gadgets.http;
 
-import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.GadgetFeatureFactory;
 import org.apache.shindig.gadgets.GadgetFeatureRegistry;
 import org.apache.shindig.gadgets.JsLibrary;
@@ -26,7 +25,7 @@
 import java.util.Set;
 
 import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -36,42 +35,24 @@
  * Used by type=URL gadgets in loading JavaScript resources.
  */
 public class JsServlet extends HttpServlet {
-  private GadgetFeatureRegistry registry = null;
-
-  /**
-   * Create a JsServlet using a pre-configured feature registry.
-   * @param registry
-   */
-  public JsServlet(GadgetFeatureRegistry registry) {
-    this.registry = registry;
-  }
-
-  /**
-   * Creates a JsServlet without a default registry; the registry will be
-   * created automatically when init is called.
-   */
-  public JsServlet() {
-    registry = null;
-  }
+  private CrossServletState servletState;
+  private static final long START_TIME = System.currentTimeMillis();
 
   @Override
-  public void init(ServletConfig config) {
-    ServletContext context = config.getServletContext();
-
-    if (registry == null) {
-      String features = context.getInitParameter("features");
-      try {
-        registry = new GadgetFeatureRegistry(features);
-      } catch (GadgetException e) {
-        e.printStackTrace();
-        throw new RuntimeException(e);
-      }
-    }
+  public void init(ServletConfig config) throws ServletException {
+    servletState = CrossServletState.get(config);
   }
 
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp)
       throws IOException {
+    // If an If-Modified-Since header is ever provided, we always say
+    // not modified. This is because when there actually is a change,
+    // cache busting should occur.
+    if (req.getHeader("If-Modified-Since") != null) {
+      resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+      return;
+    }
     // Use the last component as filename; prefix is ignored
     String uri = req.getRequestURI();
     // We only want the file name part. There will always be at least 1 slash
@@ -94,6 +75,8 @@
         = new HashSet<GadgetFeatureRegistry.Entry>();
     Set<String> missing = new HashSet<String>();
 
+    GadgetFeatureRegistry registry
+        = servletState.getGadgetServer().getConfig().getFeatureRegistry();
     if (registry.getIncludedFeatures(needed, found, missing)) {
       String containerParam = req.getParameter("c");
       RenderingContext context;
@@ -105,6 +88,8 @@
 
       Set<GadgetFeatureRegistry.Entry> done
           = new HashSet<GadgetFeatureRegistry.Entry>();
+
+      // TODO: This doesn't work correctly under JDK 1.5, but it does under 1.6
       do {
         for (GadgetFeatureRegistry.Entry entry : found) {
           if (!done.contains(entry) &&
@@ -145,8 +130,15 @@
    * @param response The HTTP response
    */
   private void setCachingHeaders(HttpServletResponse response) {
-    response.setHeader("Cache-Control", "public,max-age=2592000");
-    response.setDateHeader("Expires", System.currentTimeMillis()
-                                     + 2592000000L);
+
+    // Most browsers accept this. 2030 is the last round year before
+    // the end of time.
+    response.setHeader("Expires", "Tue, 01 Jan 2030 00:00:01 GMT");
+
+    // IE seems to need this (10 years should be enough).
+    response.setHeader("Cache-Control", "public,max-age=315360000");
+
+    // Firefox requires this for certain cases.
+    response.setDateHeader("Last-Modified", START_TIME);
   }
 }

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=619684&r1=619683&r2=619684&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 Thu Feb  7 15:18:30 2008
@@ -18,7 +18,6 @@
  */
 package org.apache.shindig.gadgets.http;
 
-import org.apache.shindig.gadgets.BasicRemoteContentFetcher;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.GadgetSigner;
 import org.apache.shindig.gadgets.GadgetToken;
@@ -48,13 +47,9 @@
   private static final int TWO_HOURS_IN_MS = 7200000;
   private static final int ONE_HOUR_IN_SECS = 3600;
   private static final int MAX_PROXY_SIZE = 1024 * 1024;
-  
+
   private final RemoteContentFetcher fetcher;
 
-  public ProxyHandler() {
-    this(new BasicRemoteContentFetcher(MAX_PROXY_SIZE));
-  }
-  
   public ProxyHandler(RemoteContentFetcher fetcher) {
     this.fetcher = fetcher;
   }
@@ -203,7 +198,9 @@
   private GadgetToken extractAndValidateToken(HttpServletRequest request,
       GadgetSigner signer) throws ServletException {
     try {
-      if (signer == null) return null;
+      if (signer == null) {
+        return null;
+      }
       String token = request.getParameter("st");
       if (token == null) {
         token = "";
@@ -229,7 +226,7 @@
 
   /**
    * Sign a URL with a GadgetToken if needed
-   * @return 
+   * @return
    */
   private URL signUrl(URL originalUrl, GadgetToken token,
       HttpServletRequest request) throws ServletException {
@@ -238,7 +235,7 @@
           !"signed".equals(request.getParameter("authz"))) {
         return originalUrl;
       }
-      return token.signUrl(originalUrl, "GET", // TODO: request.getMethod() 
+      return token.signUrl(originalUrl, "GET", // TODO: request.getMethod()
           request.getParameterMap());
     } catch (GadgetException ge) {
       throw new ServletException(ge);

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyServlet.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyServlet.java?rev=619684&r1=619683&r2=619684&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyServlet.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyServlet.java Thu Feb  7 15:18:30 2008
@@ -18,28 +18,38 @@
  */
 package org.apache.shindig.gadgets.http;
 
-import org.apache.shindig.gadgets.BasicGadgetSigner;
-import org.apache.shindig.gadgets.GadgetSigner;
+import org.apache.shindig.gadgets.GadgetServerConfigReader;
 
 import java.io.IOException;
 
+import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 public class ProxyServlet extends HttpServlet {
-  private final GadgetSigner signer;
-  private final ProxyHandler handler;
+  private CrossServletState servletState;
+  private ProxyHandler proxyHandler;
+
+  @Override
+  public void init(ServletConfig config) throws ServletException {
+    servletState = CrossServletState.get(config);
+    GadgetServerConfigReader serverConfig
+        = servletState.getGadgetServer().getConfig();
+    proxyHandler = new ProxyHandler(serverConfig.getContentFetcher());
+  }
 
   @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {
     String output = request.getParameter("output");
     if ("js".equals(output)) {
-      handler.fetchJson(request, response, signer);
+      proxyHandler.fetchJson(
+          request, response, servletState.getGadgetSigner(request));
     } else {
-      handler.fetch(request, response, signer);
+      proxyHandler.fetch(
+          request, response, servletState.getGadgetSigner(request));
     }
   }
 
@@ -48,22 +58,5 @@
       throws ServletException, IOException {
     // Currently they are identical
     doGet(request, response);
-  }
-
-  /**
-   * Constructs a ProxyServlet with the default (non-secure) GadgetSigner.
-   */
-  public ProxyServlet() {
-    this(new BasicGadgetSigner(), new ProxyHandler());
-  }
-
-  /**
-   * Creates a ProxyServlet using the specified GadgetSigner.
-   * @param signer Used to sign and verify requests
-   * @param handler Used to fetch proxied content
-   */
-  public ProxyServlet(GadgetSigner signer, ProxyHandler handler) {
-    this.signer = signer;
-    this.handler = handler;
   }
 }

Added: 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=619684&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/RpcServlet.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/RpcServlet.java Thu Feb  7 15:18:30 2008
@@ -0,0 +1,48 @@
+/*
+ * 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.http;
+
+import java.io.IOException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Handles RPC metadata requests.
+ */
+public class RpcServlet extends HttpServlet {
+  private CrossServletState state;
+
+  @Override
+  protected void doGet(HttpServletRequest request, HttpServletResponse response)
+      throws ServletException, IOException {
+  }
+
+  @Override
+  public void init(ServletConfig config) throws ServletException {
+    state = CrossServletState.get(config);
+  }
+
+  public RpcServlet() {
+  }
+}

Modified: incubator/shindig/trunk/java/gadgets/src/main/webapp/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/webapp/WEB-INF/web.xml?rev=619684&r1=619683&r2=619684&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/webapp/WEB-INF/web.xml (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/webapp/WEB-INF/web.xml Thu Feb  7 15:18:30 2008
@@ -19,48 +19,63 @@
         "http://java.sun.com/dtd/web-app_2_3.dtd">
 
 <web-app>
-    <!-- configuration -->
-    <context-param>
-        <param-name>features</param-name>
-        <param-value>res://features/features.txt</param-value>
-    </context-param>
-
-    <!-- Render a Gadget -->
-    <servlet>
-        <servlet-name>xml-to-html</servlet-name>
-        <servlet-class>
-            org.apache.shindig.gadgets.http.GadgetRenderingServlet
-        </servlet-class>
-    </servlet>
-
-    <!-- Proxy -->
-    <servlet>
-        <servlet-name>proxy</servlet-name>
-        <servlet-class>
-            org.apache.shindig.gadgets.http.ProxyServlet
-        </servlet-class>
-    </servlet>
-
-
-    <!-- javascript serving -->
-    <servlet>
-        <servlet-name>js</servlet-name>
-        <servlet-class>org.apache.shindig.gadgets.http.JsServlet</servlet-class>
-    </servlet>
-
-    <servlet-mapping>
-        <servlet-name>js</servlet-name>
-        <url-pattern>/js/*</url-pattern>
-    </servlet-mapping>
-
-    <servlet-mapping>
-        <servlet-name>proxy</servlet-name>
-        <url-pattern>/proxy</url-pattern>
-    </servlet-mapping>
-
-    <servlet-mapping>
-        <servlet-name>xml-to-html</servlet-name>
-        <url-pattern>/ifr</url-pattern>
-    </servlet-mapping>
-
+  <!-- configuration -->
+  <context-param>
+    <param-name>features</param-name>
+    <param-value>res://features/features.txt</param-value>
+  </context-param>
+  <context-param>
+    <param-name>servlet-data-class</param-name>
+    <param-value>org.apache.shindig.gadgets.http.DefaultCrossServletState</param-value>
+  </context-param>
+  
+  <!-- Render a Gadget -->
+  <servlet>
+    <servlet-name>xml-to-html</servlet-name>
+    <servlet-class>
+      org.apache.shindig.gadgets.http.GadgetRenderingServlet
+    </servlet-class>
+  </servlet>
+
+  <!-- Proxy -->
+  <servlet>
+    <servlet-name>proxy</servlet-name>
+    <servlet-class>
+      org.apache.shindig.gadgets.http.ProxyServlet
+    </servlet-class>
+  </servlet>
+
+  <!-- Metadata RPC -->
+  <servlet>
+    <servlet-name>rpc</servlet-name>
+    <servlet-class>
+      org.apache.shindig.gadgets.http.RpcServlet
+    </servlet-class>
+  </servlet>
+
+  <!-- javascript serving -->
+  <servlet>
+    <servlet-name>js</servlet-name>
+    <servlet-class>org.apache.shindig.gadgets.http.JsServlet</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>js</servlet-name>
+    <url-pattern>/js/*</url-pattern>
+  </servlet-mapping>
+
+  <servlet-mapping>
+    <servlet-name>proxy</servlet-name>
+    <url-pattern>/proxy</url-pattern>
+  </servlet-mapping>
+
+  <servlet-mapping>
+    <servlet-name>xml-to-html</servlet-name>
+    <url-pattern>/ifr</url-pattern>
+  </servlet-mapping>
+ 
+  <servlet-mapping>
+    <servlet-name>rpc</servlet-name>
+    <url-pattern>/rpc</url-pattern>
+  </servlet-mapping>
 </web-app>

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java?rev=619684&r1=619683&r2=619684&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java Thu Feb  7 15:18:30 2008
@@ -27,29 +27,48 @@
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.isA;
 
-import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
 public class GadgetServerTest extends EasyMockTestCase {
-  final Executor executor = Executors.newCachedThreadPool();
-  final GadgetServer gadgetServer;
+  GadgetServer gadgetServer;
   final RemoteContentFetcher fetcher = mock(RemoteContentFetcher.class);
   @SuppressWarnings(value="unchecked")
   final GadgetDataCache<GadgetSpec> specCache = mock(GadgetDataCache.class);
   @SuppressWarnings(value="unchecked")
   final GadgetDataCache<MessageBundle> bundleCache = mock(GadgetDataCache.class);
 
-  public GadgetServerTest() throws GadgetException {
-    // TODO: need to test for configuration errors.
-    gadgetServer = new GadgetServer(new GadgetServerConfig()
-        .setExecutor(executor)
-        .setSpecCache(specCache)
-        .setMessageBundleCache(bundleCache)
-        .setFeatureRegistry(new GadgetFeatureRegistry(null))
-        .setContentFetcher(fetcher));
+  void initServer(GadgetServerConfigReader customConfig) throws GadgetException {
+    GadgetServerConfig config = new GadgetServerConfig();
+
+    if (customConfig != null) {
+      config.copyFrom(customConfig);
+    }
+
+    if (config.getExecutor() == null) {
+      config.setExecutor(Executors.newCachedThreadPool());
+    }
+
+    if (config.getSpecCache() == null) {
+      config.setSpecCache(specCache);
+    }
+
+    if (config.getMessageBundleCache() == null) {
+      config.setMessageBundleCache(bundleCache);
+    }
+
+    if (config.getFeatureRegistry() == null) {
+      config.setFeatureRegistry(new GadgetFeatureRegistry(null));
+    }
+
+    if (config.getContentFetcher() == null) {
+      config.setContentFetcher(fetcher);
+    }
+
+    gadgetServer = new GadgetServer(config);
   }
 
   public void testGadgetSpecNotInCache() throws Exception {
+    initServer(null);
     RemoteContent results = new RemoteContent(200, DATETIME_XML.getBytes(), null);
     ProcessingOptions options = new ProcessingOptions();
 
@@ -65,6 +84,7 @@
   }
 
   public void testGadgetSpecInCache() throws Exception {
+    initServer(null);
     expect(specCache.get(eq(DATETIME_URI_STRING))).andReturn(DATETIME_SPEC);
     replay();
 
@@ -75,6 +95,7 @@
   }
 
   public void testBasicGadget() throws Exception {
+    initServer(null);
     RemoteContent results = new RemoteContent(200, DATETIME_XML.getBytes(), null);
     ProcessingOptions options = new ProcessingOptions();
 
@@ -93,8 +114,7 @@
 
   public void testBlacklistedGadget() throws Exception {
     GadgetBlacklist blacklist = mock(GadgetBlacklist.class);
-    gadgetServer.setGadgetBlacklist(blacklist);
-
+    initServer(new GadgetServerConfig().setGadgetBlacklist(blacklist));
     expect(specCache.get(eq(DATETIME_URI_STRING))).andReturn(null);
     expect(blacklist.isBlacklisted(eq(DATETIME_URI))).andReturn(true);
     replay();