You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2013/04/02 15:01:39 UTC

[5/5] git commit: Add StreamableResource.getChecksum()

Add StreamableResource.getChecksum()


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

Branch: refs/heads/master
Commit: 557a745f2ddf92a545715d197436c3b177eef7d1
Parents: 3778a37
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Mon Apr 1 09:56:42 2013 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Tue Apr 2 06:01:18 2013 -0700

----------------------------------------------------------------------
 54_RELEASE_NOTES.txt                               |    5 +
 .../assets/BaseStreamableResourceImpl.java         |   84 +++++++++++++++
 .../internal/services/assets/CSSURLRewriter.java   |    7 +-
 .../assets/CompressedStreamableResource.java       |   60 ++++++++++
 .../assets/JavaScriptStackAssemblerImpl.java       |   37 +++----
 .../services/assets/SRSCompressingInterceptor.java |   20 +---
 .../services/assets/StreamableResourceImpl.java    |   63 ++---------
 .../assets/StreamableResourceSourceImpl.java       |    7 +-
 .../tapestry5/services/assets/AssetsModule.java    |    6 +-
 .../services/assets/StreamableResource.java        |   10 ++
 .../services/assets/CSSURLRewriterTests.groovy     |    8 +-
 .../internal/yuicompressor/AbstractMinimizer.java  |    8 +-
 .../yuicompressor/CSSResourceMinimizer.java        |    7 +-
 .../yuicompressor/JavaScriptResourceMinimizer.java |    5 +-
 14 files changed, 214 insertions(+), 113 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/54_RELEASE_NOTES.txt
----------------------------------------------------------------------
diff --git a/54_RELEASE_NOTES.txt b/54_RELEASE_NOTES.txt
index 75f9ac2..543c9c7 100644
--- a/54_RELEASE_NOTES.txt
+++ b/54_RELEASE_NOTES.txt
@@ -199,3 +199,8 @@ until the end of the request. A new configuration symbol can be used to turn thi
 ## AssetPathConstructor
 
 The interface for AssetPathConstructor was changed incompatibly, to allow for individual asset checksums.
+
+## StreamableResource Extended
+
+StreamableResource has been modified, adding a new `getChecksum()` method; this interface is rarely, if ever,
+used or implemented by application code.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BaseStreamableResourceImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BaseStreamableResourceImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BaseStreamableResourceImpl.java
new file mode 100644
index 0000000..c30eb33
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BaseStreamableResourceImpl.java
@@ -0,0 +1,84 @@
+// 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.services.assets;
+
+import org.apache.tapestry5.services.assets.CompressionStatus;
+import org.apache.tapestry5.services.assets.StreamableResource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public abstract class BaseStreamableResourceImpl implements StreamableResource
+{
+    protected final String description, contentType;
+
+    protected final CompressionStatus compression;
+
+    protected final long lastModified;
+
+    protected final BytestreamCache bytestreamCache;
+
+    public BaseStreamableResourceImpl(long lastModified, String description, BytestreamCache bytestreamCache, String contentType, CompressionStatus compression)
+    {
+        this.lastModified = lastModified;
+        this.description = description;
+        this.bytestreamCache = bytestreamCache;
+        this.contentType = contentType;
+        this.compression = compression;
+    }
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+    public CompressionStatus getCompression()
+    {
+        return compression;
+    }
+
+    public String getContentType()
+    {
+        return contentType;
+    }
+
+    public int getSize()
+    {
+        return bytestreamCache.size();
+    }
+
+    public long getLastModified()
+    {
+        return lastModified;
+    }
+
+    public void streamTo(OutputStream os) throws IOException
+    {
+        bytestreamCache.writeTo(os);
+    }
+
+    public InputStream openStream() throws IOException
+    {
+        return bytestreamCache.openStream();
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("StreamableResource<%s %s %s lastModified: %tc size: %d>", contentType, description, compression.name(),
+                lastModified, getSize());
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CSSURLRewriter.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CSSURLRewriter.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CSSURLRewriter.java
index 573c24a..2b3536c 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CSSURLRewriter.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CSSURLRewriter.java
@@ -44,11 +44,14 @@ public class CSSURLRewriter extends DelegatingSRS
 
     private final AssetSource assetSource;
 
-    public CSSURLRewriter(StreamableResourceSource delegate, OperationTracker tracker, AssetSource assetSource)
+    private final AssetChecksumGenerator checksumGenerator;
+
+    public CSSURLRewriter(StreamableResourceSource delegate, OperationTracker tracker, AssetSource assetSource, AssetChecksumGenerator checksumGenerator)
     {
         super(delegate);
         this.tracker = tracker;
         this.assetSource = assetSource;
+        this.checksumGenerator = checksumGenerator;
     }
 
     @Override
@@ -86,7 +89,7 @@ public class CSSURLRewriter extends DelegatingSRS
                         return new StreamableResourceImpl(base.getDescription(), "text/css",
                                 CompressionStatus.COMPRESSABLE,
                                 base.getLastModified(),
-                                cache);
+                                cache, checksumGenerator);
                     }
                 });
     }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressedStreamableResource.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressedStreamableResource.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressedStreamableResource.java
