You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by jo...@apache.org on 2011/01/26 03:16:44 UTC

svn commit: r1063571 - in /shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/js/ test/java/org/apache/shindig/gadgets/js/

Author: johnh
Date: Wed Jan 26 02:16:43 2011
New Revision: 1063571

URL: http://svn.apache.org/viewvc?rev=1063571&view=rev
Log:
Part #2, adding /js/ packages missed in the first commit (r1063570):

Refactor HTTP JS serving into a pluggable pipeline, to break up JsServlet's ad hoc/monolithic functional nature. Adds additional testing along with inherent capability for code augmentation. JsHandler, producing core code, remains untouched so it may be shared with metadata JS service.

Patch provided by Jacobo Tarrio.


Added:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddOnloadFunctionProcessor.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/DefaultJsProcessorRegistry.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/DefaultJsServingPipeline.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/GetJsContentProcessor.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/IfModifiedSinceProcessor.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JavascriptModule.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsException.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsLoadProcessor.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsProcessor.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsProcessorRegistry.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsRequest.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsRequestBuilder.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponse.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponseBuilder.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipeline.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddOnloadFunctionProcessorTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/DefaultJsProcessorRegistryTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/DefaultJsServingPipelineTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/GetJsContentProcessorTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/IfModifiedSinceProcessorTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/JsLoadProcessorTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/JsRequestBuilderTest.java

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddOnloadFunctionProcessor.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddOnloadFunctionProcessor.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddOnloadFunctionProcessor.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/AddOnloadFunctionProcessor.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,66 @@
+/*
+ * 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.js;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import org.apache.commons.lang.StringEscapeUtils;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Adds code to call a function after the Javascript code has been interpreted.
+ */
+public class AddOnloadFunctionProcessor implements JsProcessor {
+
+  @VisibleForTesting
+  public static final String ONLOAD_FUNCTION_NAME_ERROR = "Invalid onload callback specified";
+
+  @VisibleForTesting
+  public static final String ONLOAD_JS_TPL = "(function() {" +
+      "var nm='%s';" +
+      "if (typeof window[nm]==='function') {" +
+      "window[nm]();" +
+      '}' +
+      "})();";
+
+  private static final Pattern ONLOAD_FN_PATTERN = Pattern.compile("[a-zA-Z0-9_]+");
+
+  public boolean process(JsRequest request, JsResponseBuilder builder)
+      throws JsException {
+    // Add onload handler to add callback function.
+    String onloadStr = request.getJsUri().getOnload();
+    if (onloadStr != null) {
+      if (!ONLOAD_FN_PATTERN.matcher(onloadStr).matches()) {
+        throw new JsException(HttpServletResponse.SC_BAD_REQUEST, ONLOAD_FUNCTION_NAME_ERROR);
+      }
+      builder.addJsCode(createOnloadScript(onloadStr));
+    }
+    return true;
+  }
+  
+  
+  @VisibleForTesting
+  String createOnloadScript(String function) {
+    return String.format(ONLOAD_JS_TPL, StringEscapeUtils.escapeJavaScript(function));
+  }
+
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/DefaultJsProcessorRegistry.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/DefaultJsProcessorRegistry.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/DefaultJsProcessorRegistry.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/DefaultJsProcessorRegistry.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,45 @@
+/*
+ * 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.js;
+
+import com.google.inject.Inject;
+
+import java.util.List;
+
+/**
+ * Default implementation of {@link JsProcessorRegistry}, using an injected list
+ * of processors.
+ */
+public class DefaultJsProcessorRegistry implements JsProcessorRegistry {
+
+  private final List<JsProcessor> processors;
+
+  @Inject
+  public DefaultJsProcessorRegistry(List<JsProcessor> processors) {
+    this.processors = processors;
+  }
+
+  public void process(JsRequest request, JsResponseBuilder response) throws JsException {
+    for (JsProcessor processor : processors) {
+      if (!processor.process(request, response)) {
+        break;
+      }
+    }
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/DefaultJsServingPipeline.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/DefaultJsServingPipeline.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/DefaultJsServingPipeline.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/DefaultJsServingPipeline.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,44 @@
+/*
+ * 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.js;
+
+import com.google.inject.Inject;
+
+/**
+ * Default implementation of {@link JsServingPipeline}.
+ *
+ * The processing steps are executed by a {@link JsProcessorRegistry}, which can
+ * be configured or replaced to add and remove processing steps, or to execute
+ * different processing steps depending on the context.
+ */
+public class DefaultJsServingPipeline implements JsServingPipeline {
+
+  private final JsProcessorRegistry jsProcessorRegistry;
+
+  @Inject
+  public DefaultJsServingPipeline(JsProcessorRegistry jsProcessorRegistry) {
+    this.jsProcessorRegistry = jsProcessorRegistry;
+  }
+
+  public JsResponse execute(JsRequest jsRequest) throws JsException {
+    JsResponseBuilder resp = new JsResponseBuilder();    
+    jsProcessorRegistry.process(jsRequest, resp);
+    return resp.build();
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/GetJsContentProcessor.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/GetJsContentProcessor.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/GetJsContentProcessor.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/GetJsContentProcessor.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,74 @@
+/*
+ * 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.js;
+
+import com.google.inject.Inject;
+
+import org.apache.shindig.gadgets.servlet.JsHandler;
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+import org.apache.shindig.gadgets.uri.UriStatus;
+
+/**
+ * Retrieves the requested Javascript code using a {@link JsHandler}.
+ */
+public class GetJsContentProcessor implements JsProcessor {
+
+  private final JsHandler jsHandler;
+
+  @Inject
+  public GetJsContentProcessor(JsHandler jsHandler) {
+    this.jsHandler = jsHandler;
+  }
+  
+  public boolean process(JsRequest request, JsResponseBuilder builder) {
+    // Get JavaScript content from features aliases request.
+    JsUri jsUri = request.getJsUri();
+    JsHandler.Response handlerResponse =
+        jsHandler.getJsContent(jsUri, request.getHost());
+    builder.addJsCode(handlerResponse.getJsData());
+    builder.setProxyCacheable(handlerResponse.isProxyCacheable());    
+    setResponseCacheTtl(builder, jsUri.getStatus());
+    return true;
+  }
+
+  /**
+   * Sets the cache TTL depending on the value of the {@link UriStatus} object.
+   *
+   * @param resp The {@link JsResponseBuilder} object.
+   * @param vstatus The {@link UriStatus} object.
+   */
+  protected void setResponseCacheTtl(JsResponseBuilder resp, UriStatus vstatus) {
+    switch (vstatus) {
+      case VALID_VERSIONED:
+        // Versioned files get cached indefinitely
+        resp.setCacheTtlSecs(-1);
+        break;
+      case VALID_UNVERSIONED:
+        // Unversioned files get cached for 1 hour.
+        resp.setCacheTtlSecs(60 * 60);
+        break;
+      case INVALID_VERSION:
+        // URL is invalid in some way, likely version mismatch.
+        // Indicate no-cache forcing subsequent requests to regenerate URLs.
+        resp.setCacheTtlSecs(0);
+        break;
+    }
+  }
+
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/IfModifiedSinceProcessor.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/IfModifiedSinceProcessor.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/IfModifiedSinceProcessor.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/IfModifiedSinceProcessor.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,41 @@
+/*
+ * 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.js;
+
+import org.apache.shindig.gadgets.uri.UriStatus;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Returns a 304 Not Modified response if the request is for a versioned
+ * resource and contains an If-Modified-Since header. This works in this way
+ * because we rely on cache busting for versioned resources.
+ */
+public class IfModifiedSinceProcessor implements JsProcessor {
+
+  public boolean process(JsRequest request, JsResponseBuilder builder) {
+    if (request.isInCache() &&
+        request.getJsUri().getStatus() == UriStatus.VALID_VERSIONED) {
+      builder.setStatusCode(HttpServletResponse.SC_NOT_MODIFIED);
+      return false;
+    }
+    return true;
+  }
+
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JavascriptModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JavascriptModule.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JavascriptModule.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JavascriptModule.java Wed Jan 26 02:16:43 2011
@@ -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.js;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Provides;
+
+import java.util.List;
+
+/**
+ * Guice configuration for the Javascript serving pipeline.
+ */
+public class JavascriptModule extends AbstractModule {
+
+  @Override
+  protected void configure() {
+    // nothing to configure here
+  }
+  
+  @Provides
+  @Inject
+  public List<JsProcessor> provideProcessors(JsLoadProcessor jsLoaderGeneratorProcessor,
+      IfModifiedSinceProcessor ifModifiedSinceProcessor,
+      GetJsContentProcessor getJsContentProcessor,
+      AddOnloadFunctionProcessor addOnloadFunctionProcessor) {
+    return ImmutableList.of(jsLoaderGeneratorProcessor, ifModifiedSinceProcessor,
+        getJsContentProcessor, addOnloadFunctionProcessor);
+  }
+  
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsException.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsException.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsException.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsException.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,36 @@
+/*
+ * 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.js;
+
+/**
+ * Thrown when a step in the JavaScript processing pipeline results in an error.
+ */
+public class JsException extends Exception {
+
+  private final int statusCode;
+
+  public JsException(int statusCode, String msg) {
+    super(msg);
+    this.statusCode = statusCode;
+  }
+  
+  public int getStatusCode() {
+    return statusCode;
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsLoadProcessor.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsLoadProcessor.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsLoadProcessor.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsLoadProcessor.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,109 @@
+/*
+ * 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.js;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.uri.JsUriManager;
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Emit JS loader code if the jsload query parameter is present in the request.
+ */
+public class JsLoadProcessor implements JsProcessor {
+
+  @VisibleForTesting
+  public static final String JSLOAD_ONLOAD_ERROR = "jsload requires onload";
+
+  @VisibleForTesting
+  public static final String JSLOAD_JS_TPL = "(function() {" +
+      "document.write('<scr' + 'ipt type=\"text/javascript\" src=\"%s\"></scr' + 'ipt>');" +
+      "})();"; // Concatenated to avoid some browsers do dynamic script injection.
+
+  private final JsUriManager jsUriManager;
+  private int jsloadTtlSecs;
+
+  @Inject
+  public JsLoadProcessor(
+      JsUriManager jsUriManager, @Named("shindig.jsload.ttl-secs") int jsloadTtlSecs) {
+    this.jsUriManager = jsUriManager;
+    this.jsloadTtlSecs = jsloadTtlSecs;
+  }
+
+  @VisibleForTesting
+  public void setJsloadTtlSecs(int jsloadTtlSecs) {
+    this.jsloadTtlSecs = jsloadTtlSecs;
+  }
+
+  public boolean process(JsRequest request, JsResponseBuilder builder)
+      throws JsException {
+    JsUri jsUri = request.getJsUri();
+    
+    // Don't emit the JS itself; instead emit JS loader script that loads
+    // versioned JS. The loader script is much smaller and cacheable for a
+    // configurable amount of time.
+    if (jsUri.isJsload()) {
+      doJsload(jsUri, builder);
+      return false;
+    }
+    return true;
+  }
+  
+  private void doJsload(JsUri jsUri, JsResponseBuilder resp)
+      throws JsException {
+    String onloadStr = jsUri.getOnload();
+
+    // Require users to specify &onload=. This ensures a reliable continuation
+    // of JS execution. IE asynchronously loads script, before it loads
+    // source-scripted and inlined JS.
+    if (onloadStr == null) {
+      throw new JsException(HttpServletResponse.SC_BAD_REQUEST, JSLOAD_ONLOAD_ERROR);
+    }
+
+    jsUri.setJsload(false);
+    Uri incUri = jsUriManager.makeExternJsUri(jsUri);
+
+    int refresh = getCacheTtlSecs(jsUri);
+    resp.setCacheTtlSecs(refresh);
+    resp.setProxyCacheable(true);
+    resp.setJsCode(createJsloadScript(incUri));
+  }
+
+  private int getCacheTtlSecs(JsUri jsUri) {
+    if (jsUri.isNoCache()) {
+      return 0;
+    } else {
+      Integer jsUriRefresh = jsUri.getRefresh();
+      return (jsUriRefresh != null && jsUriRefresh >= 0)
+          ? jsUriRefresh : jsloadTtlSecs;
+    }
+  }
+
+  @VisibleForTesting
+  String createJsloadScript(Uri uri) {
+    String uriString = uri.toString();
+    return String.format(JSLOAD_JS_TPL, uriString);
+  }
+
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsProcessor.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsProcessor.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsProcessor.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsProcessor.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,36 @@
+/*
+ * 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.js;
+
+/**
+ * A processing step to populate or modify a Javascript response.
+ */
+public interface JsProcessor {
+
+  /**
+   * Populates or modifies the Javascript response.
+   * 
+   * @param jsRequest The JS request that originated this execution.
+   * @param builder The response builder to work on.
+   * @return Whether processing should continue after this processor.
+   * @throw JsException If an unrecoverable error occurred.
+   */
+  boolean process(JsRequest jsRequest, JsResponseBuilder builder)
+      throws JsException;
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsProcessorRegistry.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsProcessorRegistry.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsProcessorRegistry.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsProcessorRegistry.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,40 @@
+/*
+ * 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.js;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * A class to run a series of processing steps on a JS response.
+ * 
+ * The way the processing steps are registered is implementation-dependent.
+ */
+@ImplementedBy(DefaultJsProcessorRegistry.class)
+public interface JsProcessorRegistry {
+
+  /**
+   * Executes the processing steps.
+   * 
+   * @param jsRequest The JS request that originated this execution.
+   * @param response A builder for the JS response.
+   * @throws JsException If any of the steps resulted in an error.
+   */
+  void process(JsRequest jsRequest, JsResponseBuilder response)
+      throws JsException;
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsRequest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsRequest.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsRequest.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsRequest.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,61 @@
+/*
+ * 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.js;
+
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+
+/**
+ * Data about a JavaScript request.
+ * 
+ * This class is instantiated via {@link JsRequestBuilder}.
+ */
+public class JsRequest {
+
+  private final JsUri jsUri;
+  private final String host;
+  private final boolean inCache;
+
+  JsRequest(JsUri jsUri, String host, boolean inCache) {
+    this.jsUri = jsUri;
+    this.host = host;
+    this.inCache = inCache;
+  }
+ 
+  /**
+   * Returns this request's {@link JsUri}.
+   */
+  public JsUri getJsUri() {
+    return jsUri;
+  }
+
+  /**
+   * Returns the host this request was directed to.
+   */
+  public String getHost() {
+    return host;
+  }
+
+  /**
+   * Returns whether the client has this JS code in the cache.
+   */
+  public boolean isInCache() {
+    return inCache;
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsRequestBuilder.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsRequestBuilder.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsRequestBuilder.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsRequestBuilder.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,56 @@
+/*
+ * 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.js;
+
+import com.google.inject.Inject;
+
+import org.apache.shindig.common.uri.UriBuilder;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.uri.JsUriManager;
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Builds {@link JsRequest} instances.
+ */
+public class JsRequestBuilder {
+
+  private final JsUriManager jsUriManager;
+
+  @Inject
+  public JsRequestBuilder(JsUriManager jsUriManager) {
+    this.jsUriManager = jsUriManager; 
+  }
+  
+  /**
+   * Builds a {@link JsRequest} instance from the given request object.
+   * 
+   * @param request The originating HTTP request object.
+   * @return The corresponding JsRequest object.
+   * @throws GadgetException If there was a problem parsing the URI.
+   */
+  public JsRequest build(HttpServletRequest request) throws GadgetException {
+    JsUri jsUri = jsUriManager.processExternJsUri(new UriBuilder(request).toUri());
+    String host = request.getHeader("Host");
+    boolean inCache = request.getHeader("If-Modified-Since") != null;
+    return new JsRequest(jsUri, host, inCache);
+  }
+
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponse.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponse.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponse.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponse.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,75 @@
+/*
+ * 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.js;
+
+
+/**
+ * An immutable object that contains the response for a JavaScript request.
+ */
+public class JsResponse {
+ 
+  String jsCode;
+  int cacheTtlSecs;
+  int statusCode;
+  boolean proxyCacheable;
+  
+  public JsResponse(String jsCode, int statusCode, int cachingPolicy, boolean proxyCacheable) {
+    this.jsCode = jsCode;
+    this.statusCode = statusCode;
+    this.cacheTtlSecs = cachingPolicy;
+    this.proxyCacheable = proxyCacheable;
+  }
+  
+  /**
+   * Returns the JavaScript code to serve.
+   */
+  public String getJsCode() {
+    return jsCode;
+  }
+  
+  /**
+   * Returns the HTTP status code.
+   */
+  public int getStatusCode() {
+    return statusCode;
+  }
+  
+  /**
+   * Returns whether the current response code is an error code.
+   */
+  public boolean isError() {
+    return statusCode >= 400;
+  }
+  
+  /**
+   * Returns the cache TTL in seconds for this response.
+   * 
+   * 0 seconds means "no cache"; a value below 0 means "cache forever".
+   */
+  public int getCacheTtlSecs() {
+    return cacheTtlSecs;
+  }
+  
+  /**
+   * Returns whether the response can be cached by intermediary proxies.
+   */
+  public boolean isProxyCacheable() {
+    return proxyCacheable;
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponseBuilder.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponseBuilder.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponseBuilder.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsResponseBuilder.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,131 @@
+/*
+ * 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.js;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A class with methods to create {@link JsResponse} objects.
+ */
+public class JsResponseBuilder {
+
+  private StringBuilder jsCode;
+  private int statusCode;
+  private int cacheTtlSecs;
+  private boolean proxyCacheable;
+  
+  public JsResponseBuilder() {
+    jsCode = new StringBuilder();
+    statusCode = HttpServletResponse.SC_OK;
+    cacheTtlSecs = 0;
+    proxyCacheable = false;
+  }
+
+  public JsResponseBuilder(JsResponse response) {
+    jsCode = new StringBuilder(response.jsCode);
+    statusCode = response.statusCode;
+    cacheTtlSecs = response.cacheTtlSecs;
+    proxyCacheable = response.proxyCacheable;
+  }
+  
+  /**
+   * Returns a StringBuilder to modify the current JavaScript code.
+   */
+  public StringBuilder getJsCode() {
+    return jsCode;
+  }
+  
+  /**
+   * Replaces the current JavaScript code with some new code.
+   */
+  public JsResponseBuilder setJsCode(CharSequence code) {
+    this.jsCode = new StringBuilder(code);
+    return this;
+  }
+
+  /**
+   * Deletes all JavaScript code in the builder.
+   */
+  public JsResponseBuilder clearJsCode() {
+    jsCode = new StringBuilder();
+    return this;
+  }
+
+  /**
+   * Appends some JavaScript code to the end of the current code.  
+   */
+  public JsResponseBuilder addJsCode(CharSequence data) {
+    jsCode.append(data);
+    return this;
+  }
+
+  /**
+   * Sets the HTTP status code.
+   */
+  public JsResponseBuilder setStatusCode(int responseCode) {
+    this.statusCode = responseCode;
+    return this;
+  }
+  
+  /**
+   * Returns the HTTP status code.
+   */
+  public int getStatusCode() {
+    return statusCode;
+  }
+  
+  /**
+   * Sets the cache TTL in seconds for the response being built.
+   * 
+   * 0 seconds means "no cache"; a value below 0 means "cache forever".
+   */
+  public JsResponseBuilder setCacheTtlSecs(int cacheTtlSecs) {
+    this.cacheTtlSecs = cacheTtlSecs;
+    return this;
+  }
+  
+  /**
+   * Returns the cache TTL in seconds for the response.
+   */
+  public int getCacheTtlSecs() {
+    return cacheTtlSecs;
+  }
+
+  /**
+   * Sets whether the response can be cached by intermediary proxies.
+   */
+  public JsResponseBuilder setProxyCacheable(boolean proxyCacheable) {
+    this.proxyCacheable = proxyCacheable;
+    return this;
+  }
+  
+  /**
+   * Returns whether the response can be cached by intermediary proxies.
+   */
+  public boolean isProxyCacheable() {
+    return proxyCacheable;
+  }
+
+  /**
+   * Builds a {@link JsResponse} object with the provided data.
+   */
+  public JsResponse build() {
+    return new JsResponse(jsCode.toString(), statusCode, cacheTtlSecs, proxyCacheable);
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipeline.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipeline.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipeline.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipeline.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,40 @@
+/*
+ * 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.js;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * A pipeline to process Javascript requests.
+ * 
+ * These requests go through several processing steps that may provide features
+ * like compilation, cajoling, etc. 
+ */
+@ImplementedBy(DefaultJsServingPipeline.class)
+public interface JsServingPipeline {
+
+  /**
+   * Executes the steps in the pipeline and returns the resulting response.
+   * 
+   * @param jsRequest The JS request.
+   * @return The JavaScript response generated by the pipeline.
+   * @throws JsException If any of the steps resulted in an error.
+   */
+  JsResponse execute(JsRequest jsRequest) throws JsException;
+}

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddOnloadFunctionProcessorTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddOnloadFunctionProcessorTest.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddOnloadFunctionProcessorTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/AddOnloadFunctionProcessorTest.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package org.apache.shindig.gadgets.js;
+
+import static org.junit.Assert.*;
+
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Tests for {@link AddOnloadFunctionProcessor}.
+ */
+public class AddOnloadFunctionProcessorTest {
+
+  private static final String ONLOAD_FUNCTION = "onloadFunc";
+
+  private IMocksControl control;
+  private JsUri jsUri;
+  private JsRequest request;
+  private JsResponseBuilder response;
+  private AddOnloadFunctionProcessor processor;
+
+  @Before
+  public void setUp() {
+    control = EasyMock.createControl();
+    jsUri = control.createMock(JsUri.class);
+    request = control.createMock(JsRequest.class);
+    response = new JsResponseBuilder();
+    processor = new AddOnloadFunctionProcessor();
+
+    EasyMock.expect(request.getJsUri()).andReturn(jsUri);
+  }
+
+  @Test
+  public void testSkipsWhenNoOnloadIsSpecified() throws Exception {
+    EasyMock.expect(jsUri.getOnload()).andReturn(null);
+    response = control.createMock(JsResponseBuilder.class);
+    control.replay();
+    assertTrue(processor.process(request, response));
+    control.verify();
+  }
+
+  @Test
+  public void testFailsWithInvalidFunctionName() throws Exception {
+    EasyMock.expect(jsUri.getOnload()).andReturn("!!%%!!%%");
+    control.replay();
+    try {
+      processor.process(request, response);
+      fail("A JsException should have been thrown by the processor.");
+    } catch (JsException e) {
+      assertEquals(HttpServletResponse.SC_BAD_REQUEST, e.getStatusCode());
+      assertEquals(AddOnloadFunctionProcessor.ONLOAD_FUNCTION_NAME_ERROR, e.getMessage());
+    }
+    control.verify();
+  }
+
+  @Test
+  public void testGeneratesCallbackCode() throws Exception {
+    EasyMock.expect(jsUri.getOnload()).andReturn(ONLOAD_FUNCTION);
+    control.replay();
+    assertTrue(processor.process(request, response));
+    assertEquals(HttpServletResponse.SC_OK, response.getStatusCode());
+    String expectedBody = String.format(AddOnloadFunctionProcessor.ONLOAD_JS_TPL, ONLOAD_FUNCTION);
+    assertEquals(expectedBody, response.getJsCode().toString());
+    control.verify();
+  }
+}

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/DefaultJsProcessorRegistryTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/DefaultJsProcessorRegistryTest.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/DefaultJsProcessorRegistryTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/DefaultJsProcessorRegistryTest.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,86 @@
+/*
+ * 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.js;
+
+import static org.junit.Assert.*;
+
+import com.google.common.collect.ImmutableList;
+
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * Tests for {@link DefaultJsProcessorRegistry}.
+ */
+public class DefaultJsProcessorRegistryTest {
+
+  private static final String JS_CODE = "some JS code";
+
+  private IMocksControl control;
+  private JsRequest request;
+  private JsResponseBuilder response;
+  private JsProcessor processor1;
+  private JsProcessor processor2;
+  private DefaultJsProcessorRegistry registry;
+
+  @Before
+  public void setUp() {
+    control = EasyMock.createControl();
+    request = control.createMock(JsRequest.class);
+    response = new JsResponseBuilder();
+    processor1 = control.createMock(JsProcessor.class);
+    processor2 = control.createMock(JsProcessor.class);
+    registry = new DefaultJsProcessorRegistry(ImmutableList.of(processor1, processor2));
+  }
+
+  @Test
+  public void testProcessorModifiesResponse() throws Exception {
+    JsProcessor processor = new JsProcessor() {
+      public boolean process(JsRequest request, JsResponseBuilder builder) {
+        builder.setJsCode(JS_CODE);
+        return true;
+      }
+    };
+    registry = new DefaultJsProcessorRegistry(ImmutableList.of(processor));
+    control.replay();
+    registry.process(request, response);
+    assertEquals(JS_CODE, response.getJsCode().toString());
+    control.verify();
+  }
+
+  @Test
+  public void testTwoProcessorsAreRunOneAfterAnother() throws Exception {
+    EasyMock.expect(processor1.process(request, response)).andReturn(true);
+    EasyMock.expect(processor2.process(request, response)).andReturn(true);
+    control.replay();
+    registry.process(request, response);
+    control.verify();
+  }
+  
+  @Test
+  public void testProcessorStopsProcessingWhenItReturnsFalse() throws Exception {
+    EasyMock.expect(processor1.process(request, response)).andReturn(false);
+    control.replay();
+    registry.process(request, response);
+    control.verify();    
+  }
+}

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/DefaultJsServingPipelineTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/DefaultJsServingPipelineTest.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/DefaultJsServingPipelineTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/DefaultJsServingPipelineTest.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,44 @@
+/*
+ * 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.js;
+
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.junit.Test;
+
+
+/**
+ * Tests for {@link DefaultJsServingPipeline}.
+ */
+public class DefaultJsServingPipelineTest {
+
+  @Test
+  public void testProcessorsAreCalledForRequest() throws Exception {
+    IMocksControl control = EasyMock.createControl();
+    JsRequest request = control.createMock(JsRequest.class);
+    JsProcessorRegistry registry = control.createMock(JsProcessorRegistry.class);
+    DefaultJsServingPipeline pipeline = new DefaultJsServingPipeline(registry);
+    registry.process(EasyMock.eq(request), EasyMock.isA(JsResponseBuilder.class));
+    control.replay();
+
+    pipeline.execute(request);
+
+    control.verify();
+  }
+}

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/GetJsContentProcessorTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/GetJsContentProcessorTest.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/GetJsContentProcessorTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/GetJsContentProcessorTest.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,110 @@
+/*
+ * 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.js;
+
+import static org.junit.Assert.*;
+
+import org.apache.shindig.gadgets.servlet.JsHandler;
+import org.apache.shindig.gadgets.servlet.JsHandler.Response;
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+import org.apache.shindig.gadgets.uri.UriStatus;
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * Tests for {@link GetJsContentProcessor}.
+ */
+public class GetJsContentProcessorTest {
+
+  private static final String HOST = "localhost";
+  private static final String JS_CODE = "some JS data";
+  
+  private IMocksControl control;
+  private JsHandler handler;
+  private JsUri jsUri;
+  private JsRequest request;
+  private Response handlerResponse;
+  private JsResponseBuilder response;
+  private GetJsContentProcessor processor;
+
+  @Before
+  public void setUp() {
+    control = EasyMock.createControl();
+    handler = control.createMock(JsHandler.class);
+    jsUri = control.createMock(JsUri.class);
+    request = control.createMock(JsRequest.class);
+    handlerResponse = control.createMock(JsHandler.Response.class);
+    response = new JsResponseBuilder();
+    processor = new GetJsContentProcessor(handler);
+  }
+  
+  @Test
+  public void testPopulatesResponseForUnversionedRequest() throws Exception {
+    setExpectations(true, UriStatus.VALID_UNVERSIONED);
+    control.replay();
+    processor.process(request, response);
+    checkResponse(true, 3600);
+    control.verify();
+  }
+
+  @Test
+  public void testPopulatesResponseForVersionedRequest() throws Exception {
+    setExpectations(true, UriStatus.VALID_VERSIONED);
+    control.replay();
+    processor.process(request, response);
+    checkResponse(true, -1);
+    control.verify();
+  }
+
+  @Test
+  public void testPopulatesResponseForInvalidVersion() throws Exception {
+    setExpectations(true, UriStatus.INVALID_VERSION);
+    control.replay();
+    processor.process(request, response);
+    checkResponse(true, 0);
+    control.verify();
+  }
+
+  @Test
+  public void testPopulatesResponseForNoProxyCacheable() throws Exception {
+    setExpectations(false, UriStatus.VALID_UNVERSIONED);
+    control.replay();
+    processor.process(request, response);
+    checkResponse(false, 3600);
+    control.verify();
+  }
+
+  private void setExpectations(boolean proxyCacheable, UriStatus uriStatus) {
+    EasyMock.expect(handler.getJsContent(jsUri, HOST)).andReturn(handlerResponse);
+    EasyMock.expect(request.getHost()).andReturn(HOST);
+    EasyMock.expect(request.getJsUri()).andReturn(jsUri);
+    EasyMock.expect(handlerResponse.getJsData()).andReturn(new StringBuilder(JS_CODE));
+    EasyMock.expect(handlerResponse.isProxyCacheable()).andReturn(proxyCacheable);
+    EasyMock.expect(jsUri.getStatus()).andReturn(uriStatus);
+  }
+
+  private void checkResponse(boolean proxyCacheable, int expectedTtl) {
+    assertEquals(proxyCacheable, response.isProxyCacheable());
+    assertEquals(expectedTtl, response.getCacheTtlSecs());
+    assertEquals(JS_CODE, response.getJsCode().toString());
+  }
+}

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/IfModifiedSinceProcessorTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/IfModifiedSinceProcessorTest.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/IfModifiedSinceProcessorTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/IfModifiedSinceProcessorTest.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,81 @@
+/*
+ * 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.js;
+
+import static org.junit.Assert.*;
+
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+import org.apache.shindig.gadgets.uri.UriStatus;
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Tests for {@link IfModifiedSinceProcessor}.
+ */
+public class IfModifiedSinceProcessorTest {
+
+  private IMocksControl control;
+  private JsUri jsUri;
+  private JsRequest request;
+  private JsResponseBuilder response;
+  private IfModifiedSinceProcessor processor;
+
+  @Before
+  public void setUp() {
+    control = EasyMock.createControl();
+    jsUri = control.createMock(JsUri.class);
+    request = control.createMock(JsRequest.class);
+    response = new JsResponseBuilder();
+    processor = new IfModifiedSinceProcessor();
+  }
+  
+  @Test
+  public void testDoesNothingAndContinuesProcessingWhenHeaderIsAbsent() throws Exception {
+    EasyMock.expect(request.isInCache()).andReturn(false);
+    control.replay();
+    assertTrue(processor.process(request, response));
+    control.verify();
+  }
+
+  @Test
+  public void testDoesNothingAndContinuesProcessingWhenNotVersioned() throws Exception {
+    EasyMock.expect(request.isInCache()).andReturn(true);
+    EasyMock.expect(request.getJsUri()).andReturn(jsUri);
+    EasyMock.expect(jsUri.getStatus()).andReturn(UriStatus.VALID_UNVERSIONED);
+    control.replay();
+    assertTrue(processor.process(request, response));
+    control.verify();
+  }
+
+  @Test
+  public void testReturnsNotModifiedAndStopsProcessingWithHeaderAndVersion() throws Exception {
+    EasyMock.expect(request.isInCache()).andReturn(true);
+    EasyMock.expect(request.getJsUri()).andReturn(jsUri);
+    EasyMock.expect(jsUri.getStatus()).andReturn(UriStatus.VALID_VERSIONED);
+    control.replay();
+    assertFalse(processor.process(request, response));
+    assertEquals(HttpServletResponse.SC_NOT_MODIFIED, response.getStatusCode());
+    control.verify();
+  }
+}

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/JsLoadProcessorTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/JsLoadProcessorTest.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/JsLoadProcessorTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/JsLoadProcessorTest.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,129 @@
+/*
+ * 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.js;
+
+import static org.junit.Assert.*;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.uri.JsUriManager;
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Tests for {@link JsLoadProcessor}.
+ */
+public class JsLoadProcessorTest {
+
+  private static final String ONLOAD_FUNCTION = "onloadFunc";
+
+  private IMocksControl control;
+  private JsRequest request;
+  private JsUriManager jsUriManager;
+  private JsUri jsUri;
+  private Uri uri;
+  private JsResponseBuilder response;
+  private JsLoadProcessor processor;
+
+  @Before
+  public void setUp() {
+    control = EasyMock.createControl();
+    request = control.createMock(JsRequest.class);
+    jsUriManager = control.createMock(JsUriManager.class);
+    jsUri = control.createMock(JsUri.class);
+    uri = Uri.parse("http://example.org/foo.xml");
+    response = new JsResponseBuilder();
+    processor = new JsLoadProcessor(jsUriManager, 1234);
+    
+    EasyMock.expect(request.getJsUri()).andReturn(jsUri);
+  }
+  
+  @Test
+  public void testSkipsWhenNoJsLoad() throws Exception {
+    EasyMock.expect(jsUri.isJsload()).andReturn(false);
+    response = control.createMock(JsResponseBuilder.class);
+    control.replay();
+    assertTrue(processor.process(request, response));
+    control.verify();
+  }
+  
+  @Test
+  public void testFailsWhenNoOnloadIsSpecified() throws Exception {
+    EasyMock.expect(jsUri.isJsload()).andReturn(true);
+    EasyMock.expect(jsUri.getOnload()).andReturn(null);
+    control.replay();
+    try {
+      processor.process(request, response);
+      fail("A JsException should have been thrown by the processor.");
+    } catch (JsException e) {
+      assertEquals(HttpServletResponse.SC_BAD_REQUEST, e.getStatusCode());
+      assertEquals(JsLoadProcessor.JSLOAD_ONLOAD_ERROR, e.getMessage());
+    }
+    control.verify();
+  }
+  
+  @Test
+  public void testGeneratesLoaderCodeWithNoCache() throws Exception {
+    setExpectations(true, null);
+    control.replay();
+    checkGeneratedCode(0);
+    control.verify();
+  }
+
+  @Test
+  public void testGeneratesLoaderCodeWithDefaultTtl() throws Exception {
+    setExpectations(false, null);
+    control.replay();
+    checkGeneratedCode(1234);
+    control.verify();
+  }
+
+  @Test
+  public void testGeneratesLoaderCodeWithRefresh() throws Exception {
+    setExpectations(false, 300);
+    control.replay();
+    checkGeneratedCode(300);
+    control.verify();
+  }
+
+  private void setExpectations(boolean noCache, Integer refresh) {
+    EasyMock.expect(jsUri.isJsload()).andReturn(true);
+    EasyMock.expect(jsUri.getOnload()).andReturn(ONLOAD_FUNCTION);
+    jsUri.setJsload(false);
+    EasyMock.expect(jsUriManager.makeExternJsUri(jsUri)).andReturn(uri);
+    EasyMock.expect(jsUri.isNoCache()).andReturn(noCache);
+    if (!noCache) {
+      EasyMock.expect(jsUri.getRefresh()).andReturn(refresh);
+    }
+  }
+
+  private void checkGeneratedCode(int expectedTtl) throws JsException {
+    assertFalse(processor.process(request, response));
+    assertEquals(HttpServletResponse.SC_OK, response.getStatusCode());
+    assertEquals(expectedTtl, response.getCacheTtlSecs());
+    String expectedBody = String.format(JsLoadProcessor.JSLOAD_JS_TPL, uri.toString());
+    assertEquals(expectedBody, response.getJsCode().toString());
+  }
+
+}

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/JsRequestBuilderTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/JsRequestBuilderTest.java?rev=1063571&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/JsRequestBuilderTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/js/JsRequestBuilderTest.java Wed Jan 26 02:16:43 2011
@@ -0,0 +1,106 @@
+/*
+ * 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.js;
+
+import static org.junit.Assert.*;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.GadgetException.Code;
+import org.apache.shindig.gadgets.uri.JsUriManager;
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * Tests for {@link JsRequestBuilder}.
+ */
+public class JsRequestBuilderTest {
+
+  private static final String HOST_HEADER_KEY = "Host";
+  private static final String IMS_HEADER_KEY = "If-Modified-Since";
+  private static final String HOST = "localhost";
+  
+  private IMocksControl control;
+  private JsUriManager jsUriManager;
+  private JsUri jsUri;
+  private HttpServletRequest request;
+  private JsRequestBuilder builder;
+
+  @Before
+  public void setUp() {
+    control = EasyMock.createControl();
+    jsUriManager = control.createMock(JsUriManager.class);
+    jsUri = control.createMock(JsUri.class);
+    request = control.createMock(HttpServletRequest.class);
+    builder = new JsRequestBuilder(jsUriManager);
+    
+    EasyMock.expect(request.getScheme()).andReturn("http");
+    EasyMock.expect(request.getServerPort()).andReturn(80);
+    EasyMock.expect(request.getServerName()).andReturn("HOST");
+    EasyMock.expect(request.getRequestURI()).andReturn("/foo");
+    EasyMock.expect(request.getQueryString()).andReturn("");
+  }
+
+  @Test
+  public void testCreateRequestNotInCache() throws Exception {
+    EasyMock.expect(jsUriManager.processExternJsUri(EasyMock.isA(Uri.class))).andReturn(jsUri);
+    EasyMock.expect(request.getHeader(HOST_HEADER_KEY)).andReturn(HOST);
+    EasyMock.expect(request.getHeader(IMS_HEADER_KEY)).andReturn(null);
+    control.replay();
+    JsRequest jsRequest = builder.build(request);
+    control.verify();
+    assertSame(jsUri, jsRequest.getJsUri());
+    assertEquals(HOST, jsRequest.getHost());
+    assertFalse(jsRequest.isInCache());
+  }
+
+  @Test
+  public void testCreateRequestInCache() throws Exception {
+    EasyMock.expect(jsUriManager.processExternJsUri(EasyMock.isA(Uri.class))).andReturn(jsUri);
+    EasyMock.expect(request.getHeader(HOST_HEADER_KEY)).andReturn(HOST);
+    EasyMock.expect(request.getHeader(IMS_HEADER_KEY)).andReturn("today");
+    control.replay();
+    JsRequest jsRequest = builder.build(request);
+    control.verify();
+    assertSame(jsUri, jsRequest.getJsUri());
+    assertEquals(HOST, jsRequest.getHost());
+    assertTrue(jsRequest.isInCache());
+  }
+
+  @Test
+  public void testCreateRequestThrowsExceptionOnParseError() throws Exception {
+    EasyMock.expect(jsUriManager.processExternJsUri(EasyMock.isA(Uri.class))).andThrow(
+        new GadgetException(Code.INVALID_PARAMETER));
+    control.replay();
+    try {
+      builder.build(request);
+      fail("Should have thrown a GadgetException.");
+    } catch (GadgetException e) {
+      // pass
+    }
+    control.verify();
+  }
+}