You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2013/07/16 23:37:58 UTC

[1/3] git commit: Roll version number forward to 5.4-alpha-11

Updated Branches:
  refs/heads/master a8ad6d265 -> eaada44a8


Roll version number forward to 5.4-alpha-11


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/476669d8
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/476669d8
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/476669d8

Branch: refs/heads/master
Commit: 476669d8175eb5e03c5626fc7e36192df9ce35a9
Parents: a8ad6d2
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Tue Jul 16 09:43:04 2013 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Tue Jul 16 09:43:04 2013 -0700

----------------------------------------------------------------------
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/476669d8/build.gradle
----------------------------------------------------------------------
diff --git a/build.gradle b/build.gradle
index 417dcf4..8c60182 100755
--- a/build.gradle
+++ b/build.gradle
@@ -34,7 +34,7 @@ project.version = tapestryVersion()
 def tapestryVersion() {
 
     def major = "5.4"
-    def minor = "-alpha-10"
+    def minor = "-alpha-11"
 
     // When building on the CI server, make sure -SNAPSHOT is appended, as it is a nightly build.
     // When building normally, or for a release, no suffix is desired.


[2/3] git commit: Add cross-reference documentation

Posted by hl...@apache.org.
Add cross-reference documentation


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/2beaaa9b
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/2beaaa9b
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/2beaaa9b

Branch: refs/heads/master
Commit: 2beaaa9b75faa6799b69687914e685515841e782
Parents: 476669d
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Tue Jul 16 12:53:54 2013 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Tue Jul 16 12:53:54 2013 -0700

----------------------------------------------------------------------
 .../java/org/apache/tapestry5/ioc/services/SymbolProvider.java  | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/2beaaa9b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/SymbolProvider.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/SymbolProvider.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/SymbolProvider.java
index 7d39037..8a1f134 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/SymbolProvider.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/SymbolProvider.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008 The Apache Software Foundation
+// Copyright 2006, 2008, 2013 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -21,6 +21,9 @@ import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
  * <p/>
  * This is the service interface for the FactoryDefaults and ApplicationDefaults services; each of these takes a
  * configuration mapping symbols to their values.
+ *
+ * @see FactoryDefaults
+ * @see ApplicationDefaults
  */
 @UsesMappedConfiguration(String.class)
 public interface SymbolProvider


[3/3] git commit: Add a file-system cache ofr single-input-file comilations (CoffeeScript) that can persist between executions

Posted by hl...@apache.org.
Add a file-system cache ofr single-input-file comilations (CoffeeScript) that can persist between executions


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/eaada44a
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/eaada44a
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/eaada44a

Branch: refs/heads/master
Commit: eaada44a8e6c83af346343c5d125015fe8ca6459
Parents: 2beaaa9
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Tue Jul 16 14:37:49 2013 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Tue Jul 16 14:37:49 2013 -0700

----------------------------------------------------------------------
 tapestry-wro4j/build.gradle                     |   4 +-
 .../tapestry5/internal/wro4j/CacheMode.java     |  40 ++++
 .../internal/wro4j/ContentChangeTracker.java    |  13 +-
 .../internal/wro4j/LessResourceTransformer.java | 116 ++--------
 .../internal/wro4j/ResourceTransformUtils.java  |   8 +-
 .../wro4j/ResourceTransformerFactory.java       |  24 ++-
 .../wro4j/ResourceTransformerFactoryImpl.java   | 209 +++++++++++++++----
 .../apache/tapestry5/wro4j/WRO4JSymbols.java    |  25 +++
 .../tapestry5/wro4j/modules/WRO4JModule.java    |  25 ++-
 9 files changed, 304 insertions(+), 160 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/build.gradle
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/build.gradle b/tapestry-wro4j/build.gradle
index 3c541e7..cd00fbc 100644
--- a/tapestry-wro4j/build.gradle
+++ b/tapestry-wro4j/build.gradle
@@ -38,5 +38,7 @@ jar.manifest {
 test {
     useJUnit()
 
-    systemProperties "geb.build.reportsDir": "$reporting.baseDir/geb"
+    systemProperties("geb.build.reportsDir": "$reporting.baseDir/geb",
+        "tapestry.compiled-asset-cache-dir": "$buildDir/compiled-asset-cache",
+        "tapestry.production-mode": "false")
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CacheMode.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CacheMode.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CacheMode.java
new file mode 100644
index 0000000..1410b3c
--- /dev/null
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CacheMode.java
@@ -0,0 +1,40 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.wro4j;
+
+/**
+ * Controls caching for {@link ResourceTransformerFactory} in <em>development mode</em>. In production mode, caching at this
+ * level is not needed, because artifacts are also cached later in the pipeline. This caching is all about avoid unwanted
+ */
+public enum CacheMode
+{
+    /**
+     * Cache the content on the file system, in the directory defined by {@link org.apache.tapestry5.wro4j.WRO4JSymbols#CACHE_DIR}.
+     * This allows compilation to be avoided even after a restart, as long as the source file has not changed. This only works
+     * for compilations that operate on a single file (such as CoffeeScript, but not Less, which has an {@code @import} statement).
+     */
+    SINGLE_FILE,
+
+    /**
+     * The source may be multiple files (e.g., Less). Cache in memory, and invalidate the cache if any of the multiple
+     * file's content changes.
+     */
+    MULTIPLE_FILE,
+
+    /**
+     * Do no caching. This is appropriate for extremely cheap compilers.
+     */
+    NONE;
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ContentChangeTracker.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ContentChangeTracker.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ContentChangeTracker.java
index 770509e..b0e2eb1 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ContentChangeTracker.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ContentChangeTracker.java
@@ -32,16 +32,9 @@ public class ContentChangeTracker implements ResourceDependencies
 
     public void addDependency(Resource dependency)
     {
-        try
-        {
-            long checksum = ResourceTransformUtils.toChecksum(dependency);
-
-            checksums.put(dependency, checksum);
-        } catch (IOException e)
-        {
-            throw new RuntimeException(e);
-        }
+        long checksum = ResourceTransformUtils.toChecksum(dependency);
 
+        checksums.put(dependency, checksum);
     }
 
     /**
@@ -49,7 +42,7 @@ public class ContentChangeTracker implements ResourceDependencies
      *
      * @return true if a change has occurred
      */
-    public boolean changes() throws IOException
+    public boolean dirty() throws IOException
     {
         for (Map.Entry<Resource, Long> e : checksums.entrySet())
         {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java
index b5f3709..1fd40d9 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java
@@ -19,19 +19,12 @@ import com.github.sommeri.less4j.LessCompiler;
 import com.github.sommeri.less4j.LessSource;
 import com.github.sommeri.less4j.core.DefaultLessCompiler;
 import org.apache.commons.io.IOUtils;
-import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.internal.services.assets.BytestreamCache;
-import org.apache.tapestry5.ioc.IOOperation;
-import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.Resource;
-import org.apache.tapestry5.ioc.annotations.Symbol;
-import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.services.assets.ResourceDependencies;
 import org.apache.tapestry5.services.assets.ResourceTransformer;
-import org.slf4j.Logger;
 
 import java.io.*;
-import java.util.Map;
 
 /**
  * Direct wrapper around the LessCompiler, so that Less source files may use {@code @import}, which isn't
@@ -41,41 +34,6 @@ public class LessResourceTransformer implements ResourceTransformer
 {
     private final LessCompiler compiler = new DefaultLessCompiler();
 
-    private final OperationTracker tracker;
-
-    private final Logger logger;
-
-    private final boolean cacheEnabled;
-
-    private final Map<Resource, Cached> cache = CollectionFactory.newConcurrentMap();
-
-    static class Cached
-    {
-        final BytestreamCache compiled;
-
-        final ContentChangeTracker tracker;
-
-        Cached(BytestreamCache compiled, ContentChangeTracker tracker)
-        {
-            this.compiled = compiled;
-            this.tracker = tracker;
-        }
-
-        InputStream openStream() throws IOException
-        {
-            return compiled.openStream();
-        }
-    }
-
-
-    public LessResourceTransformer(OperationTracker tracker, Logger logger, @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode)
-    {
-        this.tracker = tracker;
-        this.logger = logger;
-
-        cacheEnabled = !productionMode;
-    }
-
     public String getTransformedContentType()
     {
         return "text/css";
@@ -132,72 +90,32 @@ public class LessResourceTransformer implements ResourceTransformer
     }
 
 
-    public InputStream transform(final Resource source, final ResourceDependencies dependencies) throws IOException
+    public InputStream transform(Resource source, ResourceDependencies dependencies) throws IOException
     {
-        if (cacheEnabled)
-        {
-            Cached cached = cache.get(source);
 
-            if (cached != null && !cached.tracker.changes())
-            {
-                logger.info(String.format("Resource %s (and any dependencies) are unchanged; serving compiled Less content from cache",
-                        source));
+        BytestreamCache compiled = invokeLessCompiler(source, dependencies);
 
-                return cached.openStream();
-            }
-
-            ContentChangeTracker tracker1 = new ContentChangeTracker();
-            tracker1.addDependency(source);
-
-            BytestreamCache compiled = invokeLessCompiler(source, new ResourceDependenciesSplitter(dependencies, tracker1));
-
-            cached = new Cached(compiled, tracker1);
-
-            cache.put(source, cached);
-
-            return cached.openStream();
-        } else
-        {
-
-            BytestreamCache compiled = invokeLessCompiler(source, dependencies);
-
-            return compiled.openStream();
-        }
+        return compiled.openStream();
     }
 
-    private BytestreamCache invokeLessCompiler(final Resource source, final ResourceDependencies dependencies) throws IOException
+    private BytestreamCache invokeLessCompiler(Resource source, ResourceDependencies dependencies) throws IOException
     {
-
-        return tracker.perform(String.format("Compiling %s from Less to CSS", source), new IOOperation<BytestreamCache>()
+        try
         {
-            public BytestreamCache perform() throws IOException
-            {
-                long start = System.nanoTime();
-
-                try
-                {
-                    LessSource lessSource = new ResourceLessSource(source, dependencies);
-
-                    LessCompiler.CompilationResult compilationResult = compiler.compile(lessSource);
-
-                    BytestreamCache result = new BytestreamCache(compilationResult.getCss().getBytes("utf-8"));
+            LessSource lessSource = new ResourceLessSource(source, dependencies);
 
-                    logger.info(String.format("Compiled %s to Less in %.2f ms",
-                            source, ResourceTransformUtils.nanosToMillis(System.nanoTime() - start)));
+            LessCompiler.CompilationResult compilationResult = compiler.compile(lessSource);
 
-                    // Currently, ignoring any warnings.
+            // Currently, ignoring any warnings.
 
-                    return result;
+            return new BytestreamCache(compilationResult.getCss().getBytes("utf-8"));
 
-                } catch (Less4jException ex)
-                {
-                    throw new IOException(ex);
-                } catch (UnsupportedEncodingException ex)
-                {
-                    throw new IOException(ex);
-                }
-
-            }
-        });
+        } catch (Less4jException ex)
+        {
+            throw new IOException(ex);
+        } catch (UnsupportedEncodingException ex)
+        {
+            throw new IOException(ex);
+        }
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformUtils.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformUtils.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformUtils.java
index b236633..d0d9178 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformUtils.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformUtils.java
@@ -32,7 +32,7 @@ public class ResourceTransformUtils
         return ((double) nanos) * NANOS_TO_MILLIS;
     }
 
-    public static long toChecksum(Resource resource) throws IOException
+    public static long toChecksum(Resource resource)
     {
         Adler32 checksum = new Adler32();
 
@@ -56,11 +56,13 @@ public class ResourceTransformUtils
                 checksum.update(buffer, 0, length);
             }
 
+            is.close();
+
             // Reduces it down to just 32 bits which we express in hex.'
             return checksum.getValue();
-        } finally
+        } catch (IOException ex)
         {
-            is.close();
+            throw new RuntimeException(ex);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactory.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactory.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactory.java
index dde5c36..18436b3 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactory.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactory.java
@@ -36,10 +36,28 @@ public interface ResourceTransformerFactory
      *         for debugging: source name, e.g., "CoffeeScript"
      * @param targetName
      *         for debugging: target name, e.g., "JavaScript"
-     * @param enableCache
-     *         if true, the transformer will cache results (this is only used in development mode)
+     * @param cacheMode
+     *         Indicates if and how the compiled content should be cached (in development mode only)
      * @return transformer
      * @see org.apache.tapestry5.wro4j.services.ResourceProcessorSource
      */
-    ResourceTransformer createCompiler(String contentType, String processorName, String sourceName, String targetName, boolean enableCache);
+    ResourceTransformer createCompiler(String contentType, String processorName, String sourceName, String targetName, CacheMode cacheMode);
+
+    /**
+     * Constructs a compiler around a another ResourceTransformer implementation. In development mode, the wrapped version
+     * will handle caching, as well as logging output of timing for the real implementation.
+     *
+     * @param sourceName
+     *         for debugging: source name, e.g., "Less"
+     * @param targetName
+     *         for debugging: target name, e.g., "CSS"
+     * @param transformer
+     *         performs the actual work
+     * @param cacheMode
+     *         Indicates if and how the compiled content should be cached (in development mode only)
+     * @return transformer
+     * @see org.apache.tapestry5.wro4j.services.ResourceProcessorSource
+     */
+    ResourceTransformer createCompiler(String contentType, String sourceName, String targetName, ResourceTransformer transformer, CacheMode cacheMode);
+
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactoryImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactoryImpl.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactoryImpl.java
index f8f3dec..bebbfcf 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactoryImpl.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactoryImpl.java
@@ -14,21 +14,23 @@
 
 package org.apache.tapestry5.internal.wro4j;
 
+import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.internal.TapestryInternalUtils;
 import org.apache.tapestry5.internal.services.assets.BytestreamCache;
 import org.apache.tapestry5.ioc.IOOperation;
 import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.annotations.PostInjection;
+import org.apache.tapestry5.ioc.annotations.Symbol;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.services.assets.ResourceDependencies;
 import org.apache.tapestry5.services.assets.ResourceTransformer;
+import org.apache.tapestry5.wro4j.WRO4JSymbols;
 import org.apache.tapestry5.wro4j.services.ResourceProcessor;
 import org.apache.tapestry5.wro4j.services.ResourceProcessorSource;
 import org.slf4j.Logger;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
 import java.util.Map;
 
 public class ResourceTransformerFactoryImpl implements ResourceTransformerFactory
@@ -39,28 +41,46 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
 
     private final OperationTracker tracker;
 
-    public ResourceTransformerFactoryImpl(Logger logger, ResourceProcessorSource source, OperationTracker tracker)
+    private final boolean productionMode;
+
+    private final File cacheDir;
+
+    public ResourceTransformerFactoryImpl(Logger logger, ResourceProcessorSource source, OperationTracker tracker,
+                                          @Symbol(SymbolConstants.PRODUCTION_MODE)
+                                          boolean productionMode,
+                                          @Symbol(WRO4JSymbols.CACHE_DIR)
+                                          String cacheDir)
     {
         this.logger = logger;
         this.source = source;
         this.tracker = tracker;
-    }
+        this.productionMode = productionMode;
 
+        this.cacheDir = new File(cacheDir);
 
-    static class Compiled
-    {
-        /**
-         * Checksum of the raw source file.
-         */
-        final long checksum;
+        if (!productionMode)
+        {
+            logger.info(String.format("Using %s to store compiled assets (development mode only).", cacheDir));
+        }
+    }
 
-        private final BytestreamCache bytestreamCache;
+    @PostInjection
+    public void createCacheDir()
+    {
+        cacheDir.mkdirs();
+    }
 
+    static class Compiled extends ContentChangeTracker
+    {
+        private BytestreamCache bytestreamCache;
 
-        Compiled(long checksum, InputStream stream) throws IOException
+        Compiled(Resource root)
         {
-            this.checksum = checksum;
+            addDependency(root);
+        }
 
+        void store(InputStream stream) throws IOException
+        {
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
 
             TapestryInternalUtils.copy(stream, bos);
@@ -77,15 +97,46 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
         }
     }
 
-    public ResourceTransformer createCompiler(final String contentType, String processorName, final String sourceName, final String targetName, boolean enableCache)
+
+    public ResourceTransformer createCompiler(final String contentType, String processorName, final String sourceName, final String targetName, CacheMode cacheMode)
     {
         // This does the real work:
         ResourceProcessor resourceProcessor = source.getProcessor(processorName);
 
         // And this adapts it to the API.
-        final ResourceTransformer coreCompiler = createCoreCompiler(contentType, sourceName, targetName, resourceProcessor);
+        ResourceTransformer coreCompiler = createCoreCompiler(contentType, sourceName, targetName, resourceProcessor);
+
+        return createCompiler(contentType, sourceName, targetName, coreCompiler, cacheMode);
+
+    }
+
+    public ResourceTransformer createCompiler(String contentType, String sourceName, String targetName, ResourceTransformer transformer, CacheMode cacheMode)
+    {
+        ResourceTransformer trackingCompiler = wrapWithTracking(sourceName, targetName, transformer);
+
+        if (productionMode)
+        {
+            return trackingCompiler;
+        }
+
+        ResourceTransformer timingCompiler = wrapWithTiming(targetName, trackingCompiler);
+
+        switch (cacheMode)
+        {
+            case NONE:
+
+                return timingCompiler;
 
-        return enableCache ? wrapWithCaching(coreCompiler, targetName) : coreCompiler;
+            case SINGLE_FILE:
+
+                return wrapWithFileSystemCaching(timingCompiler, targetName);
+
+            case MULTIPLE_FILE:
+
+                return wrapWithInMemoryCaching(timingCompiler, targetName);
+        }
+
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
     }
 
     private ResourceTransformer createCoreCompiler(final String contentType, final String sourceName, final String targetName, final ResourceProcessor resourceProcessor)
@@ -101,25 +152,62 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
             {
                 final String description = String.format("Compiling %s from %s to %s", source, sourceName, targetName);
 
+                InputStream result = resourceProcessor.process(description,
+                        source.toURL().toString(),
+                        source.openStream(), contentType);
+
+                return result;
+
+            }
+        };
+    }
+
+    private ResourceTransformer wrapWithTracking(final String sourceName, final String targetName, final ResourceTransformer core)
+    {
+        return new ResourceTransformer()
+        {
+            public String getTransformedContentType()
+            {
+                return core.getTransformedContentType();
+            }
+
+            public InputStream transform(final Resource source, final ResourceDependencies dependencies) throws IOException
+            {
+                final String description = String.format("Compiling %s from %s to %s", source, sourceName, targetName);
+
                 return tracker.perform(description, new IOOperation<InputStream>()
                 {
                     public InputStream perform() throws IOException
                     {
-                        final long startTime = System.nanoTime();
+                        return core.transform(source, dependencies);
+                    }
+                });
+            }
+        };
+    }
 
-                        InputStream result = resourceProcessor.process(description,
-                                source.toURL().toString(),
-                                source.openStream(), contentType);
+    private ResourceTransformer wrapWithTiming(final String targetName, final ResourceTransformer coreCompiler)
+    {
+        return new ResourceTransformer()
+        {
+            public String getTransformedContentType()
+            {
+                return coreCompiler.getTransformedContentType();
+            }
 
-                        final long elapsedTime = System.nanoTime() - startTime;
+            public InputStream transform(final Resource source, final ResourceDependencies dependencies) throws IOException
+            {
+                final long startTime = System.nanoTime();
 
-                        logger.info(String.format("Compiled %s to %s in %.2f ms",
-                                source, targetName,
-                                ResourceTransformUtils.nanosToMillis(elapsedTime)));
+                InputStream result = coreCompiler.transform(source, dependencies);
 
-                        return result;
-                    }
-                });
+                final long elapsedTime = System.nanoTime() - startTime;
+
+                logger.info(String.format("Compiled %s to %s in %.2f ms",
+                        source, targetName,
+                        ResourceTransformUtils.nanosToMillis(elapsedTime)));
+
+                return result;
             }
         };
     }
@@ -131,7 +219,7 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
      * somewhat primitive: a change to *any* resource in a given domain results in the cache of all of those resources
      * being discarded.
      */
-    private ResourceTransformer wrapWithCaching(final ResourceTransformer core, final String targetName)
+    private ResourceTransformer wrapWithInMemoryCaching(final ResourceTransformer core, final String targetName)
     {
         return new ResourceTransformer()
         {
@@ -144,22 +232,21 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
 
             public InputStream transform(Resource source, ResourceDependencies dependencies) throws IOException
             {
-                long checksum = ResourceTransformUtils.toChecksum(source);
-
                 Compiled compiled = cache.get(source);
 
-                if (compiled != null && compiled.checksum == checksum)
+                if (compiled != null && !compiled.dirty())
                 {
-                    logger.info(String.format("Resource %s is unchanged; serving compiled %s content from cache",
+                    logger.info(String.format("Resource %s and dependencies are unchanged; serving compiled %s content from in-memory cache",
                             source, targetName));
 
                     return compiled.openStream();
                 }
 
-                InputStream is = core.transform(source, dependencies);
+                compiled = new Compiled(source);
+
+                InputStream is = core.transform(source, new ResourceDependenciesSplitter(dependencies, compiled));
 
-                // There's probably a race condition here if the source changes as we are compiling it.
-                compiled = new Compiled(checksum, is);
+                compiled.store(is);
 
                 cache.put(source, compiled);
 
@@ -167,4 +254,54 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
             }
         };
     }
+
+    private ResourceTransformer wrapWithFileSystemCaching(final ResourceTransformer core, final String targetName)
+    {
+        return new ResourceTransformer()
+        {
+            public String getTransformedContentType()
+            {
+                return core.getTransformedContentType();
+            }
+
+            public InputStream transform(Resource source, ResourceDependencies dependencies) throws IOException
+            {
+                long checksum = ResourceTransformUtils.toChecksum(source);
+
+                String fileName = Long.toHexString(checksum) + "-" + source.getFile();
+
+                File cacheFile = new File(cacheDir, fileName);
+
+                if (cacheFile.exists())
+                {
+                    logger.debug(String.format("Serving up compiled %s content for %s from file system cache", targetName, source));
+
+                    return new BufferedInputStream(new FileInputStream(cacheFile));
+                }
+
+                InputStream compiled = core.transform(source, dependencies);
+
+                // We need the InputStream twice; once to return, and once to write out to the cache file for later.
+
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+                TapestryInternalUtils.copy(compiled, bos);
+
+                BytestreamCache cache = new BytestreamCache(bos);
+
+                writeToCacheFile(cacheFile, cache.openStream());
+
+                return cache.openStream();
+            }
+        };
+    }
+
+    private void writeToCacheFile(File file, InputStream stream) throws IOException
+    {
+        OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
+
+        TapestryInternalUtils.copy(stream, outputStream);
+
+        outputStream.close();
+    }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/WRO4JSymbols.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/WRO4JSymbols.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/WRO4JSymbols.java
new file mode 100644
index 0000000..96e8b2e
--- /dev/null
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/WRO4JSymbols.java
@@ -0,0 +1,25 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.wro4j;
+
+public class WRO4JSymbols
+{
+    /**
+     * Directory that stores cached copies of compiled CoffeeScript files. The directory will be created
+     * as necessary. This allows compilation (e.g., CoffeeScript to JavaScript) to be avoided after a restart.
+     * The default is from the {@code java.io.tmpdir} system property (which is not necessarily stable between executions).
+     */
+    public static final String CACHE_DIR = "tapestry.compiled-asset-cache-dir";
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java
index 5b35c23..e272448 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java
@@ -17,19 +17,20 @@ package org.apache.tapestry5.wro4j.modules;
 import com.github.sommeri.less4j.LessCompiler;
 import com.github.sommeri.less4j.core.parser.AntlrException;
 import org.apache.tapestry5.MarkupWriter;
-import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.internal.wro4j.*;
 import org.apache.tapestry5.ioc.MappedConfiguration;
 import org.apache.tapestry5.ioc.ServiceBinder;
 import org.apache.tapestry5.ioc.annotations.Contribute;
 import org.apache.tapestry5.ioc.annotations.Primary;
-import org.apache.tapestry5.ioc.annotations.Symbol;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.FactoryDefaults;
+import org.apache.tapestry5.ioc.services.SymbolProvider;
 import org.apache.tapestry5.services.ObjectRenderer;
 import org.apache.tapestry5.services.assets.ResourceMinimizer;
 import org.apache.tapestry5.services.assets.ResourceTransformer;
 import org.apache.tapestry5.services.assets.StreamableResourceSource;
+import org.apache.tapestry5.wro4j.WRO4JSymbols;
 import org.apache.tapestry5.wro4j.services.ResourceProcessorSource;
 import ro.isdc.wro.extensions.processor.js.GoogleClosureCompressorProcessor;
 import ro.isdc.wro.extensions.processor.js.RhinoCoffeeScriptProcessor;
@@ -51,6 +52,13 @@ public class WRO4JModule
         binder.bind(ResourceTransformerFactory.class, ResourceTransformerFactoryImpl.class);
     }
 
+    @Contribute(SymbolProvider.class)
+    @FactoryDefaults
+    public static void setupDefaultCacheDirectory(MappedConfiguration<String, Object> configuration)
+    {
+        configuration.add(WRO4JSymbols.CACHE_DIR, "${java.io.tmpdir}");
+    }
+
     /**
      * Configures the default set of processors.
      * <dl>
@@ -70,15 +78,16 @@ public class WRO4JModule
     }
 
     @Contribute(StreamableResourceSource.class)
-    public static void provideCompilers(MappedConfiguration<String, ResourceTransformer> configuration, ResourceTransformerFactory factory,
-                                        @Symbol(SymbolConstants.PRODUCTION_MODE)
-                                        boolean productionMode)
+    public static void provideCompilers(MappedConfiguration<String, ResourceTransformer> configuration, ResourceTransformerFactory factory)
     {
+        // contribution ids are file extensions:
+
         configuration.add("coffee",
-                factory.createCompiler("text/javascript", "CoffeeScriptCompiler", "CoffeeScript", "JavaScript", !productionMode));
+                factory.createCompiler("text/javascript", "CoffeeScriptCompiler", "CoffeeScript", "JavaScript", CacheMode.SINGLE_FILE));
 
-        // Had to create our own wrapper around Less4J to handle @imports correctly.
-        configuration.addInstance("less", LessResourceTransformer.class);
+        configuration.add("less",
+                factory.createCompiler("text/css", "Less", "CSS", new LessResourceTransformer(),
+                        CacheMode.MULTIPLE_FILE));
     }
 
     @Contribute(ResourceMinimizer.class)


[3/3] git commit: Add a file-system cache ofr single-input-file comilations (CoffeeScript) that can persist between executions

Posted by hl...@apache.org.
Add a file-system cache ofr single-input-file comilations (CoffeeScript) that can persist between executions


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/eaada44a
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/eaada44a
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/eaada44a

Branch: refs/heads/master
Commit: eaada44a8e6c83af346343c5d125015fe8ca6459
Parents: 2beaaa9
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Tue Jul 16 14:37:49 2013 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Tue Jul 16 14:37:49 2013 -0700

----------------------------------------------------------------------
 tapestry-wro4j/build.gradle                     |   4 +-
 .../tapestry5/internal/wro4j/CacheMode.java     |  40 ++++
 .../internal/wro4j/ContentChangeTracker.java    |  13 +-
 .../internal/wro4j/LessResourceTransformer.java | 116 ++--------
 .../internal/wro4j/ResourceTransformUtils.java  |   8 +-
 .../wro4j/ResourceTransformerFactory.java       |  24 ++-
 .../wro4j/ResourceTransformerFactoryImpl.java   | 209 +++++++++++++++----
 .../apache/tapestry5/wro4j/WRO4JSymbols.java    |  25 +++
 .../tapestry5/wro4j/modules/WRO4JModule.java    |  25 ++-
 9 files changed, 304 insertions(+), 160 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/build.gradle
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/build.gradle b/tapestry-wro4j/build.gradle
index 3c541e7..cd00fbc 100644
--- a/tapestry-wro4j/build.gradle
+++ b/tapestry-wro4j/build.gradle
@@ -38,5 +38,7 @@ jar.manifest {
 test {
     useJUnit()
 
-    systemProperties "geb.build.reportsDir": "$reporting.baseDir/geb"
+    systemProperties("geb.build.reportsDir": "$reporting.baseDir/geb",
+        "tapestry.compiled-asset-cache-dir": "$buildDir/compiled-asset-cache",
+        "tapestry.production-mode": "false")
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CacheMode.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CacheMode.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CacheMode.java
new file mode 100644
index 0000000..1410b3c
--- /dev/null
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CacheMode.java
@@ -0,0 +1,40 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.wro4j;
+
+/**
+ * Controls caching for {@link ResourceTransformerFactory} in <em>development mode</em>. In production mode, caching at this
+ * level is not needed, because artifacts are also cached later in the pipeline. This caching is all about avoid unwanted
+ */
+public enum CacheMode
+{
+    /**
+     * Cache the content on the file system, in the directory defined by {@link org.apache.tapestry5.wro4j.WRO4JSymbols#CACHE_DIR}.
+     * This allows compilation to be avoided even after a restart, as long as the source file has not changed. This only works
+     * for compilations that operate on a single file (such as CoffeeScript, but not Less, which has an {@code @import} statement).
+     */
+    SINGLE_FILE,
+
+    /**
+     * The source may be multiple files (e.g., Less). Cache in memory, and invalidate the cache if any of the multiple
+     * file's content changes.
+     */
+    MULTIPLE_FILE,
+
+    /**
+     * Do no caching. This is appropriate for extremely cheap compilers.
+     */
+    NONE;
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ContentChangeTracker.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ContentChangeTracker.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ContentChangeTracker.java
index 770509e..b0e2eb1 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ContentChangeTracker.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ContentChangeTracker.java
@@ -32,16 +32,9 @@ public class ContentChangeTracker implements ResourceDependencies
 
     public void addDependency(Resource dependency)
     {
-        try
-        {
-            long checksum = ResourceTransformUtils.toChecksum(dependency);
-
-            checksums.put(dependency, checksum);
-        } catch (IOException e)
-        {
-            throw new RuntimeException(e);
-        }
+        long checksum = ResourceTransformUtils.toChecksum(dependency);
 
+        checksums.put(dependency, checksum);
     }
 
     /**
@@ -49,7 +42,7 @@ public class ContentChangeTracker implements ResourceDependencies
      *
      * @return true if a change has occurred
      */
-    public boolean changes() throws IOException
+    public boolean dirty() throws IOException
     {
         for (Map.Entry<Resource, Long> e : checksums.entrySet())
         {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java
index b5f3709..1fd40d9 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java
@@ -19,19 +19,12 @@ import com.github.sommeri.less4j.LessCompiler;
 import com.github.sommeri.less4j.LessSource;
 import com.github.sommeri.less4j.core.DefaultLessCompiler;
 import org.apache.commons.io.IOUtils;
-import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.internal.services.assets.BytestreamCache;
-import org.apache.tapestry5.ioc.IOOperation;
-import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.Resource;
-import org.apache.tapestry5.ioc.annotations.Symbol;
-import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.services.assets.ResourceDependencies;
 import org.apache.tapestry5.services.assets.ResourceTransformer;
-import org.slf4j.Logger;
 
 import java.io.*;
-import java.util.Map;
 
 /**
  * Direct wrapper around the LessCompiler, so that Less source files may use {@code @import}, which isn't
@@ -41,41 +34,6 @@ public class LessResourceTransformer implements ResourceTransformer
 {
     private final LessCompiler compiler = new DefaultLessCompiler();
 
-    private final OperationTracker tracker;
-
-    private final Logger logger;
-
-    private final boolean cacheEnabled;
-
-    private final Map<Resource, Cached> cache = CollectionFactory.newConcurrentMap();
-
-    static class Cached
-    {
-        final BytestreamCache compiled;
-
-        final ContentChangeTracker tracker;
-
-        Cached(BytestreamCache compiled, ContentChangeTracker tracker)
-        {
-            this.compiled = compiled;
-            this.tracker = tracker;
-        }
-
-        InputStream openStream() throws IOException
-        {
-            return compiled.openStream();
-        }
-    }
-
-
-    public LessResourceTransformer(OperationTracker tracker, Logger logger, @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode)
-    {
-        this.tracker = tracker;
-        this.logger = logger;
-
-        cacheEnabled = !productionMode;
-    }
-
     public String getTransformedContentType()
     {
         return "text/css";
@@ -132,72 +90,32 @@ public class LessResourceTransformer implements ResourceTransformer
     }
 
 
-    public InputStream transform(final Resource source, final ResourceDependencies dependencies) throws IOException
+    public InputStream transform(Resource source, ResourceDependencies dependencies) throws IOException
     {
-        if (cacheEnabled)
-        {
-            Cached cached = cache.get(source);
 
-            if (cached != null && !cached.tracker.changes())
-            {
-                logger.info(String.format("Resource %s (and any dependencies) are unchanged; serving compiled Less content from cache",
-                        source));
+        BytestreamCache compiled = invokeLessCompiler(source, dependencies);
 
-                return cached.openStream();
-            }
-
-            ContentChangeTracker tracker1 = new ContentChangeTracker();
-            tracker1.addDependency(source);
-
-            BytestreamCache compiled = invokeLessCompiler(source, new ResourceDependenciesSplitter(dependencies, tracker1));
-
-            cached = new Cached(compiled, tracker1);
-
-            cache.put(source, cached);
-
-            return cached.openStream();
-        } else
-        {
-
-            BytestreamCache compiled = invokeLessCompiler(source, dependencies);
-
-            return compiled.openStream();
-        }
+        return compiled.openStream();
     }
 
-    private BytestreamCache invokeLessCompiler(final Resource source, final ResourceDependencies dependencies) throws IOException
+    private BytestreamCache invokeLessCompiler(Resource source, ResourceDependencies dependencies) throws IOException
     {
-
-        return tracker.perform(String.format("Compiling %s from Less to CSS", source), new IOOperation<BytestreamCache>()
+        try
         {
-            public BytestreamCache perform() throws IOException
-            {
-                long start = System.nanoTime();
-
-                try
-                {
-                    LessSource lessSource = new ResourceLessSource(source, dependencies);
-
-                    LessCompiler.CompilationResult compilationResult = compiler.compile(lessSource);
-
-                    BytestreamCache result = new BytestreamCache(compilationResult.getCss().getBytes("utf-8"));
+            LessSource lessSource = new ResourceLessSource(source, dependencies);
 
-                    logger.info(String.format("Compiled %s to Less in %.2f ms",
-                            source, ResourceTransformUtils.nanosToMillis(System.nanoTime() - start)));
+            LessCompiler.CompilationResult compilationResult = compiler.compile(lessSource);
 
-                    // Currently, ignoring any warnings.
+            // Currently, ignoring any warnings.
 
-                    return result;
+            return new BytestreamCache(compilationResult.getCss().getBytes("utf-8"));
 
-                } catch (Less4jException ex)
-                {
-                    throw new IOException(ex);
-                } catch (UnsupportedEncodingException ex)
-                {
-                    throw new IOException(ex);
-                }
-
-            }
-        });
+        } catch (Less4jException ex)
+        {
+            throw new IOException(ex);
+        } catch (UnsupportedEncodingException ex)
+        {
+            throw new IOException(ex);
+        }
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformUtils.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformUtils.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformUtils.java
index b236633..d0d9178 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformUtils.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformUtils.java
@@ -32,7 +32,7 @@ public class ResourceTransformUtils
         return ((double) nanos) * NANOS_TO_MILLIS;
     }
 
-    public static long toChecksum(Resource resource) throws IOException
+    public static long toChecksum(Resource resource)
     {
         Adler32 checksum = new Adler32();
 
@@ -56,11 +56,13 @@ public class ResourceTransformUtils
                 checksum.update(buffer, 0, length);
             }
 
+            is.close();
+
             // Reduces it down to just 32 bits which we express in hex.'
             return checksum.getValue();
-        } finally
+        } catch (IOException ex)
         {
-            is.close();
+            throw new RuntimeException(ex);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactory.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactory.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactory.java
index dde5c36..18436b3 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactory.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactory.java
@@ -36,10 +36,28 @@ public interface ResourceTransformerFactory
      *         for debugging: source name, e.g., "CoffeeScript"
      * @param targetName
      *         for debugging: target name, e.g., "JavaScript"
-     * @param enableCache
-     *         if true, the transformer will cache results (this is only used in development mode)
+     * @param cacheMode
+     *         Indicates if and how the compiled content should be cached (in development mode only)
      * @return transformer
      * @see org.apache.tapestry5.wro4j.services.ResourceProcessorSource
      */
-    ResourceTransformer createCompiler(String contentType, String processorName, String sourceName, String targetName, boolean enableCache);
+    ResourceTransformer createCompiler(String contentType, String processorName, String sourceName, String targetName, CacheMode cacheMode);
+
+    /**
+     * Constructs a compiler around a another ResourceTransformer implementation. In development mode, the wrapped version
+     * will handle caching, as well as logging output of timing for the real implementation.
+     *
+     * @param sourceName
+     *         for debugging: source name, e.g., "Less"
+     * @param targetName
+     *         for debugging: target name, e.g., "CSS"
+     * @param transformer
+     *         performs the actual work
+     * @param cacheMode
+     *         Indicates if and how the compiled content should be cached (in development mode only)
+     * @return transformer
+     * @see org.apache.tapestry5.wro4j.services.ResourceProcessorSource
+     */
+    ResourceTransformer createCompiler(String contentType, String sourceName, String targetName, ResourceTransformer transformer, CacheMode cacheMode);
+
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactoryImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactoryImpl.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactoryImpl.java
index f8f3dec..bebbfcf 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactoryImpl.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/ResourceTransformerFactoryImpl.java
@@ -14,21 +14,23 @@
 
 package org.apache.tapestry5.internal.wro4j;
 
+import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.internal.TapestryInternalUtils;
 import org.apache.tapestry5.internal.services.assets.BytestreamCache;
 import org.apache.tapestry5.ioc.IOOperation;
 import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.annotations.PostInjection;
+import org.apache.tapestry5.ioc.annotations.Symbol;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.services.assets.ResourceDependencies;
 import org.apache.tapestry5.services.assets.ResourceTransformer;
+import org.apache.tapestry5.wro4j.WRO4JSymbols;
 import org.apache.tapestry5.wro4j.services.ResourceProcessor;
 import org.apache.tapestry5.wro4j.services.ResourceProcessorSource;
 import org.slf4j.Logger;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
 import java.util.Map;
 
 public class ResourceTransformerFactoryImpl implements ResourceTransformerFactory
@@ -39,28 +41,46 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
 
     private final OperationTracker tracker;
 
-    public ResourceTransformerFactoryImpl(Logger logger, ResourceProcessorSource source, OperationTracker tracker)
+    private final boolean productionMode;
+
+    private final File cacheDir;
+
+    public ResourceTransformerFactoryImpl(Logger logger, ResourceProcessorSource source, OperationTracker tracker,
+                                          @Symbol(SymbolConstants.PRODUCTION_MODE)
+                                          boolean productionMode,
+                                          @Symbol(WRO4JSymbols.CACHE_DIR)
+                                          String cacheDir)
     {
         this.logger = logger;
         this.source = source;
         this.tracker = tracker;
-    }
+        this.productionMode = productionMode;
 
+        this.cacheDir = new File(cacheDir);
 
-    static class Compiled
-    {
-        /**
-         * Checksum of the raw source file.
-         */
-        final long checksum;
+        if (!productionMode)
+        {
+            logger.info(String.format("Using %s to store compiled assets (development mode only).", cacheDir));
+        }
+    }
 
-        private final BytestreamCache bytestreamCache;
+    @PostInjection
+    public void createCacheDir()
+    {
+        cacheDir.mkdirs();
+    }
 
+    static class Compiled extends ContentChangeTracker
+    {
+        private BytestreamCache bytestreamCache;
 
-        Compiled(long checksum, InputStream stream) throws IOException
+        Compiled(Resource root)
         {
-            this.checksum = checksum;
+            addDependency(root);
+        }
 
+        void store(InputStream stream) throws IOException
+        {
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
 
             TapestryInternalUtils.copy(stream, bos);
@@ -77,15 +97,46 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
         }
     }
 
-    public ResourceTransformer createCompiler(final String contentType, String processorName, final String sourceName, final String targetName, boolean enableCache)
+
+    public ResourceTransformer createCompiler(final String contentType, String processorName, final String sourceName, final String targetName, CacheMode cacheMode)
     {
         // This does the real work:
         ResourceProcessor resourceProcessor = source.getProcessor(processorName);
 
         // And this adapts it to the API.
-        final ResourceTransformer coreCompiler = createCoreCompiler(contentType, sourceName, targetName, resourceProcessor);
+        ResourceTransformer coreCompiler = createCoreCompiler(contentType, sourceName, targetName, resourceProcessor);
+
+        return createCompiler(contentType, sourceName, targetName, coreCompiler, cacheMode);
+
+    }
+
+    public ResourceTransformer createCompiler(String contentType, String sourceName, String targetName, ResourceTransformer transformer, CacheMode cacheMode)
+    {
+        ResourceTransformer trackingCompiler = wrapWithTracking(sourceName, targetName, transformer);
+
+        if (productionMode)
+        {
+            return trackingCompiler;
+        }
+
+        ResourceTransformer timingCompiler = wrapWithTiming(targetName, trackingCompiler);
+
+        switch (cacheMode)
+        {
+            case NONE:
+
+                return timingCompiler;
 
-        return enableCache ? wrapWithCaching(coreCompiler, targetName) : coreCompiler;
+            case SINGLE_FILE:
+
+                return wrapWithFileSystemCaching(timingCompiler, targetName);
+
+            case MULTIPLE_FILE:
+
+                return wrapWithInMemoryCaching(timingCompiler, targetName);
+        }
+
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
     }
 
     private ResourceTransformer createCoreCompiler(final String contentType, final String sourceName, final String targetName, final ResourceProcessor resourceProcessor)
@@ -101,25 +152,62 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
             {
                 final String description = String.format("Compiling %s from %s to %s", source, sourceName, targetName);
 
+                InputStream result = resourceProcessor.process(description,
+                        source.toURL().toString(),
+                        source.openStream(), contentType);
+
+                return result;
+
+            }
+        };
+    }
+
+    private ResourceTransformer wrapWithTracking(final String sourceName, final String targetName, final ResourceTransformer core)
+    {
+        return new ResourceTransformer()
+        {
+            public String getTransformedContentType()
+            {
+                return core.getTransformedContentType();
+            }
+
+            public InputStream transform(final Resource source, final ResourceDependencies dependencies) throws IOException
+            {
+                final String description = String.format("Compiling %s from %s to %s", source, sourceName, targetName);
+
                 return tracker.perform(description, new IOOperation<InputStream>()
                 {
                     public InputStream perform() throws IOException
                     {
-                        final long startTime = System.nanoTime();
+                        return core.transform(source, dependencies);
+                    }
+                });
+            }
+        };
+    }
 
-                        InputStream result = resourceProcessor.process(description,
-                                source.toURL().toString(),
-                                source.openStream(), contentType);
+    private ResourceTransformer wrapWithTiming(final String targetName, final ResourceTransformer coreCompiler)
+    {
+        return new ResourceTransformer()
+        {
+            public String getTransformedContentType()
+            {
+                return coreCompiler.getTransformedContentType();
+            }
 
-                        final long elapsedTime = System.nanoTime() - startTime;
+            public InputStream transform(final Resource source, final ResourceDependencies dependencies) throws IOException
+            {
+                final long startTime = System.nanoTime();
 
-                        logger.info(String.format("Compiled %s to %s in %.2f ms",
-                                source, targetName,
-                                ResourceTransformUtils.nanosToMillis(elapsedTime)));
+                InputStream result = coreCompiler.transform(source, dependencies);
 
-                        return result;
-                    }
-                });
+                final long elapsedTime = System.nanoTime() - startTime;
+
+                logger.info(String.format("Compiled %s to %s in %.2f ms",
+                        source, targetName,
+                        ResourceTransformUtils.nanosToMillis(elapsedTime)));
+
+                return result;
             }
         };
     }
@@ -131,7 +219,7 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
      * somewhat primitive: a change to *any* resource in a given domain results in the cache of all of those resources
      * being discarded.
      */
-    private ResourceTransformer wrapWithCaching(final ResourceTransformer core, final String targetName)
+    private ResourceTransformer wrapWithInMemoryCaching(final ResourceTransformer core, final String targetName)
     {
         return new ResourceTransformer()
         {
@@ -144,22 +232,21 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
 
             public InputStream transform(Resource source, ResourceDependencies dependencies) throws IOException
             {
-                long checksum = ResourceTransformUtils.toChecksum(source);
-
                 Compiled compiled = cache.get(source);
 
-                if (compiled != null && compiled.checksum == checksum)
+                if (compiled != null && !compiled.dirty())
                 {
-                    logger.info(String.format("Resource %s is unchanged; serving compiled %s content from cache",
+                    logger.info(String.format("Resource %s and dependencies are unchanged; serving compiled %s content from in-memory cache",
                             source, targetName));
 
                     return compiled.openStream();
                 }
 
-                InputStream is = core.transform(source, dependencies);
+                compiled = new Compiled(source);
+
+                InputStream is = core.transform(source, new ResourceDependenciesSplitter(dependencies, compiled));
 
-                // There's probably a race condition here if the source changes as we are compiling it.
-                compiled = new Compiled(checksum, is);
+                compiled.store(is);
 
                 cache.put(source, compiled);
 
@@ -167,4 +254,54 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
             }
         };
     }
+
+    private ResourceTransformer wrapWithFileSystemCaching(final ResourceTransformer core, final String targetName)
+    {
+        return new ResourceTransformer()
+        {
+            public String getTransformedContentType()
+            {
+                return core.getTransformedContentType();
+            }
+
+            public InputStream transform(Resource source, ResourceDependencies dependencies) throws IOException
+            {
+                long checksum = ResourceTransformUtils.toChecksum(source);
+
+                String fileName = Long.toHexString(checksum) + "-" + source.getFile();
+
+                File cacheFile = new File(cacheDir, fileName);
+
+                if (cacheFile.exists())
+                {
+                    logger.debug(String.format("Serving up compiled %s content for %s from file system cache", targetName, source));
+
+                    return new BufferedInputStream(new FileInputStream(cacheFile));
+                }
+
+                InputStream compiled = core.transform(source, dependencies);
+
+                // We need the InputStream twice; once to return, and once to write out to the cache file for later.
+
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+                TapestryInternalUtils.copy(compiled, bos);
+
+                BytestreamCache cache = new BytestreamCache(bos);
+
+                writeToCacheFile(cacheFile, cache.openStream());
+
+                return cache.openStream();
+            }
+        };
+    }
+
+    private void writeToCacheFile(File file, InputStream stream) throws IOException
+    {
+        OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
+
+        TapestryInternalUtils.copy(stream, outputStream);
+
+        outputStream.close();
+    }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/WRO4JSymbols.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/WRO4JSymbols.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/WRO4JSymbols.java
new file mode 100644
index 0000000..96e8b2e
--- /dev/null
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/WRO4JSymbols.java
@@ -0,0 +1,25 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.wro4j;
+
+public class WRO4JSymbols
+{
+    /**
+     * Directory that stores cached copies of compiled CoffeeScript files. The directory will be created
+     * as necessary. This allows compilation (e.g., CoffeeScript to JavaScript) to be avoided after a restart.
+     * The default is from the {@code java.io.tmpdir} system property (which is not necessarily stable between executions).
+     */
+    public static final String CACHE_DIR = "tapestry.compiled-asset-cache-dir";
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/eaada44a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java
----------------------------------------------------------------------
diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java
index 5b35c23..e272448 100644
--- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java
+++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java
@@ -17,19 +17,20 @@ package org.apache.tapestry5.wro4j.modules;
 import com.github.sommeri.less4j.LessCompiler;
 import com.github.sommeri.less4j.core.parser.AntlrException;
 import org.apache.tapestry5.MarkupWriter;
-import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.internal.wro4j.*;
 import org.apache.tapestry5.ioc.MappedConfiguration;
 import org.apache.tapestry5.ioc.ServiceBinder;
 import org.apache.tapestry5.ioc.annotations.Contribute;
 import org.apache.tapestry5.ioc.annotations.Primary;
-import org.apache.tapestry5.ioc.annotations.Symbol;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.FactoryDefaults;
+import org.apache.tapestry5.ioc.services.SymbolProvider;
 import org.apache.tapestry5.services.ObjectRenderer;
 import org.apache.tapestry5.services.assets.ResourceMinimizer;
 import org.apache.tapestry5.services.assets.ResourceTransformer;
 import org.apache.tapestry5.services.assets.StreamableResourceSource;
+import org.apache.tapestry5.wro4j.WRO4JSymbols;
 import org.apache.tapestry5.wro4j.services.ResourceProcessorSource;
 import ro.isdc.wro.extensions.processor.js.GoogleClosureCompressorProcessor;
 import ro.isdc.wro.extensions.processor.js.RhinoCoffeeScriptProcessor;
@@ -51,6 +52,13 @@ public class WRO4JModule
         binder.bind(ResourceTransformerFactory.class, ResourceTransformerFactoryImpl.class);
     }
 
+    @Contribute(SymbolProvider.class)
+    @FactoryDefaults
+    public static void setupDefaultCacheDirectory(MappedConfiguration<String, Object> configuration)
+    {
+        configuration.add(WRO4JSymbols.CACHE_DIR, "${java.io.tmpdir}");
+    }
+
     /**
      * Configures the default set of processors.
      * <dl>
@@ -70,15 +78,16 @@ public class WRO4JModule
     }
 
     @Contribute(StreamableResourceSource.class)
-    public static void provideCompilers(MappedConfiguration<String, ResourceTransformer> configuration, ResourceTransformerFactory factory,
-                                        @Symbol(SymbolConstants.PRODUCTION_MODE)
-                                        boolean productionMode)
+    public static void provideCompilers(MappedConfiguration<String, ResourceTransformer> configuration, ResourceTransformerFactory factory)
     {
+        // contribution ids are file extensions:
+
         configuration.add("coffee",
-                factory.createCompiler("text/javascript", "CoffeeScriptCompiler", "CoffeeScript", "JavaScript", !productionMode));
+                factory.createCompiler("text/javascript", "CoffeeScriptCompiler", "CoffeeScript", "JavaScript", CacheMode.SINGLE_FILE));
 
-        // Had to create our own wrapper around Less4J to handle @imports correctly.
-        configuration.addInstance("less", LessResourceTransformer.class);
+        configuration.add("less",
+                factory.createCompiler("text/css", "Less", "CSS", new LessResourceTransformer(),
+                        CacheMode.MULTIPLE_FILE));
     }
 
     @Contribute(ResourceMinimizer.class)


[2/3] git commit: Add cross-reference documentation

Posted by hl...@apache.org.
Add cross-reference documentation


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/2beaaa9b
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/2beaaa9b
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/2beaaa9b

Branch: refs/heads/master
Commit: 2beaaa9b75faa6799b69687914e685515841e782
Parents: 476669d
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Tue Jul 16 12:53:54 2013 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Tue Jul 16 12:53:54 2013 -0700

----------------------------------------------------------------------
 .../java/org/apache/tapestry5/ioc/services/SymbolProvider.java  | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/2beaaa9b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/SymbolProvider.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/SymbolProvider.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/SymbolProvider.java
index 7d39037..8a1f134 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/SymbolProvider.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/SymbolProvider.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008 The Apache Software Foundation
+// Copyright 2006, 2008, 2013 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -21,6 +21,9 @@ import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
  * <p/>
  * This is the service interface for the FactoryDefaults and ApplicationDefaults services; each of these takes a
  * configuration mapping symbols to their values.
+ *
+ * @see FactoryDefaults
+ * @see ApplicationDefaults
  */
 @UsesMappedConfiguration(String.class)
 public interface SymbolProvider