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 2011/03/02 22:39:04 UTC

svn commit: r1076400 - in /tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5: ./ internal/services/ internal/services/assets/ services/ services/assets/

Author: hlship
Date: Wed Mar  2 21:39:04 2011
New Revision: 1076400

URL: http://svn.apache.org/viewvc?rev=1076400&view=rev
Log:
TAP5-73: Define the ResourceMinimizer service (as a do-nothing placeholder) and thread it into StreamableResourceSource and StackAssetRequestHandler

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/DefaultResourceMinimizer.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSMinimizingInterceptor.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ResourceMinimizer.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java?rev=1076400&r1=1076399&r2=1076400&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java Wed Mar  2 21:39:04 2011
@@ -17,6 +17,7 @@ package org.apache.tapestry5;
 import org.apache.tapestry5.internal.services.AssetDispatcher;
 import org.apache.tapestry5.services.ComponentClassTransformWorker;
 import org.apache.tapestry5.services.assets.AssetPathConstructor;
+import org.apache.tapestry5.services.assets.ResourceMinimizer;
 import org.apache.tapestry5.services.javascript.JavaScriptStack;
 
 /**
@@ -325,19 +326,23 @@ public class SymbolConstants
      * @since 5.2.0
      */
     public static final String PAGE_POOL_ENABLED = "tapestry.page-pool-enabled";
-    
+
     /**
-     * If "true" and {@link #PRODUCTION_MODE} is off, comments will be rendered before and after the rendering of any component
-     * allowing more visibility into which components rendered which markup. Defaults to "false". Component render tracing may be
+     * If "true" and {@link #PRODUCTION_MODE} is off, comments will be rendered before and after the rendering of any
+     * component
+     * allowing more visibility into which components rendered which markup. Defaults to "false". Component render
+     * tracing may be
      * enabled per-request by the presence of a request parameter "t:component-trace" with a value of "true".
      * 
      * @since 5.2.5
      */
     public static final String COMPONENT_RENDER_TRACING_ENABLED = "tapestry.component-render-tracing-enabled";
-    
+
     /**
-     * The hostname that application should use when constructing an absolute URL. The default is "", i.e. an empty string,
-     * in which case system will use request.getServerName(). Not the same as environment variable HOSTNAME, but you can also 
+     * The hostname that application should use when constructing an absolute URL. The default is "", i.e. an empty
+     * string,
+     * in which case system will use request.getServerName(). Not the same as environment variable HOSTNAME, but you can
+     * also
      * contribute "$HOSTNAME" as the value to make it the same as the environment variable HOSTNAME.
      * 
      * @since 5.3.0
@@ -345,18 +350,32 @@ public class SymbolConstants
     public static final String HOSTNAME = "tapestry.hostname";
 
     /**
-     * The hostport that application should use when constructing an absolute URL. The default is "0", i.e. use the port value from
+     * The hostport that application should use when constructing an absolute URL. The default is "0", i.e. use the port
+     * value from
      * the request.
      * 
      * @since 5.3.0
      */
     public static final String HOSTPORT = "tapestry.hostport";
-    
+
     /**
-     * The secure (https) hostport that application should use when constructing an absolute URL. The default is "0", i.e. use 
+     * The secure (https) hostport that application should use when constructing an absolute URL. The default is "0",
+     * i.e. use
      * the value from the request.
      * 
      * @since 5.3.0
      */
     public static final String HOSTPORT_SECURE = "tapestry.hostport-secure";
