tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hls...@apache.org
Subject [1/7] git commit: Put in place a development-mode cache for compiled resources
Date Sat, 01 Jun 2013 01:19:58 GMT
Updated Branches:
  refs/heads/master 47c83bb83 -> 512ee3168


Put in place a development-mode cache for compiled resources


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

Branch: refs/heads/master
Commit: 466f3ddaef47b188a96a522f783cc14f9623af99
Parents: 47c83bb
Author: Howard M. Lewis Ship <hlship@apache.org>
Authored: Fri May 31 16:54:52 2013 -0700
Committer: Howard M. Lewis Ship <hlship@apache.org>
Committed: Fri May 31 16:54:52 2013 -0700

----------------------------------------------------------------------
 .../internal/wro4j/ResourceTransformerFactory.java |    6 +-
 .../wro4j/ResourceTransformerFactoryImpl.java      |  128 ++++++++++++++-
 .../tapestry5/wro4j/modules/WRO4JModule.java       |   11 +-
 3 files changed, 137 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/466f3dda/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 4016ebe..dde5c36 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
@@ -19,8 +19,8 @@ import org.apache.tapestry5.services.assets.ResourceTransformer;
 /**
  * Creates ResourceTransformer around a named {@link org.apache.tapestry5.wro4j.services.ResourceProcessor}.
  *
- * @since 5.4
  * @see org.apache.tapestry5.services.assets.StreamableResourceSource
+ * @since 5.4
  */
 public interface ResourceTransformerFactory
 {
@@ -36,8 +36,10 @@ 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)
      * @return transformer
      * @see org.apache.tapestry5.wro4j.services.ResourceProcessorSource
      */
-    ResourceTransformer createCompiler(String contentType, String processorName, String sourceName,
String targetName);
+    ResourceTransformer createCompiler(String contentType, String processorName, String sourceName,
String targetName, boolean enableCache);
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/466f3dda/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 cd5005d..64e67a7 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,17 +14,23 @@
 
 package org.apache.tapestry5.internal.wro4j;
 
+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.internal.util.CollectionFactory;
 import org.apache.tapestry5.services.assets.ResourceDependencies;
 import org.apache.tapestry5.services.assets.ResourceTransformer;
 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.util.Map;
+import java.util.zip.Adler32;
 
 public class ResourceTransformerFactoryImpl implements ResourceTransformerFactory
 {
@@ -43,10 +49,82 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
         this.tracker = tracker;
     }
 
-    public ResourceTransformer createCompiler(final String contentType, String processorName,
final String sourceName, final String targetName)
+
+    static class Compiled
+    {
+        /**
+         * Checksum of the raw source file.
+         */
+        final long checksum;
+
+        private final BytestreamCache bytestreamCache;
+
+
+        Compiled(long checksum, InputStream stream) throws IOException
+        {
+            this.checksum = checksum;
+
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+            TapestryInternalUtils.copy(stream, bos);
+
+            stream.close();
+            bos.close();
+
+            this.bytestreamCache = new BytestreamCache(bos);
+        }
+
+        InputStream openStream()
+        {
+            return bytestreamCache.openStream();
+        }
+    }
+
+    private long toChecksum(Resource resource) throws IOException
+    {
+        Adler32 checksum = new Adler32();
+
+        byte[] buffer = new byte[1024];
+
+        InputStream is = null;
+
+        try
+        {
+            is = resource.openStream();
+
+            while (true)
+            {
+                int length = is.read(buffer);
+
+                if (length < 0)
+                {
+                    break;
+                }
+
+                checksum.update(buffer, 0, length);
+            }
+
+            // Reduces it down to just 32 bits which we express in hex.'
+            return checksum.getValue();
+        } finally
+        {
+            is.close();
+        }
+    }
+
+    public ResourceTransformer createCompiler(final String contentType, String processorName,
final String sourceName, final String targetName, boolean enableCache)
     {
-        final ResourceProcessor compiler = source.getProcessor(processorName);
+        // 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);
+
+        return enableCache ? wrapWithCaching(coreCompiler, targetName) : coreCompiler;
+    }
+
+    private ResourceTransformer createCoreCompiler(final String contentType, final String
sourceName, final String targetName, final ResourceProcessor resourceProcessor)
+    {
         return new ResourceTransformer()
         {
             public String getTransformedContentType()
@@ -64,7 +142,7 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
                     {
                         final long startTime = System.nanoTime();
 
-                        InputStream result = compiler.process(description,
+                        InputStream result = resourceProcessor.process(description,
                                 source.toURL().toString(),
                                 source.openStream(), contentType);
 
@@ -80,4 +158,48 @@ public class ResourceTransformerFactoryImpl implements ResourceTransformerFactor
             }
         };
     }
+
+    /**
+     * Caching is not needed in production, because caching of streamable resources occurs
at a higher level
+     * (possibly after sources have been aggregated and minimized and gzipped). However,
in development, it is
+     * very important to avoid costly CoffeeScript compilation (or similar operations); Tapestry's
caching is
+     * 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)
+    {
+        return new ResourceTransformer()
+        {
+            final Map<Resource, Compiled> cache = CollectionFactory.newConcurrentMap();
+
+            public String getTransformedContentType()
+            {
+                return core.getTransformedContentType();
+            }
+
+            public InputStream transform(Resource source, ResourceDependencies dependencies)
throws IOException
+            {
+                long checksum = toChecksum(source);
+
+                Compiled compiled = cache.get(source);
+
+                if (compiled != null && compiled.checksum == checksum)
+                {
+                    logger.info(String.format("Resource %s is unchanged; serving compiled
%s content from cache",
+                            source, targetName));
+
+                    return compiled.openStream();
+                }
+
+                InputStream is = core.transform(source, dependencies);
+
+                // There's probably a race condition here if the source changes as we are
compiling it.
+                compiled = new Compiled(checksum, is);
+
+                cache.put(source, compiled);
+
+                return compiled.openStream();
+            }
+        };
+    }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/466f3dda/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 073d3aa..fa286ee 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
@@ -16,11 +16,13 @@ package org.apache.tapestry5.wro4j.modules;
 
 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.services.ObjectRenderer;
@@ -82,12 +84,15 @@ public class WRO4JModule
     }
 
     @Contribute(StreamableResourceSource.class)
-    public static void provideCompilers(MappedConfiguration<String, ResourceTransformer>
configuration, ResourceTransformerFactory factory)
+    public static void provideCompilers(MappedConfiguration<String, ResourceTransformer>
configuration, ResourceTransformerFactory factory,
+                                        @Symbol(SymbolConstants.PRODUCTION_MODE)
+                                        boolean productionMode)
     {
         configuration.add("coffee",
-                factory.createCompiler("text/javascript", "CoffeeScriptCompiler", "CoffeeScript",
"JavaScript"));
+                factory.createCompiler("text/javascript", "CoffeeScriptCompiler", "CoffeeScript",
"JavaScript", !productionMode));
 
-        configuration.add("less", factory.createCompiler("text/css", "LessCompiler", "Less",
"CSS"));
+        // We'll have to see how imports work in a Less file before we can get into whether
we can enable development-mode caching.
+        configuration.add("less", factory.createCompiler("text/css", "LessCompiler", "Less",
"CSS", false));
     }
 
     @Contribute(ResourceMinimizer.class)


Mime
View raw message