You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by hs...@apache.org on 2010/09/04 18:34:00 UTC

svn commit: r992634 - in /shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet: JsHandler.java JsServlet.java

Author: hsaputra
Date: Sat Sep  4 16:33:59 2010
New Revision: 992634

URL: http://svn.apache.org/viewvc?rev=992634&view=rev
Log:
Separate JS content processing logic from JsServlet.

Codereview: http://codereview.appspot.com/2104046/


Added:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsHandler.java
Modified:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsHandler.java?rev=992634&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsHandler.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsHandler.java Sat Sep  4 16:33:59 2010
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.gadgets.servlet;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.shindig.common.JsonSerializer;
+import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.RenderingContext;
+import org.apache.shindig.gadgets.config.ConfigContributor;
+import org.apache.shindig.gadgets.features.FeatureRegistry;
+import org.apache.shindig.gadgets.features.FeatureResource;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * Provide processing logic for the JsServlet to serve the JavsScript as features request.
+ * This class will allow separation of flow and serving logic for easy customization.
+ * 
+ */
+@Singleton
+public class JsHandler {
+  protected final FeatureRegistry registry;
+  protected final ContainerConfig containerConfig;
+  protected final Map<String, ConfigContributor> configContributors;
+
+  @Inject
+  public JsHandler(FeatureRegistry registry, ContainerConfig containerConfig,
+      Map<String, ConfigContributor> configContributors) {
+    this.registry = registry;
+    this.containerConfig = containerConfig;
+    this.configContributors = configContributors;
+  }
+  
+  /**
+   * Get the JavaScript content from the feature name aliases.
+   *
+   * @param req the HttpRequest object.
+   * @return JsHandlerResponse object that contains JavaScript data and cacheable flag.
+   */
+  public JsHandlerResponse getJsContent(final HttpServletRequest req) {   
+    // get the Set of features needed from request
+    Set<String> needed = getFeaturesNeeded(req);
+
+    // get the GadgetContext instance
+    GadgetContext ctx = getGadgetContext(req);
+
+    // get js data from feature resoources.
+    return getFeatureResourcesContent(req, ctx, needed);
+  }
+
+  /**
+   * Get the Set of feature names from the request.
+   *
+   * @param req the HttpServletRequest object.
+   * @return Set of names of needed JavaScript as feature aliases from the request.
+   */
+  protected Set<String> getFeaturesNeeded(final HttpServletRequest req) {
+    // 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
+    // (the server root), so this is always safe.
+    String resourceName = uri.substring(uri.lastIndexOf('/') + 1);
+    if (resourceName.endsWith(".js")) {
+      // Lop off the suffix for lookup purposes
+      resourceName = resourceName.substring(0, resourceName.length() - ".js".length());
+    }
+
+    Set<String> needed = ImmutableSet.copyOf(StringUtils.split(resourceName, ':'));
+    return needed;
+  }
+
+  /**
+   * Get the GadgetContext to be used when calling FeatureRegistry.getFeatureResources.
+   * 
+   * @param req the HttpServletRequest object.
+   * @return GadgetContext instance.
+   */
+  protected GadgetContext getGadgetContext(final HttpServletRequest req) {
+    String containerParam = req.getParameter("container");
+    String containerStr = req.getParameter("c");
+
+    final RenderingContext context = "1".equals(containerStr) ?
+        RenderingContext.CONTAINER : RenderingContext.GADGET;
+    final String container =
+        containerParam != null ? containerParam : ContainerConfig.DEFAULT_CONTAINER;
+
+    return new JsGadgetContext(context, container);
+  }
+
+  /**
+   * Get the content of the feature resources and push it to jsData.
+   * 
+   * @param req The HttpServletRequest object.
+   * @param ctx GadgetContext object.
+   * @param needed Set of requested feature names.
+   * @return JsHandlerResponse object that contains JavaScript data and cacheable flag.
+   */
+  protected JsHandlerResponse getFeatureResourcesContent(final HttpServletRequest req,
+      final GadgetContext ctx, Set<String> needed) {
+    StringBuilder jsData = new StringBuilder();
+    Collection<? extends FeatureResource> resources =
+        registry.getFeatureResources(ctx, needed, null);
+    String debugStr = req.getParameter("debug");
+    boolean debug = "1".equals(debugStr);
+    String container = ctx.getContainer();
+    boolean isProxyCacheable = true;
+
+    for (FeatureResource featureResource : resources) {
+      String content = debug ? featureResource.getDebugContent() : featureResource.getContent();
+      if (!featureResource.isExternal()) {
+        jsData.append(content);
+      } else {
+        // Support external/type=url feature serving through document.write()
+        jsData.append("document.write('<script src=\"").append(content).append("\"></script>')");
+      }
+      isProxyCacheable = isProxyCacheable && featureResource.isProxyCacheable();
+      jsData.append(";\n");
+    }
+
+    if (ctx.getRenderingContext() == RenderingContext.CONTAINER) {
+      // Append some container specific things
+      Map<String, Object> features = containerConfig.getMap(container, "gadgets.features");
+      Map<String, Object> config =
+          Maps.newHashMapWithExpectedSize(features == null ? 2 : features.size() + 2);
+
+      if (features != null) {
+        // Discard what we don't care about.
+        for (String name : registry.getFeatures(needed)) {
+          Object conf = features.get(name);
+          // Add from containerConfig
+          if (conf != null) {
+            config.put(name, conf);
+          }
+          ConfigContributor contributor = configContributors.get(name);
+          if (contributor != null) {
+            contributor.contribute(config, container, req.getHeader("Host"));
+          }
+        }
+        jsData.append("gadgets.config.init(").append(JsonSerializer.serialize(config)).append(");\n");
+      }
+    }
+    return new JsHandlerResponse(jsData, isProxyCacheable);
+  }
+
+  /**
+   * Define the response data from JsHandler.
+   */
+  public static class JsHandlerResponse {
+    private final boolean isProxyCacheable;
+    private final StringBuilder jsData;
+
+    public JsHandlerResponse (StringBuilder jsData, boolean isProxyCacheable) {
+      this.jsData = jsData;
+      this.isProxyCacheable = isProxyCacheable;
+    }
+
+    public boolean isProxyCacheable() {
+      return isProxyCacheable;
+    }
+
+    public StringBuilder getJsData() {
+      return jsData;
+    }
+  }
+
+  /**
+   * GadgetContext for JsHandler called by FeatureRegistry when fetching the resources.
+   */
+  protected static class JsGadgetContext extends GadgetContext {
+    private final RenderingContext renderingContext;
+    private final String container;
+
+    public JsGadgetContext(RenderingContext renderingContext, String container) {
+      this.renderingContext = renderingContext;
+      this.container = container;
+    }
+    
+    @Override
+    public RenderingContext getRenderingContext() {
+      return renderingContext;
+    }
+
+    @Override
+    public String getContainer() {
+      return container;
+    }
+  }
+}

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java?rev=992634&r1=992633&r2=992634&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java Sat Sep  4 16:33:59 2010
@@ -17,32 +17,18 @@
  */
 package org.apache.shindig.gadgets.servlet;
 
