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/01 20:29:10 UTC
svn commit: r1075990 [1/2] - in /tapestry/tapestry5/trunk/tapestry-core/src:
main/java/org/apache/tapestry5/internal/services/
main/java/org/apache/tapestry5/internal/services/assets/
main/java/org/apache/tapestry5/services/ main/java/org/apache/tapest...
Author: hlship
Date: Tue Mar 1 19:29:08 2011
New Revision: 1075990
URL: http://svn.apache.org/viewvc?rev=1075990&view=rev
Log:
TAP5-73: Replace the old implementation of ResourceStreamer with a new one, organized around the new StreamableResourceSource service
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressionAnalyzerImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContentTypeAnalyzerImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ResourceChangeTracker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ResourceChangeTrackerImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCachingInterceptor.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressedCachingInterceptor.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceSourceImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/CompressionAnalyzer.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/CompressionStatus.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ContentTypeAnalyzer.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ResourceTransformer.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResource.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResourceFeature.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResourceSource.java
Removed:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StreamableResource.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StreamableResourceImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ResourceStreamerImplTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerImplTest.java
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceCache.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceCacheImpl.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/ResponseCompressionAnalyzerImpl.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/ResponseCompressionAnalyzer.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ResourceCacheImplTest.java
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java?rev=1075990&r1=1075989&r2=1075990&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java Tue Mar 1 19:29:08 2011
@@ -38,7 +38,17 @@ import org.apache.tapestry5.ioc.services
import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
import org.apache.tapestry5.ioc.services.PerthreadManager;
import org.apache.tapestry5.ioc.services.PropertyShadowBuilder;
-import org.apache.tapestry5.services.*;
+import org.apache.tapestry5.services.ComponentClasses;
+import org.apache.tapestry5.services.ComponentLayer;
+import org.apache.tapestry5.services.ComponentMessages;
+import org.apache.tapestry5.services.ComponentTemplates;
+import org.apache.tapestry5.services.Core;
+import org.apache.tapestry5.services.InvalidationEventHub;
+import org.apache.tapestry5.services.LinkCreationListener2;
+import org.apache.tapestry5.services.LocalizationSetter;
+import org.apache.tapestry5.services.RequestGlobals;
+import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
+import org.apache.tapestry5.services.UpdateListenerHub;
import org.apache.tapestry5.services.templates.ComponentTemplateLocator;
import org.slf4j.Logger;
@@ -91,6 +101,7 @@ public class InternalModule
binder.bind(AssetResourceLocator.class);
binder.bind(JavaScriptStackPathConstructor.class);
binder.bind(AjaxFormUpdateController.class);
+ binder.bind(ResourceCache.class, ResourceCacheImpl.class);
}
/**
@@ -263,14 +274,6 @@ public class InternalModule
};
}
- public ResourceCache buildResourceCache(@Autobuild
- ResourceCacheImpl service)
- {
- updateListenerHub.addUpdateListener(service);
-
- return service;
- }
-
public ComponentTemplateSource buildComponentTemplateSource(TemplateParser parser, @Primary
ComponentTemplateLocator locator, ClasspathURLConverter classpathURLConverter)
{
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceCache.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceCache.java?rev=1075990&r1=1075989&r2=1075990&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceCache.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceCache.java Tue Mar 1 19:29:08 2011
@@ -1,10 +1,10 @@
-// Copyright 2006, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2008, 2009, 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
+// 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,
@@ -28,7 +28,7 @@ public interface ResourceCache extends I
/**
* Returns true if the path requires that the client URL for the resource include a digest to validate that the
* client is authorized to access the resource.
- *
+ *
* @param resource
* @return true if digest is required for the resource
* @see ResourceDigestGenerator#requiresDigest(String)
@@ -36,17 +36,8 @@ public interface ResourceCache extends I
boolean requiresDigest(Resource resource);
/**
- * Returns the contents of the resource
- *
- * @param resource
- * @return access to compressed and uncompressed streams
- * @since 5.1.0.0
- */
- StreamableResource getStreamableResource(Resource resource);
-
- /**
* Returns the digest for the given path.
- *
+ *
* @param resource
* @return the digest, or null if the resource does not exist
*/
@@ -54,7 +45,7 @@ public interface ResourceCache extends I
/**
* Returns the time modified for the resource.
- *
+ *
* @param resource
* @return the date time modified for the path, or a negative value if the resource does not exist
*/
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceCacheImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceCacheImpl.java?rev=1075990&r1=1075989&r2=1075990&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceCacheImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceCacheImpl.java Tue Mar 1 19:29:08 2011
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 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
+// 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,
@@ -14,24 +14,22 @@
package org.apache.tapestry5.internal.services;
-import org.apache.tapestry5.internal.event.InvalidationEventHubImpl;
+import java.net.URL;
+import java.util.Map;
+
+import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker;
import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.annotations.PostInjection;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.ioc.internal.util.URLChangeTracker;
-import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
+import org.apache.tapestry5.services.InvalidationListener;
import org.apache.tapestry5.services.ResourceDigestGenerator;
-import org.apache.tapestry5.services.UpdateListener;
-import java.net.URL;
-import java.util.Map;
-
-public class ResourceCacheImpl extends InvalidationEventHubImpl implements ResourceCache,
- UpdateListener
+public class ResourceCacheImpl implements ResourceCache, InvalidationListener
{
- private final URLChangeTracker tracker;
-
private final ResourceDigestGenerator digestGenerator;
+ private final ResourceChangeTracker resourceChangeTracker;
+
private final Map<Resource, Cached> cache = CollectionFactory.newConcurrentMap();
final static long MISSING_RESOURCE_TIME_MODIFIED = -1L;
@@ -44,44 +42,28 @@ public class ResourceCacheImpl extends I
final long timeModified;
- final StreamableResource streamable;
-
Cached(Resource resource)
{
requiresDigest = digestGenerator.requiresDigest(resource.getPath());
URL url = resource.toURL();
- // The url may be null when a request for a protected asset arrives, because the
- // Resource initially is for the file with the digest incorporated into the path, which
- // means
- // no underlying file exists. Subsequently, we'll strip out the digest and resolve
- // to an actual resource.
+ digest = (requiresDigest && url != null) ? digestGenerator.generateDigest(url) : null;
- digest = (requiresDigest && url != null) ? digestGenerator.generateDigest(url)
- : null;
-
- timeModified = url != null ? tracker.add(url) : MISSING_RESOURCE_TIME_MODIFIED;
-
- streamable = url == null ? null : new StreamableResourceImpl(url, timeModified);
+ timeModified = url != null ? resourceChangeTracker.trackResource(resource) : MISSING_RESOURCE_TIME_MODIFIED;
}
}
- public ResourceCacheImpl(final ResourceDigestGenerator digestGenerator, ClasspathURLConverter classpathURLConverter)
+ public ResourceCacheImpl(ResourceDigestGenerator digestGenerator, ResourceChangeTracker resourceChangeTracker)
{
this.digestGenerator = digestGenerator;
- tracker = new URLChangeTracker(classpathURLConverter,true);
+ this.resourceChangeTracker = resourceChangeTracker;
}
- public void checkForUpdates()
+ @PostInjection
+ public void listenForInvalidations()
{
- if (tracker.containsChanges())
- {
- cache.clear();
- tracker.clear();
-
- fireInvalidationEvent();
- }
+ resourceChangeTracker.addInvalidationListener(this);
}
private Cached get(Resource resource)
@@ -112,8 +94,14 @@ public class ResourceCacheImpl extends I
return get(resource).requiresDigest;
}
- public StreamableResource getStreamableResource(Resource resource)
+ public void addInvalidationListener(InvalidationListener listener)
{
- return get(resource).streamable;
+ resourceChangeTracker.addInvalidationListener(listener);
}
+
+ public void objectWasInvalidated()
+ {
+ cache.clear();
+ }
+
}
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=1075990&r1=1075989&r2=1075990&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 Tue Mar 1 19:29:08 2011
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008 The Apache Software Foundation
+// Copyright 2006, 2008, 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.
@@ -14,38 +14,27 @@
package org.apache.tapestry5.internal.services;
-import org.apache.tapestry5.ioc.Resource;
-import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
-
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
+
/**
* Responsible for streaming the contents of a resource to the client. The {@link org.apache.tapestry5.ioc.Resource} to
- * stream is almost always a {@link org.apache.tapestry5.ioc.internal.util.ClasspathResource}.
- * <p/>
- * The service's configuration is used to map file extensions to content types. Note: this only works for simple
- * extensions (i.e., "jpg") not for complex extensions (i.e., "tar.gz").
+ * stream is a {@link org.apache.tapestry5.ioc.internal.util.ClasspathResource} or {@link ContextResource}.
*
* @since 5.1.0.0
*/
-@UsesMappedConfiguration(String.class)
public interface ResourceStreamer
{
/**
* Streams the content of the resource to the client (or sends
- * an alternative response such as {@link HttpServletResponse#SC_NOT_MODIFIED}).
- */
- void streamResource(Resource resource) throws IOException;
-
- /**
- * Analyzes the resource to determine what its content type is, possibly using the service's configuration.
+ * an alternative response such as {@link HttpServletResponse#SC_NOT_MODIFIED}). Encapsulates logic for compression
+ * and for caching.
*
- * @param resource
- * to analyze
- * @return content type
- * @throws IOException
+ * @see StreamableResourceSource
*/
- String getContentType(Resource resource) throws IOException;
+ void streamResource(Resource 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=1075990&r1=1075989&r2=1075990&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 Tue Mar 1 19:29:08 2011
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2010 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010, 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.
@@ -14,23 +14,22 @@
package org.apache.tapestry5.internal.services;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletResponse;
+
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.internal.InternalConstants;
-import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.annotations.Symbol;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
-import org.apache.tapestry5.services.Context;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.Response;
import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletResponse;
+import org.apache.tapestry5.services.assets.CompressionStatus;
+import org.apache.tapestry5.services.assets.StreamableResourceFeature;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
public class ResourceStreamerImpl implements ResourceStreamer
{
@@ -42,41 +41,31 @@ public class ResourceStreamerImpl implem
private final Response response;
- private final Context context;
+ private final StreamableResourceSource streamableResourceSource;
private final ResponseCompressionAnalyzer analyzer;
- private final Map<String, String> configuration;
-
- private final int compressionCutoff;
-
private final boolean productionMode;
public ResourceStreamerImpl(Request request,
Response response,
- Context context,
+ StreamableResourceSource streamableResourceSource,
ResourceCache resourceCache,
- Map<String, String> configuration,
-
ResponseCompressionAnalyzer analyzer,
- @Symbol(SymbolConstants.MIN_GZIP_SIZE)
- int compressionCutoff,
-
@Symbol(SymbolConstants.PRODUCTION_MODE)
boolean productionMode)
{
this.request = request;
this.response = response;
- this.context = context;
+ this.streamableResourceSource = streamableResourceSource;
+
this.resourceCache = resourceCache;
- this.configuration = configuration;
this.analyzer = analyzer;
- this.compressionCutoff = compressionCutoff;
this.productionMode = productionMode;
}
@@ -90,6 +79,8 @@ public class ResourceStreamerImpl implem
long ifModifiedSince = 0;
+ long modified = resourceCache.getTimeModified(resource);
+
try
{
ifModifiedSince = request.getDateHeader(IF_MODIFIED_SINCE_HEADER);
@@ -103,8 +94,6 @@ public class ResourceStreamerImpl implem
if (ifModifiedSince > 0)
{
- long modified = resourceCache.getTimeModified(resource);
-
if (ifModifiedSince >= modified)
{
response.sendError(HttpServletResponse.SC_NOT_MODIFIED, "");
@@ -112,83 +101,35 @@ public class ResourceStreamerImpl implem
}
}
+ Set<StreamableResourceFeature> features = analyzer.isGZipSupported() ? StreamableResourceFeature.ALL
+ : StreamableResourceFeature.NO_COMPRESSION;
+
+ org.apache.tapestry5.services.assets.StreamableResource streamable = streamableResourceSource
+ .getStreamableResource(resource, features);
+
// Prevent the upstream code from compressing when we don't want to.
response.disableCompression();
-
- StreamableResource streamble = resourceCache.getStreamableResource(resource);
- long lastModified = streamble.getLastModified();
+ // TODO: This may be broken, as we want the lastModified with only 1 second precision, which is
+ // as much as can be expressed via the HTTP header.
+
+ long lastModified = modified;
response.setDateHeader("Last-Modified", lastModified);
if (productionMode)
response.setDateHeader("Expires", lastModified + InternalConstants.TEN_YEARS);
- String contentType = identifyContentType(resource, streamble);
-
- boolean compress = analyzer.isGZipSupported() && streamble.getSize(false) >= compressionCutoff
- && analyzer.isCompressable(contentType);
+ response.setContentLength(streamable.getSize());
- int contentLength = streamble.getSize(compress);
-
- if (contentLength >= 0)
- response.setContentLength(contentLength);
-
- if (compress)
+ if (streamable.getCompression() == CompressionStatus.COMPRESSED)
response.setHeader(InternalConstants.CONTENT_ENCODING_HEADER, InternalConstants.GZIP_CONTENT_ENCODING);
- InputStream is = null;
+ OutputStream os = response.getOutputStream(streamable.getContentType());
- try
- {
- is = streamble.getStream(compress);
-
- OutputStream os = response.getOutputStream(contentType);
-
- TapestryInternalUtils.copy(is, os);
-
- is.close();
- is = null;
-
- os.close();
- }
- finally
- {
- InternalUtils.close(is);
- }
- }
-
- public String getContentType(Resource resource) throws IOException
- {
- return identifyContentType(resource, resourceCache.getStreamableResource(resource));
- }
-
- private String identifyContentType(Resource resource, StreamableResource streamble) throws IOException
- {
- String contentType = streamble.getContentType();
-
- if ("content/unknown".equals(contentType))
- contentType = null;
-
- if (contentType != null)
- return contentType;
-
- contentType = context.getMimeType(resource.getPath());
-
- if (contentType != null)
- return contentType;
-
- String file = resource.getFile();
- int dotx = file.lastIndexOf('.');
-
- if (dotx > 0)
- {
- String extension = file.substring(dotx + 1);
-
- contentType = configuration.get(extension);
- }
+ streamable.streamTo(os);
- return contentType != null ? contentType : "application/octet-stream";
+ os.close();
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerImpl.java?rev=1075990&r1=1075989&r2=1075990&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerImpl.java Tue Mar 1 19:29:08 2011
@@ -1,10 +1,10 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 2009, 2010 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
+// 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,
@@ -14,42 +14,42 @@
package org.apache.tapestry5.internal.services;
+import java.util.Collection;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.ioc.annotations.Symbol;
-import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
-import org.apache.tapestry5.SymbolConstants;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.Collection;
-import java.util.Map;
+import org.apache.tapestry5.services.assets.CompressionAnalyzer;
public class ResponseCompressionAnalyzerImpl implements ResponseCompressionAnalyzer
{
private final HttpServletRequest request;
- private final Map<String, Boolean> notCompressable = CollectionFactory.newCaseInsensitiveMap();
-
private final boolean gzipCompressionEnabled;
- public ResponseCompressionAnalyzerImpl(HttpServletRequest request, Collection<String> configuration,
- @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
- boolean gzipCompressionEnabled)
+ private final CompressionAnalyzer analyzer;
+
+ public ResponseCompressionAnalyzerImpl(HttpServletRequest request, CompressionAnalyzer analyzer, @Deprecated
+ Collection<String> configuration, @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
+ boolean gzipCompressionEnabled)
{
this.request = request;
+ this.analyzer = analyzer;
this.gzipCompressionEnabled = gzipCompressionEnabled;
-
- for (String contentType : configuration)
- notCompressable.put(contentType, true);
}
public boolean isGZipSupported()
{
- if (!gzipCompressionEnabled) return false;
+ if (!gzipCompressionEnabled)
+ return false;
String supportedEncodings = request.getHeader("Accept-Encoding");
- if (supportedEncodings == null) return false;
+ if (supportedEncodings == null)
+ return false;
for (String encoding : TapestryInternalUtils.splitAtCommas(supportedEncodings))
{
@@ -62,10 +62,6 @@ public class ResponseCompressionAnalyzer
public boolean isCompressable(String contentType)
{
- int x = contentType.indexOf(';');
-
- String key = x < 0 ? contentType : contentType.substring(0, x);
-
- return notCompressable.get(key) == null;
+ return analyzer.isCompressable(contentType);
}
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressionAnalyzerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressionAnalyzerImpl.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressionAnalyzerImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressionAnalyzerImpl.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,42 @@
+// 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.util.Map;
+
+import org.apache.tapestry5.services.assets.CompressionAnalyzer;
+
+public class CompressionAnalyzerImpl implements CompressionAnalyzer
+{
+ private final Map<String, Boolean> configuration;
+
+ public CompressionAnalyzerImpl(Map<String, Boolean> configuration)
+ {
+ this.configuration = configuration;
+ }
+
+ public boolean isCompressable(String contentType)
+ {
+ assert contentType != null;
+
+ int x = contentType.indexOf(';');
+
+ String key = x < 0 ? contentType : contentType.substring(0, x);
+
+ Boolean result = configuration.get(key);
+
+ return result == null ? true : result.booleanValue();
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContentTypeAnalyzerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContentTypeAnalyzerImpl.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContentTypeAnalyzerImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContentTypeAnalyzerImpl.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,53 @@
+// 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.util.Map;
+
+import org.apache.tapestry5.internal.TapestryInternalUtils;
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.services.Context;
+import org.apache.tapestry5.services.assets.ContentTypeAnalyzer;
+
+public class ContentTypeAnalyzerImpl implements ContentTypeAnalyzer
+{
+ private final Context context;
+
+ private final Map<String, String> configuration;
+
+ public ContentTypeAnalyzerImpl(Context context, Map<String, String> configuration)
+ {
+ this.context = context;
+ this.configuration = configuration;
+ }
+
+ public String getContentType(Resource resource)
+ {
+ String extension = TapestryInternalUtils.toFileSuffix(resource.getFile());
+
+ String contentType = configuration.get(extension);
+
+ if (contentType != null)
+ return contentType;
+
+ contentType = context.getMimeType(resource.getFile());
+
+ if (contentType != null)
+ return contentType;
+
+ return "application/octet-stream";
+ }
+
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ResourceChangeTracker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ResourceChangeTracker.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ResourceChangeTracker.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ResourceChangeTracker.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,43 @@
+// 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 org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.internal.util.URLChangeTracker;
+import org.apache.tapestry5.services.InvalidationEventHub;
+import org.apache.tapestry5.services.InvalidationListener;
+import org.apache.tapestry5.services.UpdateListener;
+
+/**
+ * Tracks resources (at least, resources that can change because they are on the file system) and
+ * acts as an {@link UpdateListener} to check for changes and notify its listeners.
+ *
+ * @since 5.3.0
+ */
+public interface ResourceChangeTracker extends InvalidationEventHub
+{
+ /**
+ * Start tracking the resource (or return the last modified time of an already tracked resource). Only file system
+ * resources are tracked. Resources are tracked until <em>any</em> resource changes, at which points
+ * {@linkplain InvalidationListener listeners} are notified and the internal state
+ * is cleared.
+ *
+ * @see URLChangeTracker
+ * @param resource
+ * to track
+ * @return last modified time, to nearest second
+ */
+ long trackResource(Resource resource);
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ResourceChangeTrackerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ResourceChangeTrackerImpl.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ResourceChangeTrackerImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ResourceChangeTrackerImpl.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,58 @@
+// 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 org.apache.tapestry5.internal.event.InvalidationEventHubImpl;
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.annotations.PostInjection;
+import org.apache.tapestry5.ioc.internal.util.URLChangeTracker;
+import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
+import org.apache.tapestry5.services.UpdateListener;
+import org.apache.tapestry5.services.UpdateListenerHub;
+
+public class ResourceChangeTrackerImpl extends InvalidationEventHubImpl implements ResourceChangeTracker,
+ UpdateListener
+{
+ private final URLChangeTracker tracker;
+
+ public ResourceChangeTrackerImpl(ClasspathURLConverter classpathURLConverter)
+ {
+ // Use granularity of seconds (not milliseconds) since that works properly
+ // with response headers for identifying last modified. Don't track
+ // folder changes, just changes to actual files.
+ tracker = new URLChangeTracker(classpathURLConverter, true, false);
+ }
+
+ @PostInjection
+ public void registerWithUpdateListenerHub(UpdateListenerHub hub)
+ {
+ hub.addUpdateListener(this);
+ }
+
+ public long trackResource(Resource resource)
+ {
+ return tracker.add(resource.toURL());
+ }
+
+ public void checkForUpdates()
+ {
+ if (tracker.containsChanges())
+ {
+ fireInvalidationEvent();
+ tracker.clear();
+ }
+ }
+
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCachingInterceptor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCachingInterceptor.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCachingInterceptor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCachingInterceptor.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,91 @@
+// 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 java.util.Map;
+import java.util.Set;
+
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.annotations.PostInjection;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.services.InvalidationListener;
+import org.apache.tapestry5.services.assets.StreamableResource;
+import org.apache.tapestry5.services.assets.StreamableResourceFeature;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
+
+/**
+ * An interceptor for the {@link StreamableResourceSource} service that handles caching of content.
+ */
+public class SRSCachingInterceptor implements StreamableResourceSource, InvalidationListener
+{
+ private final ResourceChangeTracker tracker;
+
+ private final StreamableResourceSource delegate;
+
+ private final Map<Resource, StreamableResource> cache = CollectionFactory.newConcurrentMap();
+
+ public SRSCachingInterceptor(ResourceChangeTracker tracker, StreamableResourceSource delegate)
+ {
+ this.tracker = tracker;
+ this.delegate = delegate;
+ }
+
+ // See Brian's thread safety book for why it's better for this logic to be outside the constructor
+ @PostInjection
+ public void registerAsInvalidationListener()
+ {
+ tracker.addInvalidationListener(this);
+ }
+
+ public StreamableResource getStreamableResource(Resource baseResource, Set<StreamableResourceFeature> features)
+ throws IOException
+ {
+ if (!features.contains(StreamableResourceFeature.CACHING))
+ return delegate.getStreamableResource(baseResource, features);
+
+ StreamableResource result = cache.get(baseResource);
+
+ if (result == null)
+ {
+ result = delegate.getStreamableResource(baseResource, features);
+
+ if (isCacheable(result))
+ {
+ tracker.trackResource(baseResource);
+
+ cache.put(baseResource, result);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Always returns true; a subclass may extend this to only cache the resource in some circumstances.
+ *
+ * @param resource
+ * @return true to cache the resource
+ */
+ protected boolean isCacheable(StreamableResource resource)
+ {
+ return true;
+ }
+
+ public void objectWasInvalidated()
+ {
+ cache.clear();
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressedCachingInterceptor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressedCachingInterceptor.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressedCachingInterceptor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressedCachingInterceptor.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,41 @@
+// 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 org.apache.tapestry5.services.assets.CompressionStatus;
+import org.apache.tapestry5.services.assets.StreamableResource;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
+
+/**
+ * Specialization of {@link SRSCachingInterceptor} that only attempts to cache
+ * compressed resources.
+ */
+public class SRSCompressedCachingInterceptor extends SRSCachingInterceptor
+{
+ public SRSCompressedCachingInterceptor(ResourceChangeTracker tracker, StreamableResourceSource delegate)
+ {
+ super(tracker, delegate);
+ }
+
+ /**
+ * Return true only if the resource is compressed.
+ */
+ @Override
+ protected boolean isCacheable(StreamableResource resource)
+ {
+ return resource.getCompression() == CompressionStatus.COMPRESSED;
+ }
+
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,75 @@
+// 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.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Set;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.services.assets.CompressionStatus;
+import org.apache.tapestry5.services.assets.StreamableResource;
+import org.apache.tapestry5.services.assets.StreamableResourceFeature;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
+
+public class SRSCompressingInterceptor implements StreamableResourceSource
+{
+ private final int compressionCutoff;
+
+ private final StreamableResourceSource delegate;
+
+ public SRSCompressingInterceptor(int compressionCutoff, StreamableResourceSource delegate)
+ {
+ this.compressionCutoff = compressionCutoff;
+ this.delegate = delegate;
+ }
+
+ public StreamableResource getStreamableResource(Resource baseResource, Set<StreamableResourceFeature> features)
+ throws IOException
+ {
+ StreamableResource streamable = delegate.getStreamableResource(baseResource, features);
+
+ if (streamable.getCompression() == CompressionStatus.COMPRESSABLE
+ && features.contains(StreamableResourceFeature.GZIP_COMPRESSION)) { return compress(streamable); }
+
+ return streamable;
+ }
+
+ private StreamableResource compress(StreamableResource uncompressed) throws IOException
+ {
+ int size = uncompressed.getSize();
+
+ // Because of GZIP overhead, streams below a certain point actually get larger when compressed so
+ // we don't even try.
+
+ if (size < compressionCutoff)
+ 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.getContentType(), CompressionStatus.COMPRESSED, cache);
+ }
+}
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=1075990&r1=1075989&r2=1075990&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 Tue Mar 1 19:29:08 2011
@@ -16,7 +16,6 @@ package org.apache.tapestry5.internal.se
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
@@ -29,10 +28,9 @@ 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.TapestryInternalUtils;
import org.apache.tapestry5.internal.services.ResourceCache;
-import org.apache.tapestry5.internal.services.StreamableResource;
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.json.JSONArray;
@@ -42,12 +40,15 @@ 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.StreamableResource;
+import org.apache.tapestry5.services.assets.StreamableResourceFeature;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
import org.apache.tapestry5.services.javascript.JavaScriptStack;
import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
public class StackAssetRequestHandler implements AssetRequestHandler, InvalidationListener
{
- private final ResourceCache resourceCache;
+ private final StreamableResourceSource streamableResourceSource;
private final JavaScriptStackSource javascriptStackSource;
@@ -64,19 +65,26 @@ public class StackAssetRequestHandler im
private final Map<String, BytestreamCache> compressedCache = CollectionFactory.newCaseInsensitiveMap();
- public StackAssetRequestHandler(ResourceCache resourceCache, JavaScriptStackSource javascriptStackSource,
- LocalizationSetter localizationSetter, ResponseCompressionAnalyzer compressionAnalyzer,
+ public StackAssetRequestHandler(StreamableResourceSource streamableResourceSource,
+ JavaScriptStackSource javascriptStackSource, LocalizationSetter localizationSetter,
+ ResponseCompressionAnalyzer compressionAnalyzer,
@Symbol(SymbolConstants.PRODUCTION_MODE)
boolean productionMode)
{
- this.resourceCache = resourceCache;
+ this.streamableResourceSource = streamableResourceSource;
this.javascriptStackSource = javascriptStackSource;
this.localizationSetter = localizationSetter;
this.compressionAnalyzer = compressionAnalyzer;
this.productionMode = productionMode;
}
+ @PostInjection
+ public void listenToInvalidations(ResourceChangeTracker resourceChangeTracker)
+ {
+ resourceChangeTracker.addInvalidationListener(this);
+ }
+
public boolean handleAssetRequest(Request request, Response response, String extraPath) throws IOException
{
boolean compress = compressionAnalyzer.isGZipSupported();
@@ -92,6 +100,8 @@ public class StackAssetRequestHandler im
if (productionMode)
response.setDateHeader("Expires", lastModified + InternalConstants.TEN_YEARS);
+ response.disableCompression();
+
response.setContentLength(cachedStream.size());
if (compress)
@@ -201,11 +211,10 @@ public class StackAssetRequestHandler im
{
Resource resource = library.getResource();
- StreamableResource streamable = resourceCache.getStreamableResource(resource);
-
- InputStream inputStream = streamable.getStream(false);
+ StreamableResource streamable = streamableResourceSource.getStreamableResource(resource,
+ StreamableResourceFeature.NONE);
- TapestryInternalUtils.copy(inputStream, outputStream);
+ streamable.streamTo(outputStream);
}
private BytestreamCache compressStream(BytestreamCache uncompressed) throws IOException
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,63 @@
+// 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 java.io.OutputStream;
+
+import org.apache.tapestry5.services.assets.CompressionStatus;
+import org.apache.tapestry5.services.assets.StreamableResource;
+
+public class StreamableResourceImpl implements StreamableResource
+{
+ private final String contentType;
+
+ private final CompressionStatus compression;
+
+ private final BytestreamCache bytestreamCache;
+
+ public StreamableResourceImpl(String contentType, CompressionStatus compression, BytestreamCache bytestreamCache)
+ {
+ this.contentType = contentType;
+ this.compression = compression;
+ this.bytestreamCache = bytestreamCache;
+ }
+
+ public CompressionStatus getCompression()
+ {
+ return compression;
+ }
+
+ public String getContentType()
+ {
+ return contentType;
+ }
+
+ public int getSize()
+ {
+ return bytestreamCache.size();
+ }
+
+ public void streamTo(OutputStream os) throws IOException
+ {
+ bytestreamCache.writeTo(os);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("StreamableResource<%s %s size: %d>", contentType, compression.name(), getSize());
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceSourceImpl.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceSourceImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceSourceImpl.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,96 @@
+// 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.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tapestry5.internal.TapestryInternalUtils;
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.services.assets.CompressionAnalyzer;
+import org.apache.tapestry5.services.assets.CompressionStatus;
+import org.apache.tapestry5.services.assets.ContentTypeAnalyzer;
+import org.apache.tapestry5.services.assets.ResourceTransformer;
+import org.apache.tapestry5.services.assets.StreamableResource;
+import org.apache.tapestry5.services.assets.StreamableResourceFeature;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
+
+public class StreamableResourceSourceImpl implements StreamableResourceSource
+{
+ private final Map<String, ResourceTransformer> configuration;
+
+ private final ContentTypeAnalyzer contentTypeAnalyzer;
+
+ private final CompressionAnalyzer compressionAnalyzer;
+
+ public StreamableResourceSourceImpl(Map<String, ResourceTransformer> configuration,
+ ContentTypeAnalyzer contentTypeAnalyzer, CompressionAnalyzer compressionAnalyzer)
+ {
+ this.configuration = configuration;
+ this.contentTypeAnalyzer = contentTypeAnalyzer;
+ this.compressionAnalyzer = compressionAnalyzer;
+ }
+
+ public StreamableResource getStreamableResource(Resource baseResource, Set<StreamableResourceFeature> features)
+ throws IOException
+ {
+ assert baseResource != null;
+
+ URL url = baseResource.toURL();
+
+ if (url == null)
+ throw new IOException(String.format("Resource %s does not exist.", baseResource));
+
+ String fileSuffix = TapestryInternalUtils.toFileSuffix(baseResource.getFile());
+
+ // Optionally, transform the resource. The main driver for this is to allow
+ // for libraries like LessJS (http://lesscss.org/) or
+ // http://jashkenas.github.com/coffee-script/
+ ResourceTransformer rt = configuration.get(fileSuffix);
+
+ InputStream buffered = new BufferedInputStream(url.openStream());
+
+ InputStream transformed = rt == null ? buffered : rt.transform(buffered);
+
+ BytestreamCache bytestreamCache = readStream(transformed);
+
+ transformed.close();
+ buffered.close();
+
+ String contentType = contentTypeAnalyzer.getContentType(baseResource);
+
+ boolean compressable = compressionAnalyzer.isCompressable(contentType);
+
+ return new StreamableResourceImpl(contentType, compressable ? CompressionStatus.COMPRESSABLE
+ : CompressionStatus.NOT_COMPRESSABLE, bytestreamCache);
+ }
+
+ private BytestreamCache readStream(InputStream stream) throws IOException
+ {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ TapestryInternalUtils.copy(stream, bos);
+
+ stream.close();
+
+ return new BytestreamCache(bos);
+ }
+
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ResponseCompressionAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ResponseCompressionAnalyzer.java?rev=1075990&r1=1075989&r2=1075990&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ResponseCompressionAnalyzer.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ResponseCompressionAnalyzer.java Tue Mar 1 19:29:08 2011
@@ -1,10 +1,10 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 2009, 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
+// 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,
@@ -15,12 +15,11 @@
package org.apache.tapestry5.services;
import org.apache.tapestry5.ioc.annotations.UsesConfiguration;
+import org.apache.tapestry5.services.assets.CompressionAnalyzer;
/**
* Used to determine if the client supports GZIP compression of the response.
- * <p/>
- * The configuration is an unordered list of content types that should <em>not</em> be compressed.
- *
+ *
* @since 5.1.0.0
*/
@UsesConfiguration(String.class)
@@ -28,7 +27,7 @@ public interface ResponseCompressionAnal
{
/**
* Checks the Accept-Encoding request header for a "gzip" token.
- *
+ *
* @return true if gzip is supported by client
*/
boolean isGZipSupported();
@@ -38,9 +37,12 @@ public interface ResponseCompressionAnal
* through a GZip filter consumes cycles and makes them larger.
* <p/>
* Contribute content type strings to the service's configuration to mark them as not compressable.
- *
- * @param contentType the mime type of the content, such as "text/html" or "image/jpeg".
- * @return true if compression is worthwile
+ *
+ * @param contentType
+ * the mime type of the content, such as "text/html" or "image/jpeg".
+ * @return true if compression is worthwhile
+ * @deprecated Deprecated in Tapestry 5.3. This method is to be removed at a later date. The service's configuration
+ * is no longer used. Instead, contribute to and use {@link CompressionAnalyzer}.
*/
boolean isCompressable(String contentType);
}
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=1075990&r1=1075989&r2=1075990&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 Tue Mar 1 19:29:08 2011
@@ -226,6 +226,7 @@ import org.apache.tapestry5.runtime.Rend
import org.apache.tapestry5.services.ajax.MultiZoneUpdateEventResultProcessor;
import org.apache.tapestry5.services.assets.AssetPathConstructor;
import org.apache.tapestry5.services.assets.AssetRequestHandler;
+import org.apache.tapestry5.services.assets.AssetsModule;
import org.apache.tapestry5.services.javascript.JavaScriptStack;
import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
@@ -255,7 +256,8 @@ import org.slf4j.Logger;
* The root module for Tapestry.
*/
@Marker(Core.class)
-@SubModule(InternalModule.class)
+@SubModule(
+{ InternalModule.class, AssetsModule.class })
public final class TapestryModule
{
private final PipelineBuilder pipelineBuilder;
@@ -288,7 +290,7 @@ public final class TapestryModule
* these service are defined by the module itself, that's ok because
* services are always lazy proxies). This isn't
* about efficiency (it may be slightly more efficient, but not in any
- * noticable way), it's about eliminating the
+ * noticeable way), it's about eliminating the
* need to keep injecting these dependencies into individual service builder
* and contribution methods.
*/
@@ -332,7 +334,7 @@ public final class TapestryModule
// A bunch of classes "promoted" from inline inner class to nested classes,
// just so that the stack trace would be more readable. Most of these
- // are teminators for pipeline services.
+ // are terminators for pipeline services.
/**
* @since 5.1.0.0
@@ -2505,34 +2507,6 @@ public final class TapestryModule
}
/**
- * Adds content types:
- * <dl>
- * <dt>css</dt>
- * <dd>text/css</dd>
- * <dt>js</dt>
- * <dd>text/javascript</dd>
- * <dt>jpg, jpeg</dt>
- * <dd>image/jpeg</dd>
- * <dt>gif</dt>
- * <dd>image/gif</dd>
- * <dt>png</dt>
- * <dd>image/png</dd>
- * <dt>swf</dt>
- * <dd>application/x-shockwave-flash</dd>
- * </dl>
- */
- public void contributeResourceStreamer(MappedConfiguration<String, String> configuration)
- {
- configuration.add("css", "text/css");
- configuration.add("js", "text/javascript");
- configuration.add("gif", "image/gif");
- configuration.add("jpg", "image/jpeg");
- configuration.add("jpeg", "image/jpeg");
- configuration.add("png", "image/png");
- configuration.add("swf", "application/x-shockwave-flash");
- }
-
- /**
* Adds a listener to the {@link org.apache.tapestry5.internal.services.ComponentInstantiatorSource} that clears the
* {@link PropertyAccess} and {@link TypeCoercer} caches on
* a class loader invalidation. In addition, forces the
@@ -2756,26 +2730,6 @@ public final class TapestryModule
}
/**
- * Contributions are content types that do not benefit from compression. Adds
- * the following content types:
- * <ul>
- * <li>image/jpeg</li>
- * <li>image/gif</li>
- * <li>image/png</li>
- * <li>application/x-shockwave-flash</li>
- * </ul>
- *
- * @since 5.1.0.0
- */
- public static void contributeResponseCompressionAnalyzer(Configuration<String> configuration)
- {
- configuration.add("image/jpeg");
- configuration.add("image/gif");
- configuration.add("image/png");
- configuration.add("application/x-shockwave-flash");
- }
-
- /**
* @since 5.1.1.0
*/
@Marker(Primary.class)
Added: 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=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetsModule.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,124 @@
+// 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 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.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.StreamableResourceSourceImpl;
+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.Decorate;
+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.services.Core;
+
+/**
+ * @since 5.3.0
+ */
+@Marker(Core.class)
+public class AssetsModule
+{
+ public static void bind(ServiceBinder binder)
+ {
+ binder.bind(StreamableResourceSource.class, StreamableResourceSourceImpl.class);
+ binder.bind(CompressionAnalyzer.class, CompressionAnalyzerImpl.class);
+ binder.bind(ContentTypeAnalyzer.class, ContentTypeAnalyzerImpl.class);
+ binder.bind(ResourceChangeTracker.class, ResourceChangeTrackerImpl.class);
+ }
+
+ // The use of decorators is to allow third-parties to get their own extensions
+ // into the pipeline.
+
+ @Decorate(id = "GZipCompression", serviceInterface = StreamableResourceSource.class)
+ public StreamableResourceSource enableCompression(StreamableResourceSource delegate,
+ @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
+ boolean gzipEnabled, @Symbol(SymbolConstants.MIN_GZIP_SIZE)
+ int compressionCutoff)
+ {
+ return gzipEnabled ? new SRSCompressingInterceptor(compressionCutoff, delegate) : null;
+ }
+
+ @Decorate(id = "CacheCompressed", serviceInterface = StreamableResourceSource.class)
+ @Order("before:GZIpCompression")
+ public StreamableResourceSource enableCompressedCaching(StreamableResourceSource delegate,
+ @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
+ boolean gzipEnabled, ResourceChangeTracker tracker)
+ {
+ return gzipEnabled ? new SRSCompressedCachingInterceptor(tracker, delegate) : null;
+ }
+
+ @Decorate(id = "Cache", serviceInterface = StreamableResourceSource.class)
+ @Order("after:GZipCompression")
+ public StreamableResourceSource enableUncompressedCaching(StreamableResourceSource delegate,
+ ResourceChangeTracker tracker)
+ {
+ return new SRSCachingInterceptor(tracker, delegate);
+ }
+
+ /**
+ * Adds content types:
+ * <dl>
+ * <dt>css</dt>
+ * <dd>text/css</dd>
+ * <dt>js</dt>
+ * <dd>text/javascript</dd>
+ * <dt>jpg, jpeg</dt>
+ * <dd>image/jpeg</dd>
+ * <dt>gif</dt>
+ * <dd>image/gif</dd>
+ * <dt>png</dt>
+ * <dd>image/png</dd>
+ * <dt>swf</dt>
+ * <dd>application/x-shockwave-flash</dd>
+ * </dl>
+ */
+ @Contribute(ContentTypeAnalyzer.class)
+ public void setupDefaultContentTypeMappings(MappedConfiguration<String, String> configuration)
+ {
+ configuration.add("css", "text/css");
+ configuration.add("js", "text/javascript");
+ configuration.add("gif", "image/gif");
+ configuration.add("jpg", "image/jpeg");
+ configuration.add("jpeg", "image/jpeg");
+ configuration.add("png", "image/png");
+ configuration.add("swf", "application/x-shockwave-flash");
+ }
+
+ /**
+ * Disables compression for the following content types:
+ * <ul>
+ * <li>image/jpeg</li>
+ * <li>image/gif</li>
+ * <li>image/png</li>
+ * <li>application/x-shockwave-flash</li>
+ * </ul>
+ */
+ @Contribute(CompressionAnalyzer.class)
+ public void disableCompressionForImageTypes(MappedConfiguration<String, Boolean> configuration)
+ {
+ configuration.add("image/jpeg", false);
+ configuration.add("image/gif", false);
+ configuration.add("image/png", false);
+ configuration.add("application/x-shockwave-flash", false);
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/CompressionAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/CompressionAnalyzer.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/CompressionAnalyzer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/CompressionAnalyzer.java Tue Mar 1 19:29:08 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 org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
+
+/**
+ * Identifies which content types are compressable. In general, content types are assumed to be compressable. The
+ * configuration of the service identifies exceptions, which are usually image file formats.
+ * <p>
+ * The configuration maps content types to boolean values (true for compressable).
+ *
+ * @since 5.3.0
+ */
+@UsesMappedConfiguration(boolean.class)
+public interface CompressionAnalyzer
+{
+ /**
+ * For a given MIME type, is the content compressable via GZip?
+ *
+ * @param contentType
+ * MIME content type, possibly included attributes such as encoding type
+ * @return true if the content is not "naturally" compressed
+ */
+ boolean isCompressable(String contentType);
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/CompressionStatus.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/CompressionStatus.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/CompressionStatus.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/CompressionStatus.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,42 @@
+// 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 org.apache.tapestry5.services.Response;
+
+/**
+ * Indicates how the content inside a {@link StreamableResource} is (potentially) compressed.
+ *
+ * @since 5.3.0
+ */
+public enum CompressionStatus
+{
+ /**
+ * The content may be compressed but has not yet been compressed. This is true for most text-oriented content types,
+ * but not found most image content types.
+ */
+ COMPRESSABLE,
+
+ /**
+ * The content has been compressed, which must be reflected in the {@link Response}'s content encoding.
+ */
+ COMPRESSED,
+
+ /**
+ * The content is not compressable. This is usually the case for image content types, where the structure
+ * of the content already includes compression.
+ */
+ NOT_COMPRESSABLE;
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ContentTypeAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ContentTypeAnalyzer.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ContentTypeAnalyzer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ContentTypeAnalyzer.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,39 @@
+// 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 org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
+import org.apache.tapestry5.services.Context;
+
+/**
+ * Used to determine the MIME content type for a resource. The service configuration is the first step,
+ * followed by {@link Context#getMimeType(String)}, and then (finally) "application/octet-stream"
+ * as a stop-gap.
+ * <p>
+ * The service configuration maps the file extension (e.g., "png") to its corresponding MIME type (e.g., "image/png");
+ */
+@UsesMappedConfiguration(String.class)
+public interface ContentTypeAnalyzer
+{
+ /**
+ * Analyze the resource to determine its content type.
+ *
+ * @param resource
+ * to analyze
+ * @return a MIME content type
+ */
+ String getContentType(Resource resource);
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ResourceTransformer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ResourceTransformer.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ResourceTransformer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/ResourceTransformer.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,23 @@
+// 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;
+import java.io.InputStream;
+
+public interface ResourceTransformer
+{
+ InputStream transform(InputStream source) throws IOException;
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResource.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResource.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResource.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,46 @@
+// 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;
+import java.io.OutputStream;
+
+import org.apache.tapestry5.ioc.Resource;
+
+/**
+ * An object, derived from a {@link Resource}, that can be streamed (ultimately, to a client web browser).
+ *
+ * @since 5.3.0
+ */
+public interface StreamableResource
+{
+ CompressionStatus getCompression();
+
+ /**
+ * Returns the MIME content type, e.g., "image/jpeg".
+ */
+ String getContentType();
+
+ /**
+ * The size, in bytes, of the underlying bytestream.
+ */
+ int getSize();
+
+ /**
+ * Streams the resource's content to the provided stream. The caller is responsible for flushing or closing
+ * the output stream.
+ */
+ void streamTo(OutputStream os) throws IOException;
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResourceFeature.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResourceFeature.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResourceFeature.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResourceFeature.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,67 @@
+// 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.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.services.javascript.JavaScriptStack;
+
+/**
+ * Defines additional features desired when accessing the content of a {@link Resource} as
+ * a {@link StreamableResource}.
+ *
+ * @since 5.3.0
+ * @see StreamableResourceSource#getStreamableResource(Resource, Set)
+ */
+public enum StreamableResourceFeature
+{
+ /**
+ * The content may be GZIP compressed (if its content type is {@linkplain CompressionAnalyzer compressable}).
+ */
+ GZIP_COMPRESSION,
+
+ /**
+ * The content may be cached. This is generally desired, except when the content is being accessed so that
+ * it can be aggregated with other content (a {@link JavaScriptStack} is the canonical example) and the individual
+ * resources are not accessed except when aggregated. There are two layers of caching: for uncompressed content, and
+ * for compressed content (where the content is compressable).
+ */
+ CACHING,
+
+ /**
+ * Applies to certain content types (specifically, JavaScript and CSS) where the content can be reduced in size
+ * without changing its effective content (i.e., remove unnecessary whitespace, comments, simplify names, etc.).
+ */
+ MINIMIZATION;
+
+ /**
+ * Unmodifiable set of all features.
+ */
+ public static final Set<StreamableResourceFeature> ALL = Collections.unmodifiableSet(EnumSet
+ .allOf(StreamableResourceFeature.class));
+
+ /**
+ * Unmodifiable set of all features, excluding {@link #GZIP_COMPRESSION}.
+ */
+ public static final Set<StreamableResourceFeature> NO_COMPRESSION = Collections.unmodifiableSet(EnumSet.range(
+ CACHING, MINIMIZATION));
+
+ /** Unmodifiable and empty. */
+ public static final Set<StreamableResourceFeature> NONE = Collections.unmodifiableSet(EnumSet
+ .noneOf(StreamableResourceFeature.class));
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResourceSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResourceSource.java?rev=1075990&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResourceSource.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/StreamableResourceSource.java Tue Mar 1 19:29:08 2011
@@ -0,0 +1,49 @@
+// 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;
+import java.util.Set;
+
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.annotations.Primary;
+import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
+
+/**
+ * Converts {@link Resource}s into {@link StreamableResource}s, and may be responsible for
+ * {@linkplain ResourceTransformer transforming} resources based on file extension. Only
+ * the {@link Primary} service has a configuration; the alternate {@link ExtendedProcessing} service
+ * adds caching, compression, and minimization.
+ *
+ * @since 5.3.0
+ */
+@UsesMappedConfiguration(ResourceTransformer.class)
+public interface StreamableResourceSource
+{
+ /**
+ * Converts a Resource (which must be non-null and exist) into a streamable resource, along with
+ * some additional optional behaviors.
+ *
+ * @param baseResource
+ * the resource to convert
+ * @param features
+ * a set of features desired for the resource, normally {@link StreamableResourceFeature#ALL}
+ * @return the contents of the Resource, possibly transformed, in a streamable format.
+ * @throws IOException
+ * if the resource does not exist or a URL for the content is not available
+ */
+ StreamableResource getStreamableResource(Resource baseResource, Set<StreamableResourceFeature> features)
+ throws IOException;
+}