new file mode 100644
index 0000000..5f447b4
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressedStreamableResource.java
@@ -0,0 +1,60 @@
+// 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.services.assets;
+
+import org.apache.tapestry5.services.assets.CompressionStatus;
+import org.apache.tapestry5.services.assets.StreamableResource;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * GZip compressed representation of a {@link StreamableResource}.
+ *
+ * @since 5.4
+ */
+public class CompressedStreamableResource extends BaseStreamableResourceImpl
+{
+    private final StreamableResource base;
+
+    public String getChecksum() throws IOException
+    {
+        return base.getChecksum();
+    }
+
+    public CompressedStreamableResource(StreamableResource base) throws IOException
+    {
+        super(base.getLastModified(), base.getDescription(), compressContent(base), base.getContentType(), CompressionStatus.COMPRESSED);
+
+        assert base.getCompression() == CompressionStatus.COMPRESSABLE;
+
+        this.base = base;
+    }
+
+    private static BytestreamCache compressContent(StreamableResource resource) throws IOException
+    {
+        ByteArrayOutputStream compressed = new ByteArrayOutputStream(resource.getSize());
+        OutputStream compressor = new BufferedOutputStream(new GZIPOutputStream(compressed));
+
+        resource.streamTo(compressor);
+
+        compressor.close();
+
+        return new BytestreamCache(compressed);
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/JavaScriptStackAssemblerImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/JavaScriptStackAssemblerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/JavaScriptStackAssemblerImpl.java
index d84bb28..92d849a 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/JavaScriptStackAssemblerImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/JavaScriptStackAssemblerImpl.java
@@ -19,18 +19,17 @@ import org.apache.tapestry5.ioc.Resource;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.services.ThreadLocale;
 import org.apache.tapestry5.json.JSONArray;
-import org.apache.tapestry5.services.assets.CompressionStatus;
-import org.apache.tapestry5.services.assets.StreamableResource;
-import org.apache.tapestry5.services.assets.StreamableResourceProcessing;
-import org.apache.tapestry5.services.assets.StreamableResourceSource;
+import org.apache.tapestry5.services.assets.*;
 import org.apache.tapestry5.services.javascript.JavaScriptStack;
 import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
 
-import java.io.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.zip.GZIPOutputStream;
 
 public class JavaScriptStackAssemblerImpl implements JavaScriptStackAssembler
 {
@@ -44,17 +43,20 @@ public class JavaScriptStackAssemblerImpl implements JavaScriptStackAssembler
 
     private final JavaScriptStackSource stackSource;
 
+    private final AssetChecksumGenerator checksumGenerator;
+
     private final Map<String, StreamableResource> cache = CollectionFactory.newCaseInsensitiveMap();
 
     // TODO: Support for minimization
     // TODO: Support for aggregated CSS as well as aggregated JavaScript
 
-    public JavaScriptStackAssemblerImpl(ThreadLocale threadLocale, ResourceChangeTracker resourceChangeTracker, StreamableResourceSource streamableResourceSource, JavaScriptStackSource stackSource)
+    public JavaScriptStackAssemblerImpl(ThreadLocale threadLocale, ResourceChangeTracker resourceChangeTracker, StreamableResourceSource streamableResourceSource, JavaScriptStackSource stackSource, AssetChecksumGenerator checksumGenerator)
     {
         this.threadLocale = threadLocale;
         this.resourceChangeTracker = resourceChangeTracker;
         this.streamableResourceSource = streamableResourceSource;
         this.stackSource = stackSource;
+        this.checksumGenerator = checksumGenerator;
 
         resourceChangeTracker.clearOnInvalidation(cache);
     }
@@ -90,7 +92,9 @@ public class JavaScriptStackAssemblerImpl implements JavaScriptStackAssembler
     {
         if (compress)
         {
-            return compressStream(assembleJavascriptResourceForStack(locale, stackName, false));
+            StreamableResource uncompressed = assembleJavascriptResourceForStack(locale, stackName, false);
+
+            return new CompressedStreamableResource(uncompressed);
         }
 
         JavaScriptStack stack = stackSource.getStack(stackName);
@@ -137,23 +141,8 @@ public class JavaScriptStackAssemblerImpl implements JavaScriptStackAssembler
         return new StreamableResourceImpl(
                 description.toString(),
                 JAVASCRIPT_CONTENT_TYPE, CompressionStatus.COMPRESSABLE, lastModified,
-                new BytestreamCache(stream));
+                new BytestreamCache(stream), checksumGenerator);
     }
 
 
-    private StreamableResource compressStream(StreamableResource uncompressed) throws IOException
-    {
-        ByteArrayOutputStream compressed = new ByteArrayOutputStream();
-        OutputStream compressor = new BufferedOutputStream(new GZIPOutputStream(compressed));
-
-        uncompressed.streamTo(compressor);
-
-        compressor.close();
-
-        BytestreamCache cache = new BytestreamCache(compressed);
-
-        return new StreamableResourceImpl(uncompressed.getDescription(), JAVASCRIPT_CONTENT_TYPE, CompressionStatus.COMPRESSED,
-                uncompressed.getLastModified(), cache);
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java
index ce115ca..3781931 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java
@@ -1,4 +1,4 @@
-// Copyright 2011, 2012 The Apache Software Foundation
+// Copyright 2011-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.
@@ -17,10 +17,7 @@ package org.apache.tapestry5.internal.services.assets;
 import org.apache.tapestry5.ioc.Resource;
 import org.apache.tapestry5.services.assets.*;
 
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.util.zip.GZIPOutputStream;
 
 public class SRSCompressingInterceptor extends DelegatingSRS
 {
@@ -57,19 +54,6 @@ public class SRSCompressingInterceptor extends DelegatingSRS
             return uncompressed;
         }
 
-        ByteArrayOutputStream bos = new ByteArrayOutputStream(size);
-
-        GZIPOutputStream gos = new GZIPOutputStream(bos);
-        BufferedOutputStream buffered = new BufferedOutputStream(gos);
-
-        uncompressed.streamTo(buffered);
-
-        buffered.close();
-
-        BytestreamCache cache = new BytestreamCache(bos);
-
-        return new StreamableResourceImpl(uncompressed.getDescription(),
-                uncompressed.getContentType(), CompressionStatus.COMPRESSED,
-                uncompressed.getLastModified(), cache);
+        return new CompressedStreamableResource(uncompressed);
     }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java
index 364ebd8..fbf8156 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry5.internal.services.assets;
 
+import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
 import org.apache.tapestry5.services.assets.CompressionStatus;
 import org.apache.tapestry5.services.assets.StreamableResource;
 
@@ -21,67 +22,23 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
-public class StreamableResourceImpl implements StreamableResource
+public class StreamableResourceImpl extends BaseStreamableResourceImpl
 {
-    private final String description;
-
-    private final String contentType;
-
-    private final CompressionStatus compression;
-
-    private final long lastModified;
-
-    private final BytestreamCache bytestreamCache;
+    private final AssetChecksumGenerator assetChecksumGenerator;
 
     public StreamableResourceImpl(String description, String contentType, CompressionStatus compression, long lastModified,
-                                  BytestreamCache bytestreamCache)
-    {
-        this.description = description;
-        this.contentType = contentType;
-        this.compression = compression;
-        this.lastModified = lastModified;
-        this.bytestreamCache = bytestreamCache;
-    }
-
-    public String getDescription()
-    {
-        return description;
-    }
-
-    public CompressionStatus getCompression()
+                                  BytestreamCache bytestreamCache, AssetChecksumGenerator assetChecksumGenerator)
     {
-        return compression;
-    }
+        super(lastModified, description, bytestreamCache, contentType, compression);
 
-    public String getContentType()
-    {
-        return contentType;
+        this.assetChecksumGenerator = assetChecksumGenerator;
     }
 
-    public int getSize()
+    public String getChecksum() throws IOException
     {
-        return bytestreamCache.size();
+        // Currently, we rely on AssetChecksumGenerator to manage a cache, but that may be better done
+        // here (but must be threadsafe).
+        return assetChecksumGenerator.generateChecksum(this);
     }
 
-    public long getLastModified()
-    {
-        return lastModified;
-    }
-
-    public void streamTo(OutputStream os) throws IOException
-    {
-        bytestreamCache.writeTo(os);
-    }
-
-    public InputStream openStream() throws IOException
-    {
-        return bytestreamCache.openStream();
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("StreamableResource<%s %s %s lastModified: %tc size: %d>", contentType, description, compression.name(),
-                lastModified, getSize());
-    }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceSourceImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceSourceImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceSourceImpl.java
index dcef6bc..14e03dd 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceSourceImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceSourceImpl.java
@@ -35,14 +35,17 @@ public class StreamableResourceSourceImpl implements StreamableResourceSource
 
     private final ResourceChangeTracker resourceChangeTracker;
 
+    private final AssetChecksumGenerator checksumGenerator;
+
     public StreamableResourceSourceImpl(Map<String, ResourceTransformer> configuration,
                                         ContentTypeAnalyzer contentTypeAnalyzer, CompressionAnalyzer compressionAnalyzer,
-                                        ResourceChangeTracker resourceChangeTracker)
+                                        ResourceChangeTracker resourceChangeTracker, AssetChecksumGenerator checksumGenerator)
     {
         this.configuration = configuration;
         this.contentTypeAnalyzer = contentTypeAnalyzer;
         this.compressionAnalyzer = compressionAnalyzer;
         this.resourceChangeTracker = resourceChangeTracker;
+        this.checksumGenerator = checksumGenerator;
     }
 
     public Set<String> fileExtensionsForContentType(String contentType)
@@ -92,7 +95,7 @@ public class StreamableResourceSourceImpl implements StreamableResourceSource
         long lastModified = resourceChangeTracker.trackResource(baseResource);
 
         return new StreamableResourceImpl(baseResource.toString(), contentType, compressable ? CompressionStatus.COMPRESSABLE
-                : CompressionStatus.NOT_COMPRESSABLE, lastModified, bytestreamCache);
+                : CompressionStatus.NOT_COMPRESSABLE, lastModified, bytestreamCache, checksumGenerator);
     }
 
     private BytestreamCache readStream(InputStream stream) throws IOException

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java
index efb21a3..ad954c0 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java
@@ -130,9 +130,10 @@ public class AssetsModule
     @Order("after:Minification")
     public StreamableResourceSource enableCSSURLRewriting(StreamableResourceSource delegate,
                                                           OperationTracker tracker,
-                                                          AssetSource assetSource)
+                                                          AssetSource assetSource,
+                                                          AssetChecksumGenerator checksumGenerator)
     {
-        return new CSSURLRewriter(delegate, tracker, assetSource);
+        return new CSSURLRewriter(delegate, tracker, assetSource, checksumGenerator);
     }
 
     /**
@@ -257,7 +258,6 @@ public class AssetsModule
 
             // This is the 5.4 version; once 5.3 support is dropped, this can be simplified, and the
             // "meta/" prefix stripped out.
-
             String folderSuffix = folder.equals("") ? folder : "/" + folder;
 
             configuration.add("meta" + folderSuffix, "META-INF/assets" + folderSuffix);

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResource.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResource.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResource.java
index cb5e891..3edeff5 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResource.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResource.java
@@ -71,4 +71,14 @@ public interface StreamableResource
      * the HTTP request/response date headers).
      */
     long getLastModified();