-import com.google.common.collect.ImmutableSet;
-
-import com.google.common.collect.Maps;
-
 import org.apache.commons.lang.StringEscapeUtils;
-import org.apache.commons.lang.StringUtils;
-import org.apache.shindig.common.JsonSerializer;
+
 import org.apache.shindig.common.servlet.HttpUtil;
 import org.apache.shindig.common.servlet.InjectedServlet;
 import org.apache.shindig.common.uri.UriBuilder;
-import org.apache.shindig.config.ContainerConfig;
-import org.apache.shindig.gadgets.GadgetContext;
 import org.apache.shindig.gadgets.GadgetException;
-import org.apache.shindig.gadgets.RenderingContext;
-import org.apache.shindig.gadgets.config.ConfigContributor;
-import org.apache.shindig.gadgets.features.FeatureRegistry;
-import org.apache.shindig.gadgets.features.FeatureResource;
 import org.apache.shindig.gadgets.uri.JsUriManager;
 import org.apache.shindig.gadgets.uri.UriStatus;
 
 import com.google.inject.Inject;
 
 import java.io.IOException;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
 import java.util.regex.Pattern;
 
 import javax.servlet.ServletConfig;
@@ -66,15 +52,13 @@ public class JsServlet extends InjectedS
       "})();";
   private static final Pattern ONLOAD_FN_PATTERN = Pattern.compile("[a-zA-Z0-9_]+");
 
-  private transient FeatureRegistry registry;
+  private transient JsHandler jsHandler;
   private transient JsUriManager jsUriManager;
-  private transient ContainerConfig containerConfig;
-  private transient Map<String, ConfigContributor> configContributors;
 
   @Inject
-  public void setRegistry(FeatureRegistry registry) {
+  public void setJsHandler(JsHandler jsHandler) {
     checkInitialized();
-    this.registry = registry;
+    this.jsHandler = jsHandler;
   }
 
   @Inject
@@ -82,19 +66,7 @@ public class JsServlet extends InjectedS
     checkInitialized();
     this.jsUriManager = jsUriManager;
   }
