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);
}
}