+
+    /**
+     * If "true", then resources (individually or when aggregated into stacks) will be minimized via the
+     * {@link ResourceMinimizer} service. If "false", then minification is disabled. Tracks production mode
+     * (minification is normally disabled in development mode).
+     * <p>
+     * Note that Tapestry's default implementation of {@link ResourceMinimizer} does nothing; minification is provided
+     * by add-on libraries.
+     * 
+     * @since 5.3.0
+     */
+    public static final String MINIFICATION_ENABLED = "tapestry.enable-minification";
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java?rev=1076400&r1=1076399&r2=1076400&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java Wed Mar  2 21:39:04 2011
@@ -19,6 +19,7 @@ import java.io.IOException;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.services.assets.StreamableResource;
 import org.apache.tapestry5.services.assets.StreamableResourceSource;
 
 /**
@@ -37,4 +38,13 @@ public interface ResourceStreamer
      * @see StreamableResourceSource
      */
     void streamResource(Resource resource) throws IOException;
+
+    /**
+     * Streams a resource that has been assembled elsewhere.
+     * 
+     * @param resource
+     * @throws IOException
+     * @since 5.3.0
+     */
+    void streamResource(StreamableResource resource) throws IOException;
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java?rev=1076400&r1=1076399&r2=1076400&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java Wed Mar  2 21:39:04 2011
@@ -73,15 +73,20 @@ public class ResourceStreamerImpl implem
             return;
         }
 
-        long ifModifiedSince = 0;
-
         StreamableResourceProcessing processing = analyzer.isGZipSupported() ? StreamableResourceProcessing.COMPRESSION_ENABLED
                 : StreamableResourceProcessing.COMPRESSION_DISABLED;
 
         StreamableResource streamable = streamableResourceSource.getStreamableResource(resource, processing);
 