+
+    /**
+     * Compute and return the checksum of the content for this asset; the checksum should be computed
+     * based on the uncompressed content.
+     *
+     * @return checksum for uncompressed content
+     * @since 5.4
+     * @see AssetChecksumGenerator#generateChecksum(StreamableResource)
+     */
+    String getChecksum() throws IOException;
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/assets/CSSURLRewriterTests.groovy
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/assets/CSSURLRewriterTests.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/assets/CSSURLRewriterTests.groovy
index e0d732d..69a92f4 100644
--- a/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/assets/CSSURLRewriterTests.groovy
+++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/assets/CSSURLRewriterTests.groovy
@@ -17,7 +17,7 @@ body {
 }
 '''
 
-        def rewriter = new CSSURLRewriter(null, null, null)
+        def rewriter = new CSSURLRewriter(null, null, null, null)
 
         assertNull rewriter.replaceURLs(input, null)
     }
@@ -43,7 +43,7 @@ body {
         replay()
 
 
-        def rewriter = new CSSURLRewriter(null, null, assetSource)
+        def rewriter = new CSSURLRewriter(null, null, assetSource, null)
 
         def output = rewriter.replaceURLs input, resource
 
@@ -65,7 +65,7 @@ body {
 }
 '''
 
-        def rewriter = new CSSURLRewriter(null, null, null)
+        def rewriter = new CSSURLRewriter(null, null, null, null)
 
         assertNull rewriter.replaceURLs(input, null)
     }
