From 3c48b421065607689068676eab005269a4d06ed6 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 26 Sep 2013 16:51:51 -0400 Subject: [PATCH] Refactor ResourceResolverChain + resolver class names This change splits out resource transformation out from the ResourceResolverChain so that chain is focused entirely on resource resolution (as its name suggests). The invocation of transformers is left as a separate step, it uses a different (recursive) algorithm in any case and iterates over a different set of objects. Also ResourceResolverChain is now limited strictly to methods that a ResourceResolver should be able to use to delegate to remaining resolvers. Furthermore, ResourceResolverChain now maintains an internal index of the "current" resolver so that resolvers don't have to pass the chain when invoking it much like a (Servlet API) FilterChain works. If the last resolver calls the chain again, a null value is returned. --- .../resource/AbstractResourceResolver.java | 50 ------------ .../DefaultResourceResolverChain.java | 80 ++++++++++++++----- .../resource/FingerprintResourceResolver.java | 21 ++--- .../resource/GzipResourceResolver.java | 12 ++- .../resource/LessResourceTransformer.java | 2 +- ...ava => PathExtensionResourceResolver.java} | 24 +++--- .../resource/PathResourceResolver.java | 23 +++--- .../resource/ResourceHttpRequestHandler.java | 24 ++++-- .../servlet/resource/ResourceResolver.java | 33 +++++++- .../resource/ResourceResolverChain.java | 33 ++++++-- .../servlet/resource/ResourceTransformer.java | 22 ++++- .../resource/ResourceUrlEncodingFilter.java | 8 +- .../servlet/resource/ResourceUrlMapper.java | 5 +- ...ExtensionMappingResourceResolverTests.java | 8 +- .../FingerprintResourceResolverTests.java | 14 ++-- .../resource/GzipResourceResolverTests.java | 6 +- .../resource/ResourceUrlMapperTests.java | 2 +- spring-webmvc/src/test/resources/log4j.xml | 4 + 18 files changed, 221 insertions(+), 150 deletions(-) delete mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AbstractResourceResolver.java rename spring-webmvc/src/main/java/org/springframework/web/servlet/resource/{ExtensionMappingResourceResolver.java => PathExtensionResourceResolver.java} (76%) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AbstractResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AbstractResourceResolver.java deleted file mode 100644 index 627d5e88e5..0000000000 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AbstractResourceResolver.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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.springframework.web.servlet.resource; - -import java.util.List; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.core.io.Resource; - - -/** - * - * @author Jeremy Grelle - * @since 4.0 - */ -public abstract class AbstractResourceResolver implements ResourceResolver { - - - @Override - public final Resource resolve(HttpServletRequest request, String requestPath, - List locations, ResourceResolverChain chain) { - - Resource resource = chain.next(this).resolve(request, requestPath, locations, chain); - return resolveInternal(request, requestPath, locations, chain, resource); - } - - protected abstract Resource resolveInternal(HttpServletRequest request, String path, - List locations, ResourceResolverChain chain, Resource resolved); - - @Override - public String resolveUrl(String resourcePath, List locations, ResourceResolverChain chain) { - return chain.next(this).resolveUrl(resourcePath, locations, chain); - } - -} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/DefaultResourceResolverChain.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/DefaultResourceResolverChain.java index dcc53bf9d2..44e8fc343d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/DefaultResourceResolverChain.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/DefaultResourceResolverChain.java @@ -16,13 +16,15 @@ package org.springframework.web.servlet.resource; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.core.io.Resource; +import org.springframework.util.Assert; /** @@ -34,42 +36,78 @@ import org.springframework.core.io.Resource; */ class DefaultResourceResolverChain implements ResourceResolverChain { - private final List resolvers; + private static Log logger = LogFactory.getLog(DefaultResourceResolverChain.class); - private List transformers = new ArrayList(); + private final List resolvers = new ArrayList(); + private int index = -1; - public DefaultResourceResolverChain(List resolvers, List transformers) { - this.resolvers = (resolvers != null) ? resolvers : new ArrayList(); - this.transformers = (transformers != null) ? transformers : new ArrayList(); + + public DefaultResourceResolverChain(List resolvers) { + this.resolvers.addAll((resolvers != null) ? resolvers : new ArrayList()); } @Override - public ResourceResolver next(ResourceResolver current) { - return this.resolvers.get(this.resolvers.indexOf(current) + 1); + public Resource resolveResource(HttpServletRequest request, String requestPath, List locations) { + ResourceResolver resolver = getNextResolver(); + if (resolver == null) { + return null; + } + try { + logBefore(resolver); + Resource resource = resolver.resolveResource(request, requestPath, locations, this); + logAfter(resolver, resource); + return resource; + } + finally { + this.index--; + } } @Override - public Resource resolveAndTransform(HttpServletRequest request, String path, List locations) - throws IOException { + public String resolveUrlPath(String resourcePath, List locations) { + ResourceResolver resolver = getNextResolver(); + if (resolver == null) { + return null; + } + try { + logBefore(resolver); + String urlPath = resolver.resolveUrlPath(resourcePath, locations, this); + logAfter(resolver, urlPath); + return urlPath; + } + finally { + this.index--; + } + } - Resource resource = this.resolvers.get(0).resolve(request, path, locations, this); - return resource != null ? applyTransformers(request, resource) : resource; + private ResourceResolver getNextResolver() { + + Assert.state(this.index <= this.resolvers.size(), + "Current index exceeds the number of configured ResourceResolver's"); + + if (this.index == (this.resolvers.size() - 1)) { + if (logger.isTraceEnabled()) { + logger.trace("No more ResourceResolver's to delegate to, returning null"); + } + return null; + } + + this.index++; + return this.resolvers.get(this.index); } - @Override - public String resolveUrl(String resourcePath, List locations) { - return this.resolvers.get(0).resolveUrl(resourcePath, locations, this); + private void logBefore(ResourceResolver resolver) { + if (logger.isTraceEnabled()) { + logger.trace("Calling " + resolver.getClass().getName() + " at index [" + this.index + "]"); + } } - private Resource applyTransformers(HttpServletRequest request, Resource resource) throws IOException { - for (ResourceTransformer transformer : this.transformers) { - if (transformer.handles(request, resource)) { - return applyTransformers(request, transformer.transform(resource)); - } + private void logAfter(ResourceResolver resolver, Object result) { + if (logger.isTraceEnabled()) { + logger.trace(resolver.getClass().getName() + " returned " + result); } - return resource; } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/FingerprintResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/FingerprintResourceResolver.java index 2daf1080cc..528a9e9bf7 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/FingerprintResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/FingerprintResourceResolver.java @@ -34,9 +34,10 @@ import org.springframework.util.StringUtils; /** * * @author Jeremy Grelle + * @author Rossen Stoyanchev * @since 4.0 */ -public class FingerprintResourceResolver extends AbstractResourceResolver { +public class FingerprintResourceResolver implements ResourceResolver { private static final Log logger = LogFactory.getLog(FingerprintResourceResolver.class); @@ -44,23 +45,25 @@ public class FingerprintResourceResolver extends AbstractResourceResolver { @Override - protected Resource resolveInternal(HttpServletRequest request, String path, List locations, - ResourceResolverChain chain, Resource resolved) { + public Resource resolveResource(HttpServletRequest request, String requestPath, + List locations, ResourceResolverChain chain) { // First try the resolved full path, in case resource has been written that way to disk at build-time // or the resource is requested without fingerprint + + Resource resolved = chain.resolveResource(request, requestPath, locations); if (resolved != null) { return resolved; } // Now try extracting and matching the hash for dev mode - String hash = extractHash(path); + String hash = extractHash(requestPath); if (StringUtils.isEmpty(hash)) { return null; } - String simplePath = StringUtils.delete(path, "-" + hash); - Resource baseResource = chain.next(this).resolve(request, simplePath, locations, chain); + String simplePath = StringUtils.delete(requestPath, "-" + hash); + Resource baseResource = chain.resolveResource(request, simplePath, locations); if (baseResource == null) { logger.debug("Failed to find resource after removing fingerprint: " + simplePath); return null; @@ -101,11 +104,11 @@ public class FingerprintResourceResolver extends AbstractResourceResolver { } @Override - public String resolveUrl(String resourcePath, List locations, ResourceResolverChain chain) { + public String resolveUrlPath(String resourcePath, List locations, ResourceResolverChain chain) { // TODO - Consider caching here for better efficiency - String baseUrl = chain.next(this).resolveUrl(resourcePath, locations, chain); + String baseUrl = chain.resolveUrlPath(resourcePath, locations); if (StringUtils.hasText(baseUrl)) { - Resource original = chain.next(this).resolve(null, resourcePath, locations, chain); + Resource original = chain.resolveResource(null, resourcePath, locations); String hash = calculateHash(original); return StringUtils.stripFilenameExtension(baseUrl) + "-" + hash + "." + StringUtils.getFilenameExtension(baseUrl); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/GzipResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/GzipResourceResolver.java index e18d4b0546..3569e1f30b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/GzipResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/GzipResourceResolver.java @@ -41,15 +41,16 @@ import org.springframework.core.io.Resource; * @author Rossen Stoyanchev * @since 4.0 */ -public class GzipResourceResolver extends AbstractResourceResolver { +public class GzipResourceResolver implements ResourceResolver { private static final Log logger = LogFactory.getLog(GzipResourceResolver.class); @Override - protected Resource resolveInternal(HttpServletRequest request, String path, - List locations, ResourceResolverChain chain, Resource resource) { + public Resource resolveResource(HttpServletRequest request, String requestPath, + List locations, ResourceResolverChain chain) { + Resource resource = chain.resolveResource(request, requestPath, locations); if ((resource == null) || !isGzipAccepted(request)) { return resource; } @@ -72,6 +73,11 @@ public class GzipResourceResolver extends AbstractResourceResolver { return ((value != null) && value.toLowerCase().contains("gzip")); } + @Override + public String resolveUrlPath(String resourcePath, List locations, ResourceResolverChain chain) { + return chain.resolveUrlPath(resourcePath, locations); + } + private static final class GzippedResource extends AbstractResource implements EncodedResource { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/LessResourceTransformer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/LessResourceTransformer.java index b759a79f22..2268a3fdde 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/LessResourceTransformer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/LessResourceTransformer.java @@ -61,7 +61,7 @@ public class LessResourceTransformer implements ResourceTransformer { } @Override - public boolean handles(HttpServletRequest request, Resource original) { + public boolean willTransform(HttpServletRequest request, Resource original) { return LESS_EXT.equals(StringUtils.getFilenameExtension(original.getFilename())); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ExtensionMappingResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathExtensionResourceResolver.java similarity index 76% rename from spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ExtensionMappingResourceResolver.java rename to spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathExtensionResourceResolver.java index 80ea21fbe7..f9e02e7091 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ExtensionMappingResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathExtensionResourceResolver.java @@ -32,36 +32,38 @@ import org.springframework.util.StringUtils; /** * * @author Jeremy Grelle + * @author Rossen Stoyanchev * @since 4.0 */ -public class ExtensionMappingResourceResolver extends AbstractResourceResolver { +public class PathExtensionResourceResolver implements ResourceResolver { - private static final Log logger = LogFactory.getLog(ExtensionMappingResourceResolver.class); + private static final Log logger = LogFactory.getLog(PathExtensionResourceResolver.class); private final boolean compareTimeStamp; - public ExtensionMappingResourceResolver() { + public PathExtensionResourceResolver() { this.compareTimeStamp = false; } - public ExtensionMappingResourceResolver(boolean compareTimeStamp) { + public PathExtensionResourceResolver(boolean compareTimeStamp) { this.compareTimeStamp = compareTimeStamp; } @Override - protected Resource resolveInternal(HttpServletRequest request, String path, - List locations, ResourceResolverChain chain, Resource resource) { + public Resource resolveResource(HttpServletRequest request, String requestPath, + List locations, ResourceResolverChain chain) { + Resource resource = chain.resolveResource(request, requestPath, locations); if ((resource != null) && !this.compareTimeStamp) { return resource; } for (Resource location : locations) { - String baseFilename = StringUtils.getFilename(path); + String baseFilename = StringUtils.getFilename(requestPath); try { - Resource basePath = location.createRelative(StringUtils.delete(path, baseFilename)); + Resource basePath = location.createRelative(StringUtils.delete(requestPath, baseFilename)); if (basePath.getFile().isDirectory()) { for (String fileName : basePath.getFile().list(new ExtensionFilenameFilter(baseFilename))) { //Always use the first match @@ -84,15 +86,15 @@ public class ExtensionMappingResourceResolver extends AbstractResourceResolver { } @Override - public String resolveUrl(String resourcePath, List locations, + public String resolveUrlPath(String resourcePath, List locations, ResourceResolverChain chain) { - String resolved = super.resolveUrl(resourcePath, locations, chain); + String resolved = chain.resolveUrlPath(resourcePath, locations); if (StringUtils.hasText(resolved)) { return resolved; } - Resource mappedResource = resolveInternal(null, resourcePath, locations, chain, null); + Resource mappedResource = resolveResource(null, resourcePath, locations, chain); if (mappedResource != null) { return resourcePath; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathResourceResolver.java index a7a7381b5d..2a55e7e0a8 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathResourceResolver.java @@ -28,7 +28,7 @@ import org.springframework.core.io.Resource; /** * A simple path-based {@link ResourceResolver} that appends the request path to each - * configured Resource location and checks if such a Resource exists. + * configured Resource location and checks if such a resource exists. * * @author Jeremy Grelle * @author Rossen Stoyanchev @@ -40,13 +40,18 @@ public class PathResourceResolver implements ResourceResolver { @Override - public Resource resolve(HttpServletRequest request, String requestPath, List locations, - ResourceResolverChain chain) { + public Resource resolveResource(HttpServletRequest request, + String requestPath, List locations, ResourceResolverChain chain) { - return resolveInternal(requestPath, locations); + return getResource(requestPath, locations); } - private Resource resolveInternal(String path, List locations) { + @Override + public String resolveUrlPath(String resourcePath, List locations, ResourceResolverChain chain) { + return (getResource(resourcePath, locations) != null) ? resourcePath : null; + } + + private Resource getResource(String path, List locations) { for (Resource location : locations) { try { if (logger.isDebugEnabled()) { @@ -70,12 +75,4 @@ public class PathResourceResolver implements ResourceResolver { return null; } - @Override - public String resolveUrl(String resourcePath, List locations, ResourceResolverChain chain) { - if (resolveInternal(resourcePath, locations) != null) { - return resourcePath; - } - return null; - } - } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index ca9c838623..48f48e80a4 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -85,9 +85,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H private List resourceResolvers = new ArrayList(); - private List resourceTransformers; - - private ResourceResolverChain resolverChain; + private List resourceTransformers = new ArrayList(); public ResourceHttpRequestHandler() { @@ -123,21 +121,19 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H return this.resourceResolvers; } - public void setResourceTransformers(List resourceTransformers) { - this.resourceTransformers = resourceTransformers; + public void setResourceTransformers(List transformers) { + this.resourceTransformers = (transformers != null) ? transformers : new ArrayList(); } public List getResourceTransformers() { return this.resourceTransformers; } - @Override public void afterPropertiesSet() throws Exception { if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) { logger.warn("Locations list is empty. No resources will be served"); } - this.resolverChain = new DefaultResourceResolverChain(this.resourceResolvers, this.resourceTransformers); } /** @@ -208,7 +204,19 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H return null; } - return this.resolverChain.resolveAndTransform(request, path, this.locations); + ResourceResolverChain chain = new DefaultResourceResolverChain(this.resourceResolvers); + Resource resource = chain.resolveResource(request, path, this.locations); + + return (resource != null) ? applyTransformers(request, resource) : null; + } + + private Resource applyTransformers(HttpServletRequest request, Resource resource) throws IOException { + for (ResourceTransformer transformer : this.resourceTransformers) { + if (transformer.willTransform(request, resource)) { + return applyTransformers(request, transformer.transform(resource)); + } + } + return resource; } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceResolver.java index 52d8771aef..bec80b02db 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceResolver.java @@ -24,15 +24,40 @@ import org.springframework.core.io.Resource; /** + * A strategy for two way resolution of URL paths to actual {@link Resource}s located + * from one or more configured locations. * * @author Jeremy Grelle + * @author Rossen Stoyanchev * @since 4.0 */ public interface ResourceResolver { - public Resource resolve(HttpServletRequest request, String requestPath, - List locations, ResourceResolverChain chain); + /** + * Resolve the URL path of an incoming request to an actual {@link Resource}. + * + * @param request the current request + * @param requestPath the portion of the request path to use + * @param locations the configured locations where to look up resources + * @param chain the chain with remaining resolvers to delegate to + * + * @return the resolved {@link Resource} or {@code null} if this resolver could not + * resolve the resource + */ + Resource resolveResource(HttpServletRequest request, String requestPath, + List locations, ResourceResolverChain chain); + + /** + * Resolve the given resource path to a URL path. This is useful when rendering URL + * links to clients to determine the actual URL to use. + * + * @param resourcePath the resource path + * @param locations the configured locations where to look up resources + * @param chain the chain with remaining resolvers to delegate to + * + * @return the resolved URL path or {@code null} if this resolver could not resolve + * the given resource path + */ + String resolveUrlPath(String resourcePath, List locations, ResourceResolverChain chain); - public String resolveUrl(String resourcePath, List locations, - ResourceResolverChain chain); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceResolverChain.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceResolverChain.java index be82752b66..339082faa6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceResolverChain.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceResolverChain.java @@ -16,7 +16,6 @@ package org.springframework.web.servlet.resource; -import java.io.IOException; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -24,18 +23,38 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.core.io.Resource; - /** + * A contract for invoking a chain of {@link ResourceResolver}s. Each resolver is passed a + * reference to the chain allowing it delegate to the remaining resolvers. * * @author Jeremy Grelle + * @author Rossen Stoyanchev * @since 4.0 */ public interface ResourceResolverChain { - public Resource resolveAndTransform(HttpServletRequest request, String path, List locations) - throws IOException; - - public ResourceResolver next(ResourceResolver current); + /** + * Resolve the URL path of an incoming request to an actual {@link Resource}. + * + * @param request the current request + * @param requestPath the portion of the request path to use + * @param locations the configured locations where to look up resources + * + * @return the resolved {@link Resource} or {@code null} if this resolver could not + * resolve the resource + */ + Resource resolveResource(HttpServletRequest request, String requestPath, List locations); + + /** + * Resolve the given resource path to a URL path. This is useful when rendering URL + * links to clients to determine the actual URL to use. + * + * @param resourcePath the resource path + * @param locations the configured locations where to look up resources + * + * @return the resolved URL path or {@code null} if this resolver could not resolve + * the given resource path + */ + String resolveUrlPath(String resourcePath, List locations); - public String resolveUrl(String resourcePath, List locations); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java index 4d9b57d5e8..b457bb568e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java @@ -24,14 +24,30 @@ import org.springframework.core.io.Resource; /** + * A strategy for transforming a resource. * * @author Jeremy Grelle + * @author Rossen Stoyanchev * @since 4.0 */ public interface ResourceTransformer { - public Resource transform(Resource original) throws IOException; - - public boolean handles(HttpServletRequest request, Resource original); + /** + * Whether this transformer can transform the given resource. + * + * @param request the context request + * @param resource the candidate resource to transform + */ + boolean willTransform(HttpServletRequest request, Resource resource); + + /** + * Transform the given resource and return a new resource. + * + * @param resource the resource to transform + * @return the transformed resource, never {@code null} + * + * @throws IOException if the transformation fails + */ + Resource transform(Resource resource) throws IOException; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java index e95773f977..84008cd029 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java @@ -39,10 +39,11 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter { private ResourceUrlMapper mapper; + @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + filterChain.doFilter(request, new ResourceUrlResponseWrapper(request, response)); } @@ -52,6 +53,7 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter { this.mapper = appContext.getBean(ResourceUrlMapper.class); } + private class ResourceUrlResponseWrapper extends HttpServletResponseWrapper { private final UrlPathHelper pathHelper = new UrlPathHelper(); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlMapper.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlMapper.java index 43b9c3dbd0..4885d05eba 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlMapper.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlMapper.java @@ -84,8 +84,9 @@ public class ResourceUrlMapper implements BeanPostProcessor, ApplicationListener ResourceHttpRequestHandler handler = mapping.getValue(); String nestedPath = matcher.extractPathWithinPattern(mapping.getKey(), resourcePath); String prefix = resourcePath.replace(nestedPath, ""); - String url = new DefaultResourceResolverChain(handler.getResourceResolvers(), handler. - getResourceTransformers()).resolveUrl(nestedPath, handler.getLocations()); + List resolvers = handler.getResourceResolvers(); + DefaultResourceResolverChain chain = new DefaultResourceResolverChain(resolvers); + String url = chain.resolveUrlPath(nestedPath, handler.getLocations()); if (url != null) { return prefix + url; } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ExtensionMappingResourceResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ExtensionMappingResourceResolverTests.java index 2cd5a890a2..fb4841ab08 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ExtensionMappingResourceResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ExtensionMappingResourceResolverTests.java @@ -40,9 +40,9 @@ public class ExtensionMappingResourceResolverTests { @Before public void setUp() { List resolvers = new ArrayList(); - resolvers.add(new ExtensionMappingResourceResolver()); + resolvers.add(new PathExtensionResourceResolver()); resolvers.add(new PathResourceResolver()); - resolver = new DefaultResourceResolverChain(resolvers, new ArrayList()); + resolver = new DefaultResourceResolverChain(resolvers); locations = new ArrayList(); locations.add(new ClassPathResource("test/", getClass())); locations.add(new ClassPathResource("testalternatepath/", getClass())); @@ -52,7 +52,7 @@ public class ExtensionMappingResourceResolverTests { public void resolveLessResource() throws Exception { String resourceId = "zoo.css"; Resource resource = new ClassPathResource("test/" + resourceId + ".less", getClass()); - Resource resolved = resolver.resolveAndTransform(null, resourceId, locations); + Resource resolved = resolver.resolveResource(null, resourceId, locations); assertEquals(resource, resolved); } @@ -60,6 +60,6 @@ public class ExtensionMappingResourceResolverTests { public void resolveLessUrl() { String resourceId = "zoo.css"; String url = "zoo.css"; - assertEquals(url, resolver.resolveUrl(resourceId, locations)); + assertEquals(url, resolver.resolveUrlPath(resourceId, locations)); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/FingerprintResourceResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/FingerprintResourceResolverTests.java index 8e8f4c9993..e9c202a3a8 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/FingerprintResourceResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/FingerprintResourceResolverTests.java @@ -48,7 +48,7 @@ public class FingerprintResourceResolverTests { List resolvers = new ArrayList(); resolvers.add(resolver); resolvers.add(new PathResourceResolver()); - chain = new DefaultResourceResolverChain(resolvers, new ArrayList()); + chain = new DefaultResourceResolverChain(resolvers); locations = new ArrayList(); locations.add(new ClassPathResource("test/", getClass())); locations.add(new ClassPathResource("testalternatepath/", getClass())); @@ -59,7 +59,7 @@ public class FingerprintResourceResolverTests { public void resolveWithoutHash() throws Exception { String file = "bar.css"; Resource expected = new ClassPathResource("test/" + file, getClass()); - Resource actual = chain.resolveAndTransform(null, file, locations); + Resource actual = chain.resolveResource(null, file, locations); assertEquals(expected, actual); } @@ -67,14 +67,14 @@ public class FingerprintResourceResolverTests { @Test public void resolveWithHashNoMatch() throws Exception { String file = "bogus-e36d2e05253c6c7085a91522ce43a0b4.css"; - assertNull(chain.resolveAndTransform(null, file, locations)); + assertNull(chain.resolveResource(null, file, locations)); } @Test public void resolveStaticFingerprintedResource() throws Exception { String file = "foo-e36d2e05253c6c7085a91522ce43a0b4.css"; Resource expected = new ClassPathResource("test/"+file, getClass()); - Resource actual = chain.resolveAndTransform(null, file, locations); + Resource actual = chain.resolveResource(null, file, locations); assertEquals(expected, actual); } @@ -84,7 +84,7 @@ public class FingerprintResourceResolverTests { Resource expected = new ClassPathResource("test/bar.css", getClass()); String hash = DigestUtils.md5DigestAsHex(FileCopyUtils.copyToByteArray(expected.getInputStream())); String path = "/bar-" + hash + ".css"; - Resource actual = chain.resolveAndTransform(null, path, locations); + Resource actual = chain.resolveResource(null, path, locations); assertEquals(expected, actual); } @@ -94,7 +94,7 @@ public class FingerprintResourceResolverTests { Resource expected = new ClassPathResource("test/bar.min.css", getClass()); String hash = DigestUtils.md5DigestAsHex(FileCopyUtils.copyToByteArray(expected.getInputStream())); String path = "/bar.min-" + hash + ".css"; - Resource actual = chain.resolveAndTransform(null, path, locations); + Resource actual = chain.resolveResource(null, path, locations); assertEquals(expected, actual); } @@ -104,7 +104,7 @@ public class FingerprintResourceResolverTests { Resource expected = new ClassPathResource("test/foo-bar/foo-bar.css", getClass()); String hash = DigestUtils.md5DigestAsHex(FileCopyUtils.copyToByteArray(expected.getInputStream())); String path = "/foo-bar/foo-bar-" + hash + ".css"; - Resource actual = chain.resolveAndTransform(null, path, locations); + Resource actual = chain.resolveResource(null, path, locations); assertEquals(expected, actual); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/GzipResourceResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/GzipResourceResolverTests.java index 0e05aeedea..61f161ba47 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/GzipResourceResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/GzipResourceResolverTests.java @@ -72,7 +72,7 @@ public class GzipResourceResolverTests { resolvers.add(new GzipResourceResolver()); resolvers.add(new FingerprintResourceResolver()); resolvers.add(new PathResourceResolver()); - resolver = new DefaultResourceResolverChain(resolvers, new ArrayList()); + resolver = new DefaultResourceResolverChain(resolvers); locations = new ArrayList(); locations.add(new ClassPathResource("test/", getClass())); locations.add(new ClassPathResource("testalternatepath/", getClass())); @@ -85,7 +85,7 @@ public class GzipResourceResolverTests { String file = "js/foo.js"; String gzFile = file+".gz"; Resource resource = new ClassPathResource("test/"+gzFile, getClass()); - Resource resolved = resolver.resolveAndTransform(request, file, locations); + Resource resolved = resolver.resolveResource(request, file, locations); assertEquals(resource.getDescription(), resolved.getDescription()); assertEquals(new ClassPathResource("test/"+file).getFilename(), resolved.getFilename()); @@ -100,7 +100,7 @@ public class GzipResourceResolverTests { String file = "foo-e36d2e05253c6c7085a91522ce43a0b4.css"; String gzFile = file+".gz"; Resource resource = new ClassPathResource("test/"+gzFile, getClass()); - Resource resolved = resolver.resolveAndTransform(request, file, locations); + Resource resolved = resolver.resolveResource(request, file, locations); assertEquals(resource.getDescription(), resolved.getDescription()); assertEquals(new ClassPathResource("test/"+file).getFilename(), resolved.getFilename()); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlMapperTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlMapperTests.java index f0397af5fc..2c6ee8f4b0 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlMapperTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlMapperTests.java @@ -86,7 +86,7 @@ public class ResourceUrlMapperTests { @Test public void getExtensionMappedResourceUrl() { List resolvers = new ArrayList(); - resolvers.add(new ExtensionMappingResourceResolver()); + resolvers.add(new PathExtensionResourceResolver()); resolvers.add(new PathResourceResolver()); handler.setResourceResolvers(resolvers); resetMapper(); diff --git a/spring-webmvc/src/test/resources/log4j.xml b/spring-webmvc/src/test/resources/log4j.xml index 785c094dde..1f7f3d0980 100644 --- a/spring-webmvc/src/test/resources/log4j.xml +++ b/spring-webmvc/src/test/resources/log4j.xml @@ -19,6 +19,10 @@ + + + +