You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2013/04/15 23:12:05 UTC
[05/10] git commit: URLs for compressed assets are now distinct from
uncompressed assets
URLs for compressed assets are now distinct from uncompressed assets
"/asset/" for normal assets, or "/asset.gz/" for compressed assets
Assets now know about the StreamableResource (necessary to get content type and content checksum)
Content checksum for compressed resources is now based on the compressed bytestream, not the uncompressed resource
Modules are now a special kind of asset
Did a better job of centralizing the logic for creating asset URLs
Tests are currently broken
Peformance may have been affected
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/154dac95
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/154dac95
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/154dac95
Branch: refs/heads/master
Commit: 154dac959c7c6bf264005ad32ba393c459b719dd
Parents: aa465fc
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Sun Apr 14 12:12:57 2013 +0100
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Mon Apr 15 22:11:18 2013 +0100
----------------------------------------------------------------------
.../java/org/apache/tapestry5/SymbolConstants.java | 8 +-
.../org/apache/tapestry5/TapestryConstants.java | 19 ++-
.../internal/services/AbstractAssetFactory.java | 90 +++++++++++-
.../internal/services/AssetDispatcher.java | 20 +++-
.../services/ClasspathAssetAliasManagerImpl.java | 42 ++----
.../internal/services/ClasspathAssetFactory.java | 95 ++-----------
.../internal/services/ContextAssetFactory.java | 109 ++-------------
.../internal/services/ResourceStreamer.java | 47 +++---
.../internal/services/ResourceStreamerImpl.java | 60 ++++----
.../services/ResponseCompressionAnalyzerImpl.java | 14 ++-
.../services/assets/AssetPathConstructorImpl.java | 69 +++++-----
.../assets/BaseStreamableResourceImpl.java | 84 -----------
.../internal/services/assets/CSSURLRewriter.java | 6 +-
.../internal/services/assets/ChecksumPath.java | 16 +--
.../assets/ClasspathAssetRequestHandler.java | 8 +-
.../assets/CompressedStreamableResource.java | 16 +--
.../assets/ContextAssetRequestHandler.java | 7 +-
.../assets/JavaScriptStackAssemblerImpl.java | 4 +-
.../services/assets/SRSCompressingInterceptor.java | 7 +-
.../services/assets/StackAssetRequestHandler.java | 41 +++---
.../services/assets/StreamableResourceImpl.java | 69 ++++++++--
.../JavaScriptStackPathConstructorImpl.java | 17 ++-
.../javascript/MOduleAssetRequestHandler.java | 94 +++++++++++++
.../services/javascript/ModuleDispatcher.java | 107 --------------
.../services/javascript/ModuleManagerImpl.java | 38 ++++--
.../tapestry5/internal/util/VirtualResource.java | 2 +-
.../org/apache/tapestry5/services/AssetAlias.java | 32 +++++
.../services/ClasspathAssetAliasManager.java | 12 +-
.../services/ResponseCompressionAnalyzer.java | 15 ++-
.../services/assets/AssetPathConstructor.java | 40 +++---
.../services/assets/AssetRequestHandler.java | 6 +-
.../tapestry5/services/assets/AssetsModule.java | 21 ++--
.../services/assets/StreamableResource.java | 2 +-
.../services/javascript/JavaScriptModule.java | 15 +-
.../ResponseCompressionAnalyzerTest.groovy | 2 +-
.../assets/AssetPathConstructorImplTest.groovy | 4 +-
.../ModuleAssetRequestHandlerTest.groovy | 6 +-
.../javascript/ModuleDispatcherTests.groovy | 6 +-
.../ClasspathAssetAliasManagerImplTest.java | 47 ++----
.../services/ClasspathAssetFactoryTest.java | 78 ----------
.../internal/services/ContextAssetFactoryTest.java | 74 ----------
.../assets/ContextAssetRequestHandlerTest.java | 4 +-
42 files changed, 620 insertions(+), 833 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
index 8f16694..ccea4a0 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
@@ -154,6 +154,8 @@ public class SymbolConstants
* necessary to GZIP requests, then override this to "false".
*
* @see #MIN_GZIP_SIZE
+ * @see org.apache.tapestry5.services.ResponseCompressionAnalyzer
+ * @see org.apache.tapestry5.services.assets.CompressionAnalyzer
* @since 5.1.0.0
*/
public static final String GZIP_COMPRESSION_ENABLED = "tapestry.gzip-compression-enabled";
@@ -330,7 +332,7 @@ public class SymbolConstants
/**
* Prefix to be used for all resource paths, used to recognize which requests are for assets. This value
* is appended to the context path and the (optional {@linkplain #APPLICATION_FOLDER application folder}.
- * Its default is "assets". It may contain slashes, but should not begin or end with one. This is the prefix
+ * Its default is "asset". It may contain slashes, but should not begin or end with one. This is the prefix
* for uncompressed assets.
*/
public static final String ASSET_PATH_PREFIX = "tapestry.asset-path-prefix";
@@ -338,7 +340,8 @@ public class SymbolConstants
/**
* As with {@link #ASSET_PATH_PREFIX} but for compressed versions of assets. At render time, it is determined
* whether each asset is compressable (for example, image file formats are already compressed). A path for
- * either {@link #ASSET_PATH_PREFIX} or this prefix is selected at render time. Defaults to the asset path prefix suffixed with ".gz".
+ * either {@link #ASSET_PATH_PREFIX} or this prefix is selected at render time. Defaults to the asset path prefix
+ * suffixed with ".gz".
*
* @since 5.4
*/
@@ -409,4 +412,5 @@ public class SymbolConstants
* @since 5.4
*/
public static final String SESSION_LOCKING_ENABLED = "tapestry.session-locking-enabled";
+
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
index 38e5994..74adf03 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
@@ -1,4 +1,4 @@
-// Copyright 2010 The Apache Software Foundation
+// Copyright 2010, 2013 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,12 +15,11 @@
package org.apache.tapestry5;
import org.apache.tapestry5.internal.structure.PageResetListener;
-import org.apache.tapestry5.runtime.PageLifecycleListener;
import org.apache.tapestry5.services.ComponentEventLinkEncoder;
/**
* Constants needed by end-user classes.
- *
+ *
* @since 5.2.0
*/
public class TapestryConstants
@@ -40,12 +39,22 @@ public class TapestryConstants
* notification} after the initialization event; the
* LOOPBACK
* prevents this reset notification.
- *
- * @since 5.2.0
+ *
* @see ComponentEventLinkEncoder#createPageRenderLink(org.apache.tapestry5.services.PageRenderRequestParameters)
* @see ComponentEventLinkEncoder#decodePageRenderRequest(org.apache.tapestry5.services.Request)
* @see PageResetListener
+ * @since 5.2.0
*/
public static final String PAGE_LOOPBACK_PARAMETER_NAME = "t:lb";
+ /**
+ * {@link org.apache.tapestry5.services.Request} attribute set to true or false before an
+ * {@link org.apache.tapestry5.services.assets.AssetRequestHandler} is invoked. This information
+ * is used downstream, typically by {@link org.apache.tapestry5.internal.services.ResourceStreamer},
+ * to decide whether to obtain a normal, or compressed, {@link org.apache.tapestry5.services.assets.StreamableResource}.
+ *
+ * @since 5.4
+ */
+ public static final String COMPRESS_CONTENT = "tapestry.compress-content";
+
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AbstractAssetFactory.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AbstractAssetFactory.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AbstractAssetFactory.java
index 7caff2c..e230999 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AbstractAssetFactory.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AbstractAssetFactory.java
@@ -14,18 +14,96 @@
package org.apache.tapestry5.internal.services;
+import org.apache.tapestry5.Asset;
import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker;
-import org.apache.tapestry5.internal.util.RecomputableSupport;
-import org.apache.tapestry5.ioc.annotations.PostInjection;
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.services.AssetFactory;
+import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
+import org.apache.tapestry5.services.assets.AssetPathConstructor;
+import org.apache.tapestry5.services.assets.StreamableResource;
+import org.apache.tapestry5.services.assets.StreamableResourceProcessing;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
+
+import java.io.IOException;
public abstract class AbstractAssetFactory implements AssetFactory
{
- protected final RecomputableSupport recomputable = new RecomputableSupport();
+ private final AssetPathConstructor assetPathConstructor;
+
+ private final ResponseCompressionAnalyzer compressionAnalyzer;
+
+ private final StreamableResourceSource streamableResourceSource;
+
+ private final ResourceChangeTracker resourceChangeTracker;
+
+ private final Resource rootResource;
+
+ public AbstractAssetFactory(ResponseCompressionAnalyzer compressionAnalyzer,
+ ResourceChangeTracker resourceChangeTracker,
+ StreamableResourceSource streamableResourceSource,
+ AssetPathConstructor assetPathConstructor,
+ Resource rootResource)
+ {
+ this.compressionAnalyzer = compressionAnalyzer;
+ this.resourceChangeTracker = resourceChangeTracker;
+ this.streamableResourceSource = streamableResourceSource;
+ this.assetPathConstructor = assetPathConstructor;
+ this.rootResource = rootResource;
+ }
+
+ protected boolean isCompressable(StreamableResource resource)
+ {
+ return compressionAnalyzer.isGZipEnabled(resource.getContentType());
+ }
- @PostInjection
- public void updateVersionNumberOnResourceChange(ResourceChangeTracker tracker)
+ /**
+ * Returns the root {@link ContextResource}.
+ */
+ public Resource getRootResource()
{
- recomputable.initialize(tracker);
+ return rootResource;
+ }
+
+ protected Asset createAsset(final Resource resource, final String folder, final String resourcePath)
+ {
+ assert resource != null;
+ assert InternalUtils.isNonBlank(folder);
+ assert InternalUtils.isNonBlank(resourcePath);
+
+ return new AbstractAsset(false)
+ {
+ @Override
+ public String toClientURL()
+ {
+ // TODO: Some caching to ensure this is fast! Fortunately, the SRS pipeline includes caching already,
+ // but the results of assetPathConstructor could be cached as well, maybe.
+
+ try
+ {
+ // Get the uncompressed version, so that we can identify its content type (and remember, the extension is not enough,
+ // as some types get translated to new content types by the SRS).
+
+ StreamableResource uncompressed = streamableResourceSource.getStreamableResource(resource, StreamableResourceProcessing.COMPRESSION_DISABLED, resourceChangeTracker);
+
+ StreamableResource forRequest = isCompressable(uncompressed)
+ ? streamableResourceSource.getStreamableResource(resource, StreamableResourceProcessing.COMPRESSION_ENABLED, resourceChangeTracker)
+ : uncompressed;
+
+ return assetPathConstructor.constructAssetPath(folder, resourcePath, forRequest);
+ } catch (IOException ex)
+ {
+ throw new RuntimeException(String.format("Unable to construct asset path for %s: %s",
+ resource, InternalUtils.toMessage(ex)), ex);
+ }
+ }
+
+ @Override
+ public Resource getResource()
+ {
+ return resource;
+ }
+ };
+
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetDispatcher.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetDispatcher.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetDispatcher.java
index 2ab1a68..526c9d5 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetDispatcher.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetDispatcher.java
@@ -15,6 +15,7 @@
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.TapestryConstants;
import org.apache.tapestry5.ioc.annotations.Marker;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
@@ -55,6 +56,20 @@ public class AssetDispatcher implements Dispatcher
private final String uncompressedPathPrefix, compressedPathPrefix;
+ private AssetRequestHandler wrap(final boolean compress, final AssetRequestHandler handler)
+ {
+ return new AssetRequestHandler()
+ {
+ @Override
+ public boolean handleAssetRequest(Request request, Response response, String extraPath) throws IOException
+ {
+ request.setAttribute(TapestryConstants.COMPRESS_CONTENT, compress);
+
+ return handler.handleAssetRequest(request, response, extraPath);
+ }
+ };
+ }
+
public AssetDispatcher(Map<String, AssetRequestHandler> configuration,
PathConstructor pathConstructor,
@@ -72,8 +87,8 @@ public class AssetDispatcher implements Dispatcher
{
AssetRequestHandler handler = configuration.get(path);
- addPath(uncompressedPathPrefix, path, handler);
- addPath(compressedPathPrefix, path, handler);
+ addPath(uncompressedPathPrefix, path, wrap(false, handler));
+ addPath(compressedPathPrefix, path, wrap(true, handler));
}
// Sort by descending length
@@ -124,7 +139,6 @@ public class AssetDispatcher implements Dispatcher
for (String extendedPath : assetPaths)
{
-
if (path.startsWith(extendedPath))
{
AssetRequestHandler handler = pathToHandler.get(extendedPath);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClasspathAssetAliasManagerImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClasspathAssetAliasManagerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClasspathAssetAliasManagerImpl.java
index 7a04a87..9c14d6a 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClasspathAssetAliasManagerImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClasspathAssetAliasManagerImpl.java
@@ -16,13 +16,11 @@ package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.util.AvailableValues;
import org.apache.tapestry5.ioc.util.UnknownValueException;
+import org.apache.tapestry5.services.AssetAlias;
import org.apache.tapestry5.services.ClasspathAssetAliasManager;
-import org.apache.tapestry5.services.assets.AssetPathConstructor;
-import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -30,8 +28,6 @@ import java.util.Map;
public class ClasspathAssetAliasManagerImpl implements ClasspathAssetAliasManager
{
- private final AssetPathConstructor assetPathConstructor;
-
/**
* Map from alias to path.
*/
@@ -42,25 +38,18 @@ public class ClasspathAssetAliasManagerImpl implements ClasspathAssetAliasManage
*/
private final Map<String, String> pathPrefixToAlias = CollectionFactory.newMap();
- private final List<String> sortedAliases;
-
private final List<String> sortedPathPrefixes;
/**
- * Configuration is a map of aliases (short names) to complete names. Keys and values should end with a slash, but
- * one will be provided as necessary, so don't both.
+ * Configuration is a map of aliases (short names) to complete names. Keys and values must not start or end with a slash,
+ * but may contain them.
*/
- public ClasspathAssetAliasManagerImpl(AssetPathConstructor assetPathConstructor,
-
- Map<String, String> configuration)
+ public ClasspathAssetAliasManagerImpl(Map<String, String> configuration)
{
- this.assetPathConstructor = assetPathConstructor;
-
for (Map.Entry<String, String> e : configuration.entrySet())
{
String alias = verify("folder name", e.getKey());
-
String path = verify("path", e.getValue());
aliasToPathPrefix.put(alias, path);
@@ -75,9 +64,6 @@ public class ClasspathAssetAliasManagerImpl implements ClasspathAssetAliasManage
}
};
- sortedAliases = CollectionFactory.newList(aliasToPathPrefix.keySet());
- Collections.sort(sortedAliases, sortDescendingByLength);
-
sortedPathPrefixes = CollectionFactory.newList(aliasToPathPrefix.values());
Collections.sort(sortedPathPrefixes, sortDescendingByLength);
}
@@ -93,7 +79,7 @@ public class ClasspathAssetAliasManagerImpl implements ClasspathAssetAliasManage
}
- public String toClientURL(Resource resource)
+ public AssetAlias extractAssetAlias(Resource resource)
{
String resourcePath = resource.getPath();
@@ -101,18 +87,18 @@ public class ClasspathAssetAliasManagerImpl implements ClasspathAssetAliasManage
{
if (resourcePath.startsWith(pathPrefix))
{
+ // Prevent matching path prefix "foo" against "foobar" ... it must match against "foo/".
+ if (resourcePath.charAt(pathPrefix.length()) != '/')
+ {
+ continue;
+ }
+
String virtualFolder = pathPrefixToAlias.get(pathPrefix);
- String virtualPath = resourcePath.substring(pathPrefix.length() + 1);
+ // Don't want that slash seperating the folder from the rest of the path.
+ String path = resourcePath.substring(pathPrefix.length() + 1);
- try
- {
- return assetPathConstructor.constructAssetPath(virtualFolder, virtualPath, resource);
- } catch (IOException ex)
- {
- throw new RuntimeException(String.format("Unable to construct asset path for %s/%s (from %s): %s",
- virtualFolder, virtualPath, resource, InternalUtils.toMessage(ex)), ex);
- }
+ return new AssetAlias(virtualFolder, path);
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClasspathAssetFactory.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClasspathAssetFactory.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClasspathAssetFactory.java
index 0173058..1b34fd6 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClasspathAssetFactory.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClasspathAssetFactory.java
@@ -15,13 +15,16 @@
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.Asset;
-import org.apache.tapestry5.ioc.Invokable;
+import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker;
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.annotations.Marker;
import org.apache.tapestry5.ioc.internal.util.ClasspathResource;
-import org.apache.tapestry5.services.AssetPathConverter;
+import org.apache.tapestry5.services.AssetAlias;
import org.apache.tapestry5.services.ClasspathAssetAliasManager;
import org.apache.tapestry5.services.ClasspathProvider;
+import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
+import org.apache.tapestry5.services.assets.AssetPathConstructor;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
/**
* Generates Assets for files on the classpath.
@@ -33,89 +36,23 @@ public class ClasspathAssetFactory extends AbstractAssetFactory
{
private final ClasspathAssetAliasManager aliasManager;
- private final ClasspathResource rootResource;
-
- private final AssetPathConverter converter;
-
- private final boolean invariant;
-
- public ClasspathAssetFactory(ClasspathAssetAliasManager aliasManager, AssetPathConverter converter)
+ public ClasspathAssetFactory(ResponseCompressionAnalyzer compressionAnalyzer,
+ ResourceChangeTracker resourceChangeTracker,
+ StreamableResourceSource streamableResourceSource,
+ AssetPathConstructor assetPathConstructor,
+ ClasspathAssetAliasManager aliasManager)
{
- this.aliasManager = aliasManager;
- this.converter = converter;
-
- rootResource = new ClasspathResource("");
+ super(compressionAnalyzer, resourceChangeTracker, streamableResourceSource, assetPathConstructor,
+ new ClasspathResource(""));
- invariant = converter.isInvariant();
- }
-
- private String clientURL(Resource resource)
- {
- String assetPath = aliasManager.toClientURL(resource);
-
- return converter.convertAssetPath(assetPath);
+ this.aliasManager = aliasManager;
}
+ @Override
public Asset createAsset(Resource resource)
{
- if (invariant)
- {
- return createInvariantAsset(resource);
- }
-
- return createVariantAsset(resource);
- }
-
- /**
- * A variant asset must pass the resource through clientURL() all the time; very inefficient.
- */
- private Asset createVariantAsset(final Resource resource)
- {
- return new AbstractAsset(false)
- {
- public Resource getResource()
- {
- return resource;
- }
+ AssetAlias alias = aliasManager.extractAssetAlias(resource);
- public String toClientURL()
- {
- return clientURL(resource);
- }
- };
- }
-
- /**
- * An invariant asset is normal, and only needs to compute the clientURL for the resource once (
- * or when the underlying Resource's content has changed).
- */
- private Asset createInvariantAsset(final Resource resource)
- {
- return new AbstractAsset(true)
- {
- private final Invokable<String> clientURL = recomputable.create(new Invokable<String>()
- {
- @Override
- public String invoke()
- {
- return clientURL(resource);
- }
- });
-
- public Resource getResource()
- {
- return resource;
- }
-
- public String toClientURL()
- {
- return clientURL.invoke();
- }
- };
- }
-
- public Resource getRootResource()
- {
- return rootResource;
+ return createAsset(resource, alias.virtualFolder, alias.path);
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextAssetFactory.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextAssetFactory.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextAssetFactory.java
index 32d63f7..4731cdf 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextAssetFactory.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextAssetFactory.java
@@ -15,15 +15,13 @@
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.Asset;
-import org.apache.tapestry5.ioc.Invokable;
+import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker;
import org.apache.tapestry5.ioc.Resource;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.services.AssetFactory;
-import org.apache.tapestry5.services.AssetPathConverter;
import org.apache.tapestry5.services.Context;
+import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
import org.apache.tapestry5.services.assets.AssetPathConstructor;
-
-import java.io.IOException;
+import org.apache.tapestry5.services.assets.StreamableResourceSource;
/**
* Implementation of {@link AssetFactory} for assets that are part of the web application context.
@@ -32,102 +30,19 @@ import java.io.IOException;
*/
public class ContextAssetFactory extends AbstractAssetFactory
{
- private final AssetPathConstructor assetPathConstructor;
-
- private final Resource rootResource;
-
- private final AssetPathConverter converter;
-
- private final boolean invariant;
-
- public ContextAssetFactory(AssetPathConstructor assetPathConstructor, Context context,
-
- AssetPathConverter converter)
- {
- this.assetPathConstructor = assetPathConstructor;
- this.converter = converter;
-
- rootResource = new ContextResource(context, "/");
- invariant = this.converter.isInvariant();
- }
- public Asset createAsset(Resource resource)
+ public ContextAssetFactory(ResponseCompressionAnalyzer compressionAnalyzer,
+ ResourceChangeTracker resourceChangeTracker,
+ StreamableResourceSource streamableResourceSource,
+ AssetPathConstructor assetPathConstructor,
+ Context context)
{
- if (invariant)
- {
- return createInvariantAsset(resource);
- }
-
- return createVariantAsset(resource);
- }
-
- private String defaultPath(Resource resource)
- {
- try
- {
- return assetPathConstructor.constructAssetPath(RequestConstants.CONTEXT_FOLDER, resource.getPath(), resource);
- } catch (IOException ex)
- {
- throw new RuntimeException(String.format("Unable to construct asset path for %s: %s",
- resource, InternalUtils.toMessage(ex)), ex);
- }
- }
-
- private Asset createInvariantAsset(final Resource resource)
- {
- return new AbstractAsset(true)
- {
- private final Invokable<String> clientURL = recomputable.create(new Invokable<String>()
- {
- @Override
- public String invoke()
- {
- return converter.convertAssetPath(defaultPath(resource));
- }
- });
-
- public Resource getResource()
- {
- return resource;
- }
-
- public String toClientURL()
- {
- return clientURL.invoke();
- }
- };
- }
-
- private Asset createVariantAsset(final Resource resource)
- {
- return new AbstractAsset(false)
- {
- private final Invokable<String> defaultPath = recomputable.create(new Invokable<String>()
- {
- @Override
- public String invoke()
- {
- return defaultPath(resource);
- }
- });
-
- public Resource getResource()
- {
- return resource;
- }
-
- public String toClientURL()
- {
- return converter.convertAssetPath(defaultPath.invoke());
- }
- };
+ super(compressionAnalyzer, resourceChangeTracker, streamableResourceSource, assetPathConstructor,
+ new ContextResource(context, "/"));
}
- /**
- * Returns the root {@link org.apache.tapestry5.internal.services.ContextResource}.
- */
- public Resource getRootResource()
+ public Asset createAsset(final Resource resource)
{
- return rootResource;
+ return createAsset(resource, RequestConstants.CONTEXT_FOLDER, resource.getPath());
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java
index cf4eefd..81027bf 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008, 2011 The Apache Software Foundation
+// Copyright 2006, 2008, 2011, 2013 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -20,18 +20,22 @@ import org.apache.tapestry5.services.assets.StreamableResourceSource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+import java.util.EnumSet;
import java.util.Set;
/**
- * Responsible for streaming the contents of a resource to the client. The {@link org.apache.tapestry5.ioc.Resource} to
- * stream is a {@link org.apache.tapestry5.ioc.internal.util.ClasspathResource} or {@link ContextResource}.
+ * Responsible for streaming the contents of a resource to the client. This is sometimes a simple
+ * {@link Resource} (often from the {@link org.apache.tapestry5.internal.services.javascript.ModuleAssetRequestHandler},
+ * or more frequently an asset represented as a {@link StreamableResource} (via {@link AssetDispatcher}, {@link org.apache.tapestry5.services.assets.AssetRequestHandler},
+ * and {@link StreamableResourceSource}). As of 5.4, the ResourceStreamer handles ETag support, as well as
+ * validation of the checksum (provided in the URL).
*
* @since 5.1.0.0
*/
public interface ResourceStreamer
{
/**
- * Used to customize the behavior of {@link #streamResource(org.apache.tapestry5.ioc.Resource, java.util.Set)}.
+ * Used to customize the behavior of {@link #streamResource(org.apache.tapestry5.ioc.Resource, java.lang.String, java.util.Set)}.
*
* @since 5.4
*/
@@ -44,42 +48,37 @@ public interface ResourceStreamer
OMIT_EXPIRATION
}
- /**
- * Streams the content of the resource to the client (or sends
- * an alternative response such as {@link HttpServletResponse#SC_NOT_MODIFIED}). Encapsulates logic for compression
- * and for caching.
- *
- * @see StreamableResourceSource
- */
- void streamResource(Resource resource) throws IOException;
+ static final Set<Options> DEFAULT_OPTIONS = EnumSet.noneOf(Options.class);
/**
* Streams the content of the resource to the client (or sends
* an alternative response such as {@link HttpServletResponse#SC_NOT_MODIFIED}). Encapsulates logic for compression
* and for caching.
*
- * @see StreamableResourceSource
- */
- void streamResource(Resource resource, Set<Options> options) throws IOException;
-
- /**
- * Streams a resource that has been assembled elsewhere, using default options of none.
- *
* @param resource
- * @throws IOException
- * @since 5.3
+ * to stream
+ * @param providedChecksum
+ * checksum from URL (or null to not validate against checksum, which is normal for modules)
+ * @param options
+ * enable or disable certain features
+ * @see StreamableResourceSource
*/
- void streamResource(StreamableResource resource) throws IOException;
+ boolean streamResource(Resource resource, String providedChecksum, Set<Options> options) throws IOException;
/**
* Streams a resource that has been assembled elsewhere.
*
* @param resource
* content to stream
+ * @param providedChecksum
+ * checksum provided (in the URL) to validate against the {@linkplain org.apache.tapestry5.services.assets.StreamableResource#getChecksum()} actual checksum}
+ * for the resource, may be blank to not validate against the checksum
* @param options
- * any special options related to streaming the resource
+ * enable or disable certain features
+ * @return true if the request was handled (even if sending a {@link HttpServletResponse#SC_NOT_MODIFIED} response),
+ * or false if the request was not handled (because the provided checksum did not match the actual checksum).
* @throws IOException
* @since 5.4
*/
- void streamResource(StreamableResource resource, Set<Options> options) throws IOException;
+ boolean streamResource(StreamableResource resource, String providedChecksum, Set<Options> options) throws IOException;
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java
index 6020adc..6b41cc8 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java
@@ -15,6 +15,7 @@
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.TapestryConstants;
import org.apache.tapestry5.internal.InternalConstants;
import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker;
import org.apache.tapestry5.ioc.IOOperation;
@@ -23,7 +24,6 @@ import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.Response;
-import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
import org.apache.tapestry5.services.assets.CompressionStatus;
import org.apache.tapestry5.services.assets.StreamableResource;
import org.apache.tapestry5.services.assets.StreamableResourceProcessing;
@@ -32,37 +32,32 @@ import org.apache.tapestry5.services.assets.StreamableResourceSource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.EnumSet;
import java.util.Set;
public class ResourceStreamerImpl implements ResourceStreamer
{
static final String IF_MODIFIED_SINCE_HEADER = "If-Modified-Since";
+ private static final String QUOTE = "\"";
+
private final Request request;
private final Response response;
private final StreamableResourceSource streamableResourceSource;
- private final ResponseCompressionAnalyzer analyzer;
-
private final boolean productionMode;
private final OperationTracker tracker;
private final ResourceChangeTracker resourceChangeTracker;
- private final Set<Options> defaultOptions = EnumSet.noneOf(Options.class);
-
public ResourceStreamerImpl(Request request,
Response response,
StreamableResourceSource streamableResourceSource,
- ResponseCompressionAnalyzer analyzer,
-
OperationTracker tracker,
@Symbol(SymbolConstants.PRODUCTION_MODE)
@@ -74,52 +69,53 @@ public class ResourceStreamerImpl implements ResourceStreamer
this.response = response;
this.streamableResourceSource = streamableResourceSource;
- this.analyzer = analyzer;
this.tracker = tracker;
this.productionMode = productionMode;
this.resourceChangeTracker = resourceChangeTracker;
}
- public void streamResource(Resource resource) throws IOException
- {
- streamResource(resource, defaultOptions);
- }
-
- public void streamResource(final Resource resource, final Set<Options> options) throws IOException
+ public boolean streamResource(final Resource resource, final String providedChecksum, final Set<Options> options) throws IOException
{
if (!resource.exists())
{
+ // TODO: Or should we just return false here and not send back a specific error with the (eventual) 404?
+
response.sendError(HttpServletResponse.SC_NOT_FOUND, String.format("Unable to locate asset '%s' (the file does not exist).", resource));
- return;
+
+ return true;
}
- tracker.perform(String.format("Streaming %s", resource), new IOOperation<Void>()
+ final boolean compress = (Boolean) request.getAttribute(TapestryConstants.COMPRESS_CONTENT);
+
+
+ return tracker.perform(String.format("Streaming %s%s", resource, compress ? " (compressed)" : ""), new IOOperation<Boolean>()
{
- public Void perform() throws IOException
+ public Boolean perform() throws IOException
{
- StreamableResourceProcessing processing = analyzer.isGZipSupported()
+ StreamableResourceProcessing processing = compress
? StreamableResourceProcessing.COMPRESSION_ENABLED
: StreamableResourceProcessing.COMPRESSION_DISABLED;
StreamableResource streamable = streamableResourceSource.getStreamableResource(resource, processing, resourceChangeTracker);
- streamResource(streamable, options);
-
- return null;
+ return streamResource(streamable, providedChecksum, options);
}
});
}
- public void streamResource(StreamableResource resource) throws IOException
- {
- streamResource(resource, defaultOptions);
- }
-
- public void streamResource(StreamableResource streamable, Set<Options> options) throws IOException
+ public boolean streamResource(StreamableResource streamable, String providedChecksum, Set<Options> options) throws IOException
{
assert streamable != null;
+ assert providedChecksum != null;
assert options != null;
+ String actualChecksum = streamable.getChecksum();
+
+ if (providedChecksum.length() > 0 && !providedChecksum.equals(actualChecksum))
+ {
+ return false;
+ }
+
long lastModified = streamable.getLastModified();
long ifModifiedSince = 0;
@@ -137,11 +133,11 @@ public class ResourceStreamerImpl implements ResourceStreamer
if (ifModifiedSince > 0 && ifModifiedSince >= lastModified)
{
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
- return;
+ return true;
}
// ETag should be surrounded with quotes.
- String token = "\"" + streamable.getChecksum() + "\"";
+ String token = QUOTE + actualChecksum + QUOTE;
// Even when sending a 304, we want the ETag associated with the request.
// In most cases (except JavaScript modules), the checksum is also embedded into the URL.
@@ -155,7 +151,7 @@ public class ResourceStreamerImpl implements ResourceStreamer
if (token.equals(providedToken))
{
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
- return;
+ return true;
}
// Prevent the upstream code from compressing when we don't want to.
@@ -184,6 +180,8 @@ public class ResourceStreamerImpl implements ResourceStreamer
streamable.streamTo(os);
os.close();
+
+ return true;
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerImpl.java
index 7432019..7bea13c 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2009, 2010, 2012 The Apache Software Foundation
+// Copyright 2009-2013 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
+import org.apache.tapestry5.services.assets.CompressionAnalyzer;
import javax.servlet.http.HttpServletRequest;
@@ -27,12 +28,15 @@ public class ResponseCompressionAnalyzerImpl implements ResponseCompressionAnaly
private final boolean gzipCompressionEnabled;
+ private final CompressionAnalyzer compressionAnalyzer;
+
public ResponseCompressionAnalyzerImpl(HttpServletRequest request,
@Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
- boolean gzipCompressionEnabled)
+ boolean gzipCompressionEnabled, CompressionAnalyzer compressionAnalyzer)
{
this.request = request;
this.gzipCompressionEnabled = gzipCompressionEnabled;
+ this.compressionAnalyzer = compressionAnalyzer;
}
public boolean isGZipSupported()
@@ -65,4 +69,10 @@ public class ResponseCompressionAnalyzerImpl implements ResponseCompressionAnaly
return false;
}
+
+ @Override
+ public boolean isGZipEnabled(String contentType)
+ {
+ return isGZipSupported() && compressionAnalyzer.isCompressable(contentType);
+ }
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/AssetPathConstructorImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/AssetPathConstructorImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/AssetPathConstructorImpl.java
index 6ba0d72..09e434f 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/AssetPathConstructorImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/AssetPathConstructorImpl.java
@@ -15,15 +15,14 @@
package org.apache.tapestry5.internal.services.assets;
import org.apache.tapestry5.SymbolConstants;
-import org.apache.tapestry5.internal.services.RequestConstants;
-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.AssetPathConverter;
import org.apache.tapestry5.services.BaseURLSource;
import org.apache.tapestry5.services.PathConstructor;
import org.apache.tapestry5.services.Request;
-import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
import org.apache.tapestry5.services.assets.AssetPathConstructor;
+import org.apache.tapestry5.services.assets.CompressionStatus;
import org.apache.tapestry5.services.assets.StreamableResource;
import java.io.IOException;
@@ -32,11 +31,11 @@ public class AssetPathConstructorImpl implements AssetPathConstructor
{
private final Request request;
- private final String prefix;
+ private final String uncompressedPrefix, compressedPrefix;
private final BaseURLSource baseURLSource;
- private final AssetChecksumGenerator assetChecksumGenerator;
+ private final AssetPathConverter pathConverter;
private final boolean fullyQualified;
@@ -47,44 +46,53 @@ public class AssetPathConstructorImpl implements AssetPathConstructor
boolean fullyQualified,
@Symbol(SymbolConstants.ASSET_PATH_PREFIX)
- String assetPathPrefix,
+ String uncompressedAssetPrefix,
+
+ @Symbol(SymbolConstants.COMPRESSED_ASSET_PATH_PREFIX)
+ String compressedAssetPrefix,
PathConstructor pathConstructor,
- AssetChecksumGenerator assetChecksumGenerator)
+ AssetPathConverter pathConverter)
{
this.request = request;
this.baseURLSource = baseURLSource;
this.fullyQualified = fullyQualified;
- this.assetChecksumGenerator = assetChecksumGenerator;
+ this.pathConverter = pathConverter;
- prefix = pathConstructor.constructClientPath(assetPathPrefix, "");
+ uncompressedPrefix = pathConstructor.constructClientPath(uncompressedAssetPrefix, "");
+ compressedPrefix = pathConstructor.constructClientPath(compressedAssetPrefix, "");
}
- public String constructStackAssetPath(String localeName, String path, StreamableResource resource) throws IOException
+ public String constructAssetPath(String virtualFolder, String path, StreamableResource resource) throws IOException
{
- StringBuilder builder = new StringBuilder();
+ assert InternalUtils.isNonBlank(path);
- if (fullyQualified)
- {
- builder.append(baseURLSource.getBaseURL(request.isSecure()));
- }
+ StringBuilder builder = create(resource.getCompression() == CompressionStatus.COMPRESSED, virtualFolder);
+ builder.append("/");
- builder.append(prefix); // ends with a slash
+ builder.append(resource.getChecksum());
- builder.append(RequestConstants.STACK_FOLDER).append("/");
- builder.append(assetChecksumGenerator.generateChecksum(resource)).append("/");
- builder.append(localeName).append("/");
+ builder.append('/');
builder.append(path);
- return builder.toString();
+ return finish(builder);
+ }
+
+ public String constructAssetPath(String virtualFolder, boolean compressed)
+ {
+ return finish(create(compressed, virtualFolder));
}
- public String constructAssetPath(String virtualFolder, String path, Resource resource) throws IOException
+ private String finish(StringBuilder builder)
+ {
+ return pathConverter.convertAssetPath(builder.toString());
+ }
+
+ private StringBuilder create(boolean compress, String virtualFolder)
{
assert InternalUtils.isNonBlank(virtualFolder);
- assert path != null;
StringBuilder builder = new StringBuilder();
@@ -93,21 +101,8 @@ public class AssetPathConstructorImpl implements AssetPathConstructor
builder.append(baseURLSource.getBaseURL(request.isSecure()));
}
- builder.append(prefix);
- builder.append(virtualFolder);
- builder.append("/");
-
- builder.append(assetChecksumGenerator.generateChecksum(resource));
-
- // TODO: Under what conditions would the path ever be blank? Is this allowed? It may have made sense
- // in 5.3, to allow access to a folder (to allow the client to build relative URLs), but that
- // is no longer true in 5.4 because of the checksum in the URL.
- if (InternalUtils.isNonBlank(path))
- {
- builder.append('/');
- builder.append(path);
- }
+ builder.append(compress ? compressedPrefix : uncompressedPrefix);
- return builder.toString();
+ return builder.append(virtualFolder);
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BaseStreamableResourceImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BaseStreamableResourceImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BaseStreamableResourceImpl.java
deleted file mode 100644
index c30eb33..0000000
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BaseStreamableResourceImpl.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2013 The Apache Software Foundation
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package org.apache.tapestry5.internal.services.assets;
-
-import org.apache.tapestry5.services.assets.CompressionStatus;
-import org.apache.tapestry5.services.assets.StreamableResource;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public abstract class BaseStreamableResourceImpl implements StreamableResource
-{
- protected final String description, contentType;
-
- protected final CompressionStatus compression;
-
- protected final long lastModified;
-
- protected final BytestreamCache bytestreamCache;
-
- public BaseStreamableResourceImpl(long lastModified, String description, BytestreamCache bytestreamCache, String contentType, CompressionStatus compression)
- {
- this.lastModified = lastModified;
- this.description = description;
- this.bytestreamCache = bytestreamCache;
- this.contentType = contentType;
- this.compression = compression;
- }
-
- public String getDescription()
- {
- return description;
- }
-
- public CompressionStatus getCompression()
- {
- return compression;
- }
-
- public String getContentType()
- {
- return contentType;
- }
-
- public int getSize()
- {
- return bytestreamCache.size();
- }
-
- public long getLastModified()
- {
- return lastModified;
- }
-
- public void streamTo(OutputStream os) throws IOException
- {
- bytestreamCache.writeTo(os);
- }
-
- public InputStream openStream() throws IOException
- {
- return bytestreamCache.openStream();
- }
-
- @Override
- public String toString()
- {
- return String.format("StreamableResource<%s %s %s lastModified: %tc size: %d>", contentType, description, compression.name(),
- lastModified, getSize());
- }
-}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CSSURLRewriter.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CSSURLRewriter.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CSSURLRewriter.java
index 2dec879..fc673fa 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CSSURLRewriter.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CSSURLRewriter.java
@@ -34,11 +34,15 @@ import java.util.regex.Pattern;
* the CSS file and the image assets it may refer to (useful for incorporating a hash of the resource's content into
* the exposed URL).
*
+ * <p>
+ * One potential problem with URL rewriting is the way that URLs for referenced resources are generated; we are
+ * somewhat banking on the fact that referenced resources are non-compressable images.
+ *
* @since 5.4
*/
public class CSSURLRewriter extends DelegatingSRS
{
- // Group 1 is the optional single or double quote
+ // Group 1 is the optional single or double quote (note the use of backtracking to match it)
// Group 2 is the text inside the quotes, or inside the parens if no quotes
private final Pattern urlPattern = Pattern.compile("url\\(\\s*(['\"]?)(.+?)\\1\\s*\\)", Pattern.MULTILINE);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ChecksumPath.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ChecksumPath.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ChecksumPath.java
index da6dc77..074fd86 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ChecksumPath.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ChecksumPath.java
@@ -16,7 +16,6 @@ package org.apache.tapestry5.internal.services.assets;
import org.apache.tapestry5.internal.services.ResourceStreamer;
import org.apache.tapestry5.ioc.Resource;
-import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
import java.io.IOException;
@@ -34,12 +33,9 @@ public class ChecksumPath
private final ResourceStreamer streamer;
- private final AssetChecksumGenerator assetChecksumGenerator;
-
- public ChecksumPath(ResourceStreamer streamer, AssetChecksumGenerator assetChecksumGenerator, String baseFolder, String extraPath)
+ public ChecksumPath(ResourceStreamer streamer, String baseFolder, String extraPath)
{
this.streamer = streamer;
- this.assetChecksumGenerator = assetChecksumGenerator;
int slashx = extraPath.indexOf('/');
checksum = extraPath.substring(0, slashx);
@@ -67,15 +63,7 @@ public class ChecksumPath
return false;
}
- String actualChecksum = assetChecksumGenerator.generateChecksum(resource);
-
- if (!actualChecksum.equals(checksum))
- {
- return false;
- }
-
- streamer.streamResource(resource);
- return true;
+ return streamer.streamResource(resource, checksum, ResourceStreamer.DEFAULT_OPTIONS);
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ClasspathAssetRequestHandler.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ClasspathAssetRequestHandler.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ClasspathAssetRequestHandler.java
index 0a7e39a..b2bdeff 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ClasspathAssetRequestHandler.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ClasspathAssetRequestHandler.java
@@ -20,7 +20,6 @@ import org.apache.tapestry5.services.AssetSource;
import org.apache.tapestry5.services.ClasspathAssetAliasManager;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.Response;
-import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
import org.apache.tapestry5.services.assets.AssetRequestHandler;
import java.io.IOException;
@@ -35,24 +34,21 @@ public class ClasspathAssetRequestHandler implements AssetRequestHandler
{
private final ResourceStreamer streamer;
- private final AssetChecksumGenerator checksumGenerator;
-
private final AssetSource assetSource;
private final String baseFolder;
public ClasspathAssetRequestHandler(ResourceStreamer streamer,
- AssetChecksumGenerator checksumGenerator, AssetSource assetSource, String baseFolder)
+ AssetSource assetSource, String baseFolder)
{
this.streamer = streamer;
- this.checksumGenerator = checksumGenerator;
this.assetSource = assetSource;
this.baseFolder = baseFolder;
}
public boolean handleAssetRequest(Request request, Response response, String extraPath) throws IOException
{
- ChecksumPath path = new ChecksumPath(streamer, checksumGenerator, baseFolder, extraPath);
+ ChecksumPath path = new ChecksumPath(streamer, baseFolder, extraPath);
Resource resource = assetSource.resourceForPath(path.resourcePath);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressedStreamableResource.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressedStreamableResource.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressedStreamableResource.java
index 5f447b4..5dc7ea2 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressedStreamableResource.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/CompressedStreamableResource.java
@@ -14,6 +14,7 @@
package org.apache.tapestry5.internal.services.assets;
+import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
import org.apache.tapestry5.services.assets.CompressionStatus;
import org.apache.tapestry5.services.assets.StreamableResource;
@@ -28,22 +29,13 @@ import java.util.zip.GZIPOutputStream;
*
* @since 5.4
*/
-public class CompressedStreamableResource extends BaseStreamableResourceImpl
+public class CompressedStreamableResource extends StreamableResourceImpl
{
- private final StreamableResource base;
-
- public String getChecksum() throws IOException
+ public CompressedStreamableResource(StreamableResource base, AssetChecksumGenerator assetChecksumGenerator) throws IOException
{
- return base.getChecksum();
- }
-
- public CompressedStreamableResource(StreamableResource base) throws IOException
- {
- super(base.getLastModified(), base.getDescription(), compressContent(base), base.getContentType(), CompressionStatus.COMPRESSED);
+ super(base.getDescription(), base.getContentType(), CompressionStatus.COMPRESSED, base.getLastModified(), compressContent(base), assetChecksumGenerator);
assert base.getCompression() == CompressionStatus.COMPRESSABLE;
-
- this.base = base;
}
private static BytestreamCache compressContent(StreamableResource resource) throws IOException
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContextAssetRequestHandler.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContextAssetRequestHandler.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContextAssetRequestHandler.java
index 72a634c..75a4b6c 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContextAssetRequestHandler.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContextAssetRequestHandler.java
@@ -18,7 +18,6 @@ import org.apache.tapestry5.internal.services.ResourceStreamer;
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.Response;
-import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
import org.apache.tapestry5.services.assets.AssetRequestHandler;
import java.io.IOException;
@@ -34,21 +33,19 @@ public class ContextAssetRequestHandler implements AssetRequestHandler
{
private final ResourceStreamer resourceStreamer;
- private final AssetChecksumGenerator checksumGenerator;
private final Resource rootContextResource;
private final Pattern illegal = Pattern.compile("^(((web|meta)-inf.*)|(.*\\.tml$))", Pattern.CASE_INSENSITIVE);
- public ContextAssetRequestHandler(ResourceStreamer resourceStreamer, AssetChecksumGenerator checksumGenerator, Resource rootContextResource)
+ public ContextAssetRequestHandler(ResourceStreamer resourceStreamer, Resource rootContextResource)
{
this.resourceStreamer = resourceStreamer;
- this.checksumGenerator = checksumGenerator;
this.rootContextResource = rootContextResource;
}
public boolean handleAssetRequest(Request request, Response response, String extraPath) throws IOException
{
- ChecksumPath path = new ChecksumPath(resourceStreamer, checksumGenerator, null, extraPath);
+ ChecksumPath path = new ChecksumPath(resourceStreamer, null, extraPath);
if (illegal.matcher(path.resourcePath).matches())
{
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/JavaScriptStackAssemblerImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/JavaScriptStackAssemblerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/JavaScriptStackAssemblerImpl.java
index 92d849a..ece7533 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/JavaScriptStackAssemblerImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/JavaScriptStackAssemblerImpl.java
@@ -94,7 +94,7 @@ public class JavaScriptStackAssemblerImpl implements JavaScriptStackAssembler
{
StreamableResource uncompressed = assembleJavascriptResourceForStack(locale, stackName, false);
- return new CompressedStreamableResource(uncompressed);
+ return new CompressedStreamableResource(uncompressed, checksumGenerator);
}
JavaScriptStack stack = stackSource.getStack(stackName);
@@ -117,7 +117,7 @@ public class JavaScriptStackAssemblerImpl implements JavaScriptStackAssembler
for (Asset library : libraries)
{
- String path = library.toClientURL();
+ String path = library.getResource().toString();
paths.put(path);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java
index 3781931..f772930 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/SRSCompressingInterceptor.java
@@ -23,10 +23,13 @@ public class SRSCompressingInterceptor extends DelegatingSRS
{
private final int compressionCutoff;
- public SRSCompressingInterceptor(StreamableResourceSource delegate, int compressionCutoff)
+ private final AssetChecksumGenerator checksumGenerator;
+
+ public SRSCompressingInterceptor(StreamableResourceSource delegate, int compressionCutoff, AssetChecksumGenerator checksumGenerator)
{
super(delegate);
this.compressionCutoff = compressionCutoff;
+ this.checksumGenerator = checksumGenerator;
}
public StreamableResource getStreamableResource(Resource baseResource, StreamableResourceProcessing processing, ResourceDependencies dependencies)
@@ -54,6 +57,6 @@ public class SRSCompressingInterceptor extends DelegatingSRS
return uncompressed;
}
- return new CompressedStreamableResource(uncompressed);
+ return new CompressedStreamableResource(uncompressed, checksumGenerator);
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
index 7edff7f..614cf73 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
@@ -14,13 +14,13 @@
package org.apache.tapestry5.internal.services.assets;
+import org.apache.tapestry5.TapestryConstants;
import org.apache.tapestry5.internal.services.ResourceStreamer;
import org.apache.tapestry5.ioc.IOOperation;
import org.apache.tapestry5.ioc.OperationTracker;
import org.apache.tapestry5.services.LocalizationSetter;
import org.apache.tapestry5.services.Request;
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.slf4j.Logger;
@@ -35,8 +35,6 @@ public class StackAssetRequestHandler implements AssetRequestHandler
private final LocalizationSetter localizationSetter;
- private final ResponseCompressionAnalyzer compressionAnalyzer;
-
private final ResourceStreamer resourceStreamer;
// Group 1: checksum
@@ -48,18 +46,19 @@ public class StackAssetRequestHandler implements AssetRequestHandler
private final JavaScriptStackAssembler javaScriptStackAssembler;
+ private final Request request;
+
public StackAssetRequestHandler(Logger logger, LocalizationSetter localizationSetter,
- ResponseCompressionAnalyzer compressionAnalyzer,
ResourceStreamer resourceStreamer,
OperationTracker tracker,
- JavaScriptStackAssembler javaScriptStackAssembler)
+ JavaScriptStackAssembler javaScriptStackAssembler, Request request)
{
this.logger = logger;
this.localizationSetter = localizationSetter;
- this.compressionAnalyzer = compressionAnalyzer;
this.resourceStreamer = resourceStreamer;
this.tracker = tracker;
this.javaScriptStackAssembler = javaScriptStackAssembler;
+ this.request = request;
}
public boolean handleAssetRequest(Request request, Response response, final String extraPath) throws IOException
@@ -69,23 +68,12 @@ public class StackAssetRequestHandler implements AssetRequestHandler
{
public Boolean perform() throws IOException
{
- boolean compress = compressionAnalyzer.isGZipSupported();
-
- StreamableResource resource = getResource(extraPath, compress);
-
- if (resource == null)
- {
- return false;
- }
-
- resourceStreamer.streamResource(resource);
-
- return true;
+ return streamStackResource(extraPath);
}
});
}
- private StreamableResource getResource(String extraPath, final boolean compressed) throws IOException
+ private boolean streamStackResource(String extraPath) throws IOException
{
Matcher matcher = pathPattern.matcher(extraPath);
@@ -93,7 +81,7 @@ public class StackAssetRequestHandler implements AssetRequestHandler
{
logger.warn(String.format("Unable to parse '%s' as an asset stack path", extraPath));
- return null;
+ return false;
}
String checksum = matcher.group(1);
@@ -112,13 +100,20 @@ public class StackAssetRequestHandler implements AssetRequestHandler
{
public StreamableResource perform() throws IOException
{
+ boolean compressed =
+ (Boolean) request.getAttribute(TapestryConstants.COMPRESS_CONTENT);
+
return javaScriptStackAssembler.assembleJavaScriptResourceForStack(stackName, compressed);
}
});
- return checksum.equals(resource.getChecksum())
- ? resource
- : null;
+
+ if (resource == null)
+ {
+ return false;
+ }
+
+ return resourceStreamer.streamResource(resource, checksum, ResourceStreamer.DEFAULT_OPTIONS);
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java
index fbf8156..6e4e7e7 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StreamableResourceImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2011 The Apache Software Foundation
+// Copyright 2013 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,23 +22,74 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-public class StreamableResourceImpl extends BaseStreamableResourceImpl
+public class StreamableResourceImpl implements StreamableResource
{
- private final AssetChecksumGenerator assetChecksumGenerator;
+ protected final String description, contentType;
- public StreamableResourceImpl(String description, String contentType, CompressionStatus compression, long lastModified,
- BytestreamCache bytestreamCache, AssetChecksumGenerator assetChecksumGenerator)
- {
- super(lastModified, description, bytestreamCache, contentType, compression);
+ protected final CompressionStatus compression;
+
+ protected final long lastModified;
+
+ protected final BytestreamCache bytestreamCache;
+
+ protected final AssetChecksumGenerator assetChecksumGenerator;
+ public StreamableResourceImpl(String description, String contentType, CompressionStatus compression, long lastModified, BytestreamCache bytestreamCache, AssetChecksumGenerator assetChecksumGenerator)
+ {
+ this.lastModified = lastModified;
+ this.description = description;
+ this.bytestreamCache = bytestreamCache;
+ this.contentType = contentType;
+ this.compression = compression;
this.assetChecksumGenerator = assetChecksumGenerator;
}
+ public String getDescription()
+ {
+ return description;
+ }
+
+ public CompressionStatus getCompression()
+ {
+ return compression;
+ }
+
+ public String getContentType()
+ {
+ return contentType;
+ }
+
+ public int getSize()
+ {
+ return bytestreamCache.size();
+ }
+
+ public long getLastModified()
+ {
+ return lastModified;
+ }
+
+ public void streamTo(OutputStream os) throws IOException
+ {
+ bytestreamCache.writeTo(os);
+ }
+
+ public InputStream openStream() throws IOException
+ {
+ return bytestreamCache.openStream();
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("StreamableResource<%s %s %s lastModified: %tc size: %d>", contentType, description, compression.name(),
+ lastModified, getSize());
+ }
+
public String getChecksum() throws IOException
{
// Currently, we rely on AssetChecksumGenerator to manage a cache, but that may be better done
- // here (but must be threadsafe).
+ // here (but must be threads-afe).
return assetChecksumGenerator.generateChecksum(this);
}
-
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/JavaScriptStackPathConstructorImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/JavaScriptStackPathConstructorImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/JavaScriptStackPathConstructorImpl.java
index 3ff8c06..1f2ab02 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/JavaScriptStackPathConstructorImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/JavaScriptStackPathConstructorImpl.java
@@ -18,11 +18,13 @@ import org.apache.tapestry5.Asset;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.func.F;
import org.apache.tapestry5.func.Mapper;
+import org.apache.tapestry5.internal.services.RequestConstants;
import org.apache.tapestry5.internal.services.assets.JavaScriptStackAssembler;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.services.ThreadLocale;
+import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
import org.apache.tapestry5.services.assets.AssetPathConstructor;
import org.apache.tapestry5.services.assets.StreamableResource;
import org.apache.tapestry5.services.javascript.JavaScriptStack;
@@ -41,6 +43,8 @@ public class JavaScriptStackPathConstructorImpl implements JavaScriptStackPathCo
private final JavaScriptStackAssembler assembler;
+ private final ResponseCompressionAnalyzer compressionAnalyzer;
+
private final boolean combineScripts;
private final Mapper<Asset, String> toPath = new Mapper<Asset, String>()
@@ -54,13 +58,14 @@ public class JavaScriptStackPathConstructorImpl implements JavaScriptStackPathCo
public JavaScriptStackPathConstructorImpl(ThreadLocale threadLocale, AssetPathConstructor assetPathConstructor,
JavaScriptStackSource javascriptStackSource,
JavaScriptStackAssembler assembler,
- @Symbol(SymbolConstants.COMBINE_SCRIPTS)
- boolean combineScripts)
+ ResponseCompressionAnalyzer compressionAnalyzer, @Symbol(SymbolConstants.COMBINE_SCRIPTS)
+ boolean combineScripts)
{
this.threadLocale = threadLocale;
this.assetPathConstructor = assetPathConstructor;
this.javascriptStackSource = javascriptStackSource;
this.assembler = assembler;
+ this.compressionAnalyzer = compressionAnalyzer;
this.combineScripts = combineScripts;
}
@@ -89,11 +94,13 @@ public class JavaScriptStackPathConstructorImpl implements JavaScriptStackPathCo
{
try
{
- StreamableResource streamable = assembler.assembleJavaScriptResourceForStack(stackName, false);
+ StreamableResource assembled = assembler.assembleJavaScriptResourceForStack(stackName, compressionAnalyzer.isGZipSupported());
- String path = stackName + ".js";
+ String path = String.format("%s/%s.js",
+ threadLocale.getLocale(),
+ stackName);
- String stackURL = assetPathConstructor.constructStackAssetPath(threadLocale.getLocale().toString(), path, streamable);
+ String stackURL = assetPathConstructor.constructAssetPath(RequestConstants.STACK_FOLDER, path, assembled);
return CollectionFactory.newList(stackURL);
} catch (IOException ex)
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/MOduleAssetRequestHandler.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/MOduleAssetRequestHandler.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/MOduleAssetRequestHandler.java
new file mode 100644
index 0000000..d809556
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/MOduleAssetRequestHandler.java
@@ -0,0 +1,94 @@
+// Copyright 2012, 2013 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.services.javascript;
+
+import org.apache.tapestry5.internal.services.AssetDispatcher;
+import org.apache.tapestry5.internal.services.ResourceStreamer;
+import org.apache.tapestry5.ioc.IOOperation;
+import org.apache.tapestry5.ioc.OperationTracker;
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.services.Request;
+import org.apache.tapestry5.services.Response;
+import org.apache.tapestry5.services.assets.AssetRequestHandler;
+import org.apache.tapestry5.services.javascript.ModuleManager;
+
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * Handler contributed to {@link AssetDispatcher} with key "modules". It interprets the extra path as a module name,
+ * and searches for the corresponding JavaScript module. Unlike normal assets, modules do not include any kind of checksum
+ * in the URL, and do not set a far-future expires header.
+ *
+ * @see ModuleManager
+ */
+public class ModuleAssetRequestHandler implements AssetRequestHandler
+{
+ private final ModuleManager moduleManager;
+
+ private final ResourceStreamer streamer;
+
+ private final OperationTracker tracker;
+
+ private final Set<ResourceStreamer.Options> omitExpiration = EnumSet.of(ResourceStreamer.Options.OMIT_EXPIRATION);
+
+ public ModuleAssetRequestHandler(ModuleManager moduleManager,
+ ResourceStreamer streamer,
+ OperationTracker tracker)
+ {
+ this.moduleManager = moduleManager;
+ this.streamer = streamer;
+ this.tracker = tracker;
+ }
+
+ @Override
+ public boolean handleAssetRequest(Request request, Response response, String extraPath) throws IOException
+ {
+ // Ensure request ends with '.js'. That's the extension tacked on by RequireJS because it expects there
+ // to be a hierarchy of static JavaScript files here. In reality, we may be cross-compiling CoffeeScript to
+ // JavaScript, or generating modules on-the-fly, or exposing arbitrary Resources from somewhere on the classpath
+ // as a module.
+
+ int dotx = extraPath.lastIndexOf('.');
+
+ if (dotx < 0)
+ {
+ return false;
+ }
+
+ if (!extraPath.substring(dotx + 1).equals("js"))
+ {
+ return false;
+ }
+
+ final String moduleName = extraPath.substring(0, dotx);
+
+ return tracker.perform(String.format("Streaming module %s", extraPath), new IOOperation<Boolean>()
+ {
+ public Boolean perform() throws IOException
+ {
+ Resource resource = moduleManager.findResourceForModule(moduleName);
+
+ if (resource != null)
+ {
+ return streamer.streamResource(resource, "", omitExpiration);
+ }
+
+ return false;
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/154dac95/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/ModuleDispatcher.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/ModuleDispatcher.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/ModuleDispatcher.java
deleted file mode 100644
index 85e2279..0000000
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/ModuleDispatcher.java
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2012, 2013 The Apache Software Foundation
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package org.apache.tapestry5.internal.services.javascript;
-
-import org.apache.tapestry5.internal.services.AssetDispatcher;
-import org.apache.tapestry5.internal.services.ResourceStreamer;
-import org.apache.tapestry5.ioc.IOOperation;
-import org.apache.tapestry5.ioc.OperationTracker;
-import org.apache.tapestry5.ioc.Resource;
-import org.apache.tapestry5.services.Dispatcher;
-import org.apache.tapestry5.services.PathConstructor;
-import org.apache.tapestry5.services.Request;
-import org.apache.tapestry5.services.Response;
-import org.apache.tapestry5.services.javascript.ModuleManager;
-
-import java.io.IOException;
-import java.util.EnumSet;
-import java.util.Set;
-
-/**
- * Handler contributed to {@link AssetDispatcher} with key "modules". It interprets the extra path as a module name,
- * and searches for the corresponding JavaScript module.
- */
-public class ModuleDispatcher implements Dispatcher
-{
- private final ModuleManager moduleManager;
-
- private final ResourceStreamer streamer;
-
- private final OperationTracker tracker;
-
- private final String prefix;
-
- private final Set<ResourceStreamer.Options> omitExpiration = EnumSet.of(ResourceStreamer.Options.OMIT_EXPIRATION);
-
- public ModuleDispatcher(ModuleManager moduleManager,
- ResourceStreamer streamer,
- PathConstructor pathConstructor,
- OperationTracker tracker)
- {
- this.moduleManager = moduleManager;
- this.streamer = streamer;
- this.tracker = tracker;
-
- prefix = pathConstructor.constructDispatchPath("modules", "");
- }
-
- public boolean dispatch(Request request, Response response) throws IOException
- {
- String requestPath = request.getPath();
-
- if (!requestPath.startsWith(prefix))
- {
- return false;
- }
-
- String extraPath = requestPath.substring(prefix.length());
-
- // Ensure request ends with '.js'. That's the extension tacked on by RequireJS because it expects there
- // to be a hierarchy of static JavaScript files here. In reality, we may be cross-compiling CoffeeScript to
- // JavaScript, or generating modules on-the-fly, or exposing arbitrary Resources from somewhere on the classpath
- // as a module.
-
- int dotx = extraPath.lastIndexOf('.');
-
- if (dotx < 0)
- {
- return false;
- }
-
- if (!extraPath.substring(dotx + 1).equals("js"))
- {
- return false;
- }
-
- final String moduleName = extraPath.substring(0, dotx);
-
- return tracker.perform(String.format("Streaming module %s", extraPath), new IOOperation<Boolean>()
- {
- public Boolean perform() throws IOException
- {
- Resource resource = moduleManager.findResourceForModule(moduleName);
-
- if (resource != null)
- {
- streamer.streamResource(resource, omitExpiration);
-
- return true;
- }
-
- return false;
- }
- });
- }
-}