@@ -95,7 +95,7 @@ div.busy {
 
         replay()
 
-        def rewriter = new CSSURLRewriter(null, null, assetSource)
+        def rewriter = new CSSURLRewriter(null, null, assetSource, null)
 
         def output = rewriter.replaceURLs input, resource
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/AbstractMinimizer.java
----------------------------------------------------------------------
diff --git a/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/AbstractMinimizer.java b/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/AbstractMinimizer.java
index 47b4540..fc0b1ef 100644
--- a/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/AbstractMinimizer.java
+++ b/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/AbstractMinimizer.java
@@ -19,6 +19,7 @@ import org.apache.tapestry5.internal.services.assets.StreamableResourceImpl;
 import org.apache.tapestry5.ioc.IOOperation;
 import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
 import org.apache.tapestry5.services.assets.CompressionStatus;
 import org.apache.tapestry5.services.assets.ResourceMinimizer;
 import org.apache.tapestry5.services.assets.StreamableResource;
@@ -40,13 +41,16 @@ public abstract class AbstractMinimizer implements ResourceMinimizer
 
     protected final OperationTracker tracker;
 
+    private final AssetChecksumGenerator checksumGenerator;
+
     private final String resourceType;
 
-    public AbstractMinimizer(Logger logger, OperationTracker tracker, String resourceType)
+    public AbstractMinimizer(Logger logger, OperationTracker tracker, AssetChecksumGenerator checksumGenerator, String resourceType)
     {
         this.logger = logger;
         this.tracker = tracker;
         this.resourceType = resourceType;
+        this.checksumGenerator = checksumGenerator;
     }
 
     public StreamableResource minimize(final StreamableResource input) throws IOException
@@ -80,7 +84,7 @@ public abstract class AbstractMinimizer implements ResourceMinimizer
 
         StreamableResource output = new StreamableResourceImpl("minimized " + input.getDescription(),
                 input.getContentType(), CompressionStatus.COMPRESSABLE,
-                input.getLastModified(), new BytestreamCache(bos));
+                input.getLastModified(), new BytestreamCache(bos), checksumGenerator);
 
         if (logger.isInfoEnabled())
         {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/CSSResourceMinimizer.java
----------------------------------------------------------------------
diff --git a/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/CSSResourceMinimizer.java b/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/CSSResourceMinimizer.java
index e3f91e8..d786829 100644
--- a/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/CSSResourceMinimizer.java
+++ b/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/CSSResourceMinimizer.java
@@ -1,4 +1,4 @@
-// Copyright 2011 The Apache Software Foundation
+// Copyright 2011-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.
@@ -16,6 +16,7 @@ package org.apache.tapestry5.internal.yuicompressor;
 
 import com.yahoo.platform.yui.compressor.CssCompressor;
 import org.apache.tapestry5.ioc.OperationTracker;
+import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
 import org.apache.tapestry5.services.assets.StreamableResource;
 import org.slf4j.Logger;
 
@@ -30,9 +31,9 @@ import java.io.Writer;
  */
 public class CSSResourceMinimizer extends AbstractMinimizer
 {
-    public CSSResourceMinimizer(Logger logger, OperationTracker tracker)
+    public CSSResourceMinimizer(Logger logger, OperationTracker tracker, AssetChecksumGenerator checksumGenerator)
     {
-        super(logger, tracker, "CSS");
+        super(logger, tracker, checksumGenerator, "CSS");
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/557a745f/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/JavaScriptResourceMinimizer.java
----------------------------------------------------------------------
diff --git a/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/JavaScriptResourceMinimizer.java b/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/JavaScriptResourceMinimizer.java
index c8d823a..3be4014 100644
--- a/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/JavaScriptResourceMinimizer.java
+++ b/tapestry-yuicompressor/src/main/java/org/apache/tapestry5/internal/yuicompressor/JavaScriptResourceMinimizer.java
@@ -18,6 +18,7 @@ import com.yahoo.platform.yui.compressor.JavaScriptCompressor;
 import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
 import org.apache.tapestry5.services.assets.StreamableResource;
 import org.mozilla.javascript.ErrorReporter;
 import org.mozilla.javascript.EvaluatorException;
@@ -50,9 +51,9 @@ public class JavaScriptResourceMinimizer extends AbstractMinimizer
             "has already been declared in the same scope"
     };
 
-    public JavaScriptResourceMinimizer(final Logger logger, OperationTracker tracker)
+    public JavaScriptResourceMinimizer(Logger logger, OperationTracker tracker, AssetChecksumGenerator checksumGenerator)
     {
-        super(logger, tracker, "JavaScript");
+        super(logger, tracker, checksumGenerator, "JavaScript");
     }
 
     protected void doMinimize(final StreamableResource resource, Writer output) throws IOException