-
-  @Inject
-  public void setContainerConfig(ContainerConfig containerConfig) {
-    checkInitialized();
-    this.containerConfig = containerConfig;
-  }
-
-  @Inject
-  public void setConfigContributors(Map<String, ConfigContributor> configContributors) {
-    checkInitialized();
-    this.configContributors = configContributors;
-  }
-
+  
   @Override
   public void init(ServletConfig config) throws ServletException {
     super.init(config);
@@ -106,7 +78,7 @@ public class JsServlet extends InjectedS
     // 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.
-    UriStatus vstatus;
+    UriStatus vstatus = null;
     try {
       vstatus = jsUriManager.processExternJsUri(new UriBuilder(req).toUri()).getStatus();
     } catch (GadgetException e) {
@@ -119,79 +91,12 @@ public class JsServlet extends InjectedS
       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
-    // (the server root), so this is always safe.
-    String resourceName = uri.substring(uri.lastIndexOf('/') + 1);
-    if (resourceName.endsWith(".js")) {
-      // Lop off the suffix for lookup purposes
-      resourceName = resourceName.substring(
-          0, resourceName.length() - ".js".length());
-    }
-
-    Set<String> needed = ImmutableSet.copyOf(StringUtils.split(resourceName, ':'));
-
-    String debugStr = req.getParameter("debug");
-    String containerParam = req.getParameter("container");
-    String containerStr = req.getParameter("c");
-
-    boolean debug = "1".equals(debugStr);
-    final RenderingContext context = "1".equals(containerStr) ?
-        RenderingContext.CONTAINER : RenderingContext.GADGET;
-    final String container =
-        containerParam != null ? containerParam : ContainerConfig.DEFAULT_CONTAINER;
-
-    GadgetContext ctx = new GadgetContext() {
-      @Override
-      public RenderingContext getRenderingContext() {
-        return context;
-      }
-
-      @Override
-      public String getContainer() {
-        return container;
-      }
-    };
-
-    Collection<? extends FeatureResource> resources = registry.getFeatureResources(ctx, needed, null);
-    StringBuilder jsData = new StringBuilder();
-    boolean isProxyCacheable = true;
-    for (FeatureResource featureResource : resources) {
-      String content = debug ? featureResource.getDebugContent() : featureResource.getContent();
-      if (!featureResource.isExternal()) {
-        jsData.append(content);
-      } else {
-        // Support external/type=url feature serving through document.write()
-        jsData.append("document.write('<script src=\"").append(content).append("\"></script>')");
-      }
-      isProxyCacheable = isProxyCacheable && featureResource.isProxyCacheable();
-      jsData.append(";\n");
-    }
-
-    if (context == RenderingContext.CONTAINER) {
-      // Append some container specific things
-
-      Map<String, Object> features = containerConfig.getMap(container, "gadgets.features");
-      Map<String, Object> config = Maps.newHashMapWithExpectedSize(features == null ? 2 : features.size() + 2);
-
-      if (features != null) {
-        // Discard what we don't care about.
-        for (String name : registry.getFeatures(needed)) {
-          Object conf = features.get(name);
-          // Add from containerConfig
-          if (conf != null) {
-            config.put(name, conf);
-          }
-          ConfigContributor contributor = configContributors.get(name);
-          if (contributor != null) {
-            contributor.contribute(config, container, req.getHeader("Host"));
-          }
-        }
-        jsData.append("gadgets.config.init(").append(JsonSerializer.serialize(config)).append(");\n");
-      }
-    }
+    // Get JavaScript content from features aliases request.
+    JsHandler.JsHandlerResponse handlerResponse = jsHandler.getJsContent(req);
+    StringBuilder jsData = handlerResponse.getJsData();
+    boolean isProxyCacheable = handlerResponse.isProxyCacheable();
 
+    // Add onload handler to add callback function.
     String onloadStr = req.getParameter("onload");
     if (onloadStr != null) {
       if (!ONLOAD_FN_PATTERN.matcher(onloadStr).matches()) {
@@ -206,6 +111,25 @@ public class JsServlet extends InjectedS
       return;
     }
 
+    // post JavaScript content fetching
+    postJsContentProcessing(resp, vstatus, isProxyCacheable);
+
+    resp.setContentType("text/javascript; charset=utf-8");
+    byte[] response = jsData.toString().getBytes("UTF-8");
+    resp.setContentLength(response.length);
+    resp.getOutputStream().write(response);
+  }
+
+  /**
+   * Provides post JavaScript content processing. The default behavior will check the UriStatus and
+   * update the response header with cache option.
+   * 
+   * @param resp The HttpServeltResponse object.
+   * @param vstatus The UriStatus object.
+   * @param isProxyCacheable boolean true if content is cacheable and false otherwise.
+   */
+  protected void postJsContentProcessing(HttpServletResponse resp, UriStatus vstatus,
+      boolean isProxyCacheable) {
     switch (vstatus) {
       case VALID_VERSIONED:
         // Versioned files get cached indefinitely
@@ -221,9 +145,5 @@ public class JsServlet extends InjectedS
         HttpUtil.setNoCache(resp);
         break;
     }
-    resp.setContentType("text/javascript; charset=utf-8");
-    byte[] response = jsData.toString().getBytes("UTF-8");
-    resp.setContentLength(response.length);
-    resp.getOutputStream().write(response);
   }
 }