+        streamResource(streamable);
+    }
+
+    public void streamResource(StreamableResource streamable) throws IOException
+    {
         long lastModified = streamable.getLastModified();
 
+        long ifModifiedSince = 0;
+
         try
         {
             ifModifiedSince = request.getDateHeader(IF_MODIFIED_SINCE_HEADER);

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/DefaultResourceMinimizer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/DefaultResourceMinimizer.java?rev=1076400&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/DefaultResourceMinimizer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/DefaultResourceMinimizer.java Wed Mar  2 21:39:04 2011
@@ -0,0 +1,32 @@
+// Copyright 2011 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 java.io.IOException;
+
+import org.apache.tapestry5.services.assets.ResourceMinimizer;
+import org.apache.tapestry5.services.assets.StreamableResource;
+
+/**
+ * Default implementation that simply returns the resource unchanged.
+ */
+public class DefaultResourceMinimizer implements ResourceMinimizer
+{
+    /** Does nothing; an override of this service can be installed to provide minimization. */
+    public StreamableResource minimize(StreamableResource resource) throws IOException
+    {
+        return resource;
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSMinimizingInterceptor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSMinimizingInterceptor.java?rev=1076400&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSMinimizingInterceptor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSMinimizingInterceptor.java Wed Mar  2 21:39:04 2011
@@ -0,0 +1,51 @@
+// Copyright 2011 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 java.io.IOException;
+
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.services.assets.ResourceMinimizer;
+import org.apache.tapestry5.services.assets.StreamableResource;
+import org.apache.tapestry5.services.assets.StreamableResourceProcessing;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
+
+/**
+ * Loops the result through the {@link ResourceMinimizer} service.
+ */
+public class SRSMinimizingInterceptor implements StreamableResourceSource
+{
+    private final StreamableResourceSource delegate;
+
+    private final ResourceMinimizer minimizer;
+
+    public SRSMinimizingInterceptor(StreamableResourceSource delegate, ResourceMinimizer minimizer)
+    {
+        this.delegate = delegate;
+        this.minimizer = minimizer;
+    }
+
+    public StreamableResource getStreamableResource(Resource baseResource, StreamableResourceProcessing processing)
+            throws IOException
+    {
+        StreamableResource streamable = delegate.getStreamableResource(baseResource, processing);
+
+        if (processing != StreamableResourceProcessing.FOR_AGGREGATION)
+            return minimizer.minimize(streamable);
+
+        return streamable;
+    }
+
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java?rev=1076400&r1=1076399&r2=1076400&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java Wed Mar  2 21:39:04 2011
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry5.internal.services.assets;
 
+import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -27,7 +28,7 @@ import java.util.zip.GZIPOutputStream;
 
 import org.apache.tapestry5.Asset;
 import org.apache.tapestry5.SymbolConstants;
-import org.apache.tapestry5.internal.InternalConstants;
+import org.apache.tapestry5.internal.services.ResourceStreamer;
 import org.apache.tapestry5.ioc.Resource;
 import org.apache.tapestry5.ioc.annotations.PostInjection;
 import org.apache.tapestry5.ioc.annotations.Symbol;
@@ -39,6 +40,8 @@ import org.apache.tapestry5.services.Req
 import org.apache.tapestry5.services.Response;
 import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
 import org.apache.tapestry5.services.assets.AssetRequestHandler;
+import org.apache.tapestry5.services.assets.CompressionStatus;
+import org.apache.tapestry5.services.assets.ResourceMinimizer;
 import org.apache.tapestry5.services.assets.StreamableResource;
 import org.apache.tapestry5.services.assets.StreamableResourceProcessing;
 import org.apache.tapestry5.services.assets.StreamableResourceSource;
@@ -47,6 +50,8 @@ import org.apache.tapestry5.services.jav
 
 public class StackAssetRequestHandler implements AssetRequestHandler, InvalidationListener
 {
+    private static final String JAVASCRIPT_CONTENT_TYPE = "text/javascript";
+
     private final StreamableResourceSource streamableResourceSource;
 
     private final JavaScriptStackSource javascriptStackSource;
@@ -55,27 +60,34 @@ public class StackAssetRequestHandler im
 
     private final ResponseCompressionAnalyzer compressionAnalyzer;
 
-    private final boolean productionMode;
+    private final ResourceStreamer resourceStreamer;
 
     private final Pattern pathPattern = Pattern.compile("^(.+)/(.+)\\.js$");
 
     // Two caches, keyed on extra path. Both are accessed only from synchronized blocks.
-    private final Map<String, BytestreamCache> uncompressedCache = CollectionFactory.newCaseInsensitiveMap();
+    private final Map<String, StreamableResource> uncompressedCache = CollectionFactory.newCaseInsensitiveMap();
+
+    private final Map<String, StreamableResource> compressedCache = CollectionFactory.newCaseInsensitiveMap();
+
+    private final ResourceMinimizer resourceMinimizer;
 
-    private final Map<String, BytestreamCache> compressedCache = CollectionFactory.newCaseInsensitiveMap();
+    private final boolean minificationEnabled;
 
     public StackAssetRequestHandler(StreamableResourceSource streamableResourceSource,
             JavaScriptStackSource javascriptStackSource, LocalizationSetter localizationSetter,
-            ResponseCompressionAnalyzer compressionAnalyzer,
+            ResponseCompressionAnalyzer compressionAnalyzer, ResourceStreamer resourceStreamer,
+            ResourceMinimizer resourceMinimizer,
 
-            @Symbol(SymbolConstants.PRODUCTION_MODE)
-            boolean productionMode)
+            @Symbol(SymbolConstants.MINIFICATION_ENABLED)
+            boolean minificationEnabled)
     {
         this.streamableResourceSource = streamableResourceSource;
         this.javascriptStackSource = javascriptStackSource;
         this.localizationSetter = localizationSetter;
         this.compressionAnalyzer = compressionAnalyzer;
-        this.productionMode = productionMode;
+        this.resourceStreamer = resourceStreamer;
+        this.resourceMinimizer = resourceMinimizer;
+        this.minificationEnabled = minificationEnabled;
     }
 
     @PostInjection
@@ -88,32 +100,9 @@ public class StackAssetRequestHandler im
     {
         boolean compress = compressionAnalyzer.isGZipSupported();
 
-        BytestreamCache cachedStream = getStream(extraPath, compress);
-
-        // The whole point of this is to force the client to aggressively cache the combined, virtual
-        // stack asset.
-
-        long lastModified = System.currentTimeMillis();
-        response.setDateHeader("Last-Modified", lastModified);
-
-        if (productionMode)
-            response.setDateHeader("Expires", lastModified + InternalConstants.TEN_YEARS);
-
-        response.disableCompression();
-
-        response.setContentLength(cachedStream.size());
-
-        if (compress)
-            response.setHeader(InternalConstants.CONTENT_ENCODING_HEADER, InternalConstants.GZIP_CONTENT_ENCODING);
-
-        // CSS aggregation is problematic, because of relative URLs inside the CSS files. For the
-        // moment, only JavaScript is supported.
-
-        OutputStream output = response.getOutputStream("text/javascript");
-
-        cachedStream.writeTo(output);
+        StreamableResource resource = getResource(extraPath, compress);
 
-        output.close();
+        resourceStreamer.streamResource(resource);
 
         return true;
     }
@@ -125,18 +114,18 @@ public class StackAssetRequestHandler im
         compressedCache.clear();
     }
 
-    private BytestreamCache getStream(String extraPath, boolean compressed) throws IOException
+    private StreamableResource getResource(String extraPath, boolean compressed) throws IOException
     {
-        return compressed ? getCompressedStream(extraPath) : getUncompressedStream(extraPath);
+        return compressed ? getCompressedResource(extraPath) : getUncompressedResource(extraPath);
     }
 
-    private synchronized BytestreamCache getCompressedStream(String extraPath) throws IOException
+    private synchronized StreamableResource getCompressedResource(String extraPath) throws IOException
     {
-        BytestreamCache result = compressedCache.get(extraPath);
+        StreamableResource result = compressedCache.get(extraPath);
 
         if (result == null)
         {
-            BytestreamCache uncompressed = getUncompressedStream(extraPath);
+            StreamableResource uncompressed = getUncompressedResource(extraPath);
             result = compressStream(uncompressed);
             compressedCache.put(extraPath, result);
         }
@@ -144,9 +133,9 @@ public class StackAssetRequestHandler im
         return result;
     }
 
-    private synchronized BytestreamCache getUncompressedStream(String extraPath) throws IOException
+    private synchronized StreamableResource getUncompressedResource(String extraPath) throws IOException
     {
-        BytestreamCache result = uncompressedCache.get(extraPath);
+        StreamableResource result = uncompressedCache.get(extraPath);
 
         if (result == null)
         {
@@ -157,7 +146,7 @@ public class StackAssetRequestHandler im
         return result;
     }
 
-    private BytestreamCache assembleStackContent(String extraPath) throws IOException
+    private StreamableResource assembleStackContent(String extraPath) throws IOException
     {
         Matcher matcher = pathPattern.matcher(extraPath);
 
@@ -170,21 +159,24 @@ public class StackAssetRequestHandler im
         return assembleStackContent(localeName, stackName);
     }
 
-    private BytestreamCache assembleStackContent(String localeName, String stackName) throws IOException
+    private StreamableResource assembleStackContent(String localeName, String stackName) throws IOException
     {
         localizationSetter.setNonPeristentLocaleFromLocaleName(localeName);
 
         JavaScriptStack stack = javascriptStackSource.getStack(stackName);
         List<Asset> libraries = stack.getJavaScriptLibraries();
 
-        return assembleStackContent(libraries);
+        StreamableResource stackContent = assembleStackContent(libraries);
+
+        return minificationEnabled ? resourceMinimizer.minimize(stackContent) : stackContent;
     }
 
-    private BytestreamCache assembleStackContent(List<Asset> libraries) throws IOException
+    private StreamableResource assembleStackContent(List<Asset> libraries) throws IOException
     {
         ByteArrayOutputStream stream = new ByteArrayOutputStream();
         OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8");
         PrintWriter writer = new PrintWriter(osw, true);
+        long lastModified = 0;
 
         JSONArray paths = new JSONArray();
 
@@ -196,36 +188,36 @@ public class StackAssetRequestHandler im
 
             writer.format("\n/* %s */;\n", path);
 
-            streamLibraryContent(library, stream);
-        }
+            Resource resource = library.getResource();
 
-        writer.format("\n;/**/\nTapestry.markScriptLibrariesLoaded(%s);\n", paths);
+            StreamableResource streamable = streamableResourceSource.getStreamableResource(resource,
+                    StreamableResourceProcessing.FOR_AGGREGATION);
 
-        writer.close();
+            streamable.streamTo(stream);
 
-        return new BytestreamCache(stream);
-    }
+            lastModified = Math.max(lastModified, streamable.getLastModified());
+        }
 
-    private void streamLibraryContent(Asset library, OutputStream outputStream) throws IOException
-    {
-        Resource resource = library.getResource();
+        writer.format("\n;/**/\nTapestry.markScriptLibrariesLoaded(%s);\n", paths);
 
-        StreamableResource streamable = streamableResourceSource.getStreamableResource(resource,
-                StreamableResourceProcessing.FOR_AGGREGATION);
+        writer.close();
 
-        streamable.streamTo(outputStream);
+        return new StreamableResourceImpl(JAVASCRIPT_CONTENT_TYPE, CompressionStatus.COMPRESSABLE, lastModified,
+                new BytestreamCache(stream));
     }
 
-    private BytestreamCache compressStream(BytestreamCache uncompressed) throws IOException
+    private StreamableResource compressStream(StreamableResource uncompressed) throws IOException
     {
         ByteArrayOutputStream compressed = new ByteArrayOutputStream();
-        OutputStream compressor = new GZIPOutputStream(compressed);
+        OutputStream compressor = new BufferedOutputStream(new GZIPOutputStream(compressed));
 
-        uncompressed.writeTo(compressor);
+        uncompressed.streamTo(compressor);
 
         compressor.close();
 
-        return new BytestreamCache(compressed);
-    }
+        BytestreamCache cache = new BytestreamCache(compressed);
 
+        return new StreamableResourceImpl(JAVASCRIPT_CONTENT_TYPE, CompressionStatus.COMPRESSED,
+                uncompressed.getLastModified(), cache);
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=1076400&r1=1076399&r2=1076400&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Wed Mar  2 21:39:04 2011
@@ -2470,10 +2470,8 @@ public final class TapestryModule
         configuration.add(SymbolConstants.APPLICATION_VERSION, Long.toHexString(random.nextLong()));
 
         configuration.add(SymbolConstants.OMIT_GENERATOR_META, "false");
-        configuration.add(SymbolConstants.GZIP_COMPRESSION_ENABLED, "true");
 
         configuration.add(SymbolConstants.SECURE_ENABLED, SymbolConstants.PRODUCTION_MODE_VALUE);
-        configuration.add(SymbolConstants.COMBINE_SCRIPTS, SymbolConstants.PRODUCTION_MODE_VALUE);
         configuration.add(SymbolConstants.COMPACT_JSON, SymbolConstants.PRODUCTION_MODE_VALUE);
 
         configuration.add(SymbolConstants.ENCODE_LOCALE_INTO_PATH, "true");

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java?rev=1076400&r1=1076399&r2=1076400&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java Wed Mar  2 21:39:04 2011
@@ -17,11 +17,13 @@ package org.apache.tapestry5.services.as
 import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.internal.services.assets.CompressionAnalyzerImpl;
 import org.apache.tapestry5.internal.services.assets.ContentTypeAnalyzerImpl;
+import org.apache.tapestry5.internal.services.assets.DefaultResourceMinimizer;
 import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker;
 import org.apache.tapestry5.internal.services.assets.ResourceChangeTrackerImpl;
 import org.apache.tapestry5.internal.services.assets.SRSCachingInterceptor;
 import org.apache.tapestry5.internal.services.assets.SRSCompressedCachingInterceptor;
 import org.apache.tapestry5.internal.services.assets.SRSCompressingInterceptor;
+import org.apache.tapestry5.internal.services.assets.SRSMinimizingInterceptor;
 import org.apache.tapestry5.internal.services.assets.StreamableResourceSourceImpl;
 import org.apache.tapestry5.ioc.MappedConfiguration;
 import org.apache.tapestry5.ioc.ServiceBinder;
@@ -30,6 +32,8 @@ import org.apache.tapestry5.ioc.annotati
 import org.apache.tapestry5.ioc.annotations.Marker;
 import org.apache.tapestry5.ioc.annotations.Order;
 import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.ioc.services.FactoryDefaults;
+import org.apache.tapestry5.ioc.services.SymbolProvider;
 import org.apache.tapestry5.services.Core;
 
 /**
@@ -44,6 +48,16 @@ public class AssetsModule
         binder.bind(CompressionAnalyzer.class, CompressionAnalyzerImpl.class);
         binder.bind(ContentTypeAnalyzer.class, ContentTypeAnalyzerImpl.class);
         binder.bind(ResourceChangeTracker.class, ResourceChangeTrackerImpl.class);
+        binder.bind(ResourceMinimizer.class, DefaultResourceMinimizer.class);
+    }
+
+    @Contribute(SymbolProvider.class)
+    @FactoryDefaults
+    public static void setupSymbols(MappedConfiguration<String, String> configuration)
+    {
+        configuration.add(SymbolConstants.MINIFICATION_ENABLED, SymbolConstants.PRODUCTION_MODE_VALUE);
+        configuration.add(SymbolConstants.GZIP_COMPRESSION_ENABLED, "true");
+        configuration.add(SymbolConstants.COMBINE_SCRIPTS, SymbolConstants.PRODUCTION_MODE_VALUE);
     }
 
     // The use of decorators is to allow third-parties to get their own extensions
@@ -67,8 +81,7 @@ public class AssetsModule
         if (!gzipEnabled)
             return null;
 
-        SRSCompressedCachingInterceptor interceptor = new SRSCompressedCachingInterceptor(tracker,
-                delegate);
+        SRSCompressedCachingInterceptor interceptor = new SRSCompressedCachingInterceptor(tracker, delegate);
 
         tracker.addInvalidationListener(interceptor);
 
@@ -87,6 +100,18 @@ public class AssetsModule
         return interceptor;
     }
 
+    @Decorate(id = "Minification", serviceInterface = StreamableResourceSource.class)
+    @Order("after:Cache")
+    public StreamableResourceSource enableMinification(StreamableResourceSource delegate, ResourceMinimizer minimizer,
+            @Symbol(SymbolConstants.MINIFICATION_ENABLED)
+            boolean enabled)
+    {
+        if (enabled)
+            return new SRSMinimizingInterceptor(delegate, minimizer);
+
+        return null;
+    }
+
     /**
      * Adds content types:
      * <dl>

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ResourceMinimizer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ResourceMinimizer.java?rev=1076400&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ResourceMinimizer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ResourceMinimizer.java Wed Mar  2 21:39:04 2011
@@ -0,0 +1,38 @@
+// Copyright 2011 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.services.assets;
+
+import java.io.IOException;
+
+/**
+ * Certain kinds of resources can be minimized: this primarily refers to JavaScript and CSS, both of which contain
+ * whitespace, comments and other features that can be reduced.
+ * 
+ * @since 5.3.0
+ */
+public interface ResourceMinimizer
+{
+    /**
+     * Checks the {@linkplain StreamableResource#getContentType() content type} of the resource and applies an
+     * appropriate
+     * minimization to it if possible.
+     * 
+     * @param resource
+     *            to minimize
+     * @return the same resource, or a minimized replacement for the resource
+     * @throws IOException
+     */
+    StreamableResource minimize(StreamableResource resource) throws IOException;
+}