Author: johnh
Date: Fri Jan 14 02:24:43 2011
New Revision: 1058839

URL: http://svn.apache.org/viewvc?rev=1058839&view=rev
Log:
Stub: Add ability to @Inject a JsCompiler that performs runtime JS compilation 
on served code.

This does not actually implement the compiler as yet, due to JRE version 
issues. Specifically, Closure Compiler appears to demand 1.6 yet Shindig is 
still on 1.5.


Added:
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/JsCompiler.java
Modified:
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsHandler.java
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/JsServletTest.java
    shindig/trunk/pom.xml

Added: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/JsCompiler.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/JsCompiler.java?rev=1058839&view=auto
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/JsCompiler.java
 (added)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/JsCompiler.java
 Fri Jan 14 02:24:43 2011
@@ -0,0 +1,47 @@
+/*
+ * 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.rewrite.js;
+
+import java.util.List;
+
+public interface JsCompiler {
+  public Result compile(String jsData, List<String> externs);
+  
+  public static class Result {
+    private final String compiled;
+    private final List<String> errors;
+    
+    public Result(String compiled) {
+      this.compiled = compiled;
+      this.errors = null;
+    }
+    
+    public Result(List<String> errors) {
+      this.compiled = null;
+      this.errors = errors;
+    }
+    
+    public String getCode() {
+      return compiled;
+    }
+    
+    public List<String> getErrors() {
+      return errors;
+    }
+  }
+}

Modified: 
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=1058839&r1=1058838&r2=1058839&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsHandler.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsHandler.java
 Fri Jan 14 02:24:43 2011
@@ -22,13 +22,21 @@ import org.apache.shindig.config.Contain
 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.ApiDirective;
 import org.apache.shindig.gadgets.features.FeatureRegistry;
 import org.apache.shindig.gadgets.features.FeatureResource;
+import org.apache.shindig.gadgets.rewrite.js.JsCompiler;
 import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
 
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
+import com.google.caja.util.Join;
+import com.google.caja.util.Sets;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -36,13 +44,13 @@ 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;
+  private JsCompiler compiler = null;
 
   @Inject
   public JsHandler(
@@ -53,6 +61,15 @@ public class JsHandler {
     this.containerConfig = containerConfig;
     this.configContributors = configContributors;
   }
+  
+  @Inject(optional = true)
+  public void setSupportCompiler(JsCompiler compiler) {
+    this.compiler = compiler;
+  }
+  
+  protected boolean shouldUseCompiler(JsUri jsUri) {
+    return compiler != null;
+  }
 
   /**
    * Get the content of the feature resources and push it to jsData.
@@ -64,25 +81,79 @@ public class JsHandler {
    */
   public Response getJsContent(final JsUri jsUri, String host) {
     GadgetContext ctx = new JsGadgetContext(jsUri);
-    StringBuilder jsData = new StringBuilder();
     Collection<String> needed = jsUri.getLibs();
-    Collection<? extends FeatureResource> resources =
-        registry.getFeatureResources(ctx, needed, null).getResources();
     String container = ctx.getContainer();
     boolean isProxyCacheable = true;
+    
+    FeatureRegistry.LookupResult lookup = registry.getFeatureResources(ctx, 
needed, null);
 
-    for (FeatureResource featureResource : resources) {
-      String content = jsUri.isDebug() ? featureResource.getDebugContent() : 
featureResource.getContent();
-      if (!featureResource.isExternal()) {
-        jsData.append(content);
+    // Collate all JS desired for the current request.
+    StringBuilder jsData = new StringBuilder();
+    List<String> externs = Lists.newArrayList();
+    boolean doCompile = !jsUri.isDebug() && shouldUseCompiler(jsUri);
+    Set<String> everythingExported = Sets.newHashSet();
+    for (FeatureRegistry.FeatureBundle bundle : lookup.getBundles()) {
+      for (FeatureResource featureResource : bundle.getResources()) {
+        String content = jsUri.isDebug() || doCompile
+           ? featureResource.getDebugContent() : featureResource.getContent();
+        if (content == null) content = "";
+        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 (doCompile) {
+        // Add all needed exports while collecting externs.
+        List<String> rawExports = Lists.newArrayList();
+        for (ApiDirective api : bundle.getApis()) {
+          if (api.getType() == ApiDirective.Type.JS) {
+            if (api.isExports()) {
+              rawExports.add(api.getValue());
+            } else if (api.isUses()) {
+              externs.add(api.getValue());
+            }
+          }
+        }
+        Collections.sort(rawExports);
+        String prevExport = null;
+        for (String export : rawExports) {
+          if (!export.equals(prevExport)) {
+            String[] pieces = export.split("\\.");
+            String base = "window";
+            for (int i = 0; i < pieces.length; ++i) {
+              String symExported = (i == 0) ? pieces[0] : base + "." + 
pieces[i];
+              if (!everythingExported.contains(symExported)) {
+                String curExport = base + "['" + pieces[i] + "']=" + 
symExported + ";\n";
+                jsData.append(curExport);
+                everythingExported.add(symExported);
+              }
+              base = symExported;
+            }
+          }
+          prevExport = export;
+        }
+      }
+    }
+    
+    // Compile if desired. Specific compiler options are provided to the 
JsCompiler instance.
+    if (doCompile) {
+      StringBuilder compiled = new StringBuilder();
+      JsCompiler.Result result = compiler.compile(jsData.toString(), externs);
+      String code = result.getCode();
+      if (code != null) {
+        compiled.append(code);
+        jsData = compiled;
       } else {
-        // Support external/type=url feature serving through document.write()
-        jsData.append("document.write('<script 
src=\"").append(content).append("\"></script>')");
+        System.err.println("JS Compilation error: " + Join.join(", ", 
result.getErrors()));
       }
-      isProxyCacheable = isProxyCacheable && 
featureResource.isProxyCacheable();
-      jsData.append(";\n");
     }
 
+    // Append gadgets.config initialization if not in standard gadget mode.
     if (ctx.getRenderingContext() != RenderingContext.GADGET) {
       // Append some container specific things
       Map<String, Object> features = containerConfig.getMap(container, 
"gadgets.features");
@@ -93,7 +164,7 @@ public class JsHandler {
         // Discard what we don't care about.
         for (String name : registry.getFeatures(needed)) {
           Object conf = features.get(name);
-          // Add from containerConfig
+          // Add from containerConfig.
           if (conf != null) {
             config.put(name, conf);
           }
@@ -105,6 +176,8 @@ public class JsHandler {
         
jsData.append("gadgets.config.init(").append(JsonSerializer.serialize(config)).append(");\n");
       }
     }
+    
+    // Wrap up the response.
     return new Response(jsData, isProxyCacheable);
   }
 

Modified: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/JsServletTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/JsServletTest.java?rev=1058839&r1=1058838&r2=1058839&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/JsServletTest.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/JsServletTest.java
 Fri Jan 14 02:24:43 2011
@@ -79,7 +79,6 @@ public class JsServletTest extends Servl
   }
 
   @Test
-  @SuppressWarnings("unchecked")
   public void testDoJsloadNormal() throws Exception {
     String url = 
"http://localhost/gadgets/js/feature.js?v=abc&nocache=0&onload="; + ONLOAD_PARAM;
     JsUri jsUri = mockJsUri(CONTAINER_PARAM, RenderingContext.CONTAINER, true, 
true, false,
@@ -98,7 +97,6 @@ public class JsServletTest extends Servl
   }
 
   @Test
-  @SuppressWarnings("unchecked")
   public void testDoJsloadWithJsLoadTimeout() throws Exception {
     String url = 
"http://localhost/gadgets/js/feature.js?v=abc&nocache=0&onload="; + ONLOAD_PARAM;
     JsUri jsUri = mockJsUri(CONTAINER_PARAM, RenderingContext.CONTAINER, true, 
true,

Modified: shindig/trunk/pom.xml
URL: 
http://svn.apache.org/viewvc/shindig/trunk/pom.xml?rev=1058839&r1=1058838&r2=1058839&view=diff
==============================================================================
--- shindig/trunk/pom.xml (original)
+++ shindig/trunk/pom.xml Fri Jan 14 02:24:43 2011
@@ -623,11 +623,11 @@
             <artifactId>maven-javadoc-plugin</artifactId>
             <version>2.7</version>
             <configuration>
-              <source>1.5</source>
-              <target>1.5</target>
+              <source>1.6</source>
+              <target>1.6</target>
               <encoding>${project.build.sourceEncoding}</encoding>
               <links>
-                <link>http://java.sun.com/j2se/1.5.0/docs/api</link>
+                <link>http://java.sun.com/j2se/1.6.0/docs/api</link>
                 
<link>http://java.sun.com/products/servlet/2.5/docs/servlet-2_5-mr2/</link>
                 <link>http://www.json.org/javadoc/</link>
                 <link>http://junit.sourceforge.net/javadoc/</link>
@@ -687,7 +687,7 @@
             <artifactId>maven-pmd-plugin</artifactId>
             <version>2.5</version>
             <configuration>
-              <targetJdk>1.5</targetJdk>
+              <targetJdk>1.6</targetJdk>
               <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
             </configuration>
             <reportSets>
@@ -1003,8 +1003,8 @@
           <artifactId>maven-compiler-plugin</artifactId>
           <version>2.3.2</version>
           <configuration>
-            <source>1.5</source>
-            <target>1.5</target>
+            <source>1.6</source>
+            <target>1.6</target>
             <showDeprecation>true</showDeprecation>
             
<compilerArgument>-Xlint:unchecked,deprecation,fallthrough,finally</compilerArgument>
             <fork>true</fork>
@@ -1034,7 +1034,7 @@
             <configuration>
               <rules>
                 <requireJavaVersion>
-                  <version>[1.5,)</version>
+                  <version>[1.6,)</version>
                 </requireJavaVersion>
               </rules>    
             </configuration>
@@ -1074,11 +1074,11 @@
           <artifactId>maven-javadoc-plugin</artifactId>
           <version>2.7</version>
           <configuration>
-            <source>1.5</source>
-            <target>1.5</target>
+            <source>1.6</source>
+            <target>1.6</target>
             <encoding>${project.build.sourceEncoding}</encoding>
             <links>
-              <link>http://java.sun.com/j2se/1.5.0/docs/api</link>
+              <link>http://java.sun.com/j2se/1.6.0/docs/api</link>
               
<link>http://java.sun.com/products/servlet/2.5/docs/servlet-2_5-mr2/</link>
               <link>http://www.json.org/javadoc/</link>
               <link>http://junit.sourceforge.net/javadoc/</link>


Reply via email to