diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AppCacheManifestTransformer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AppCacheManifestTransformer.java index da6634fd86a..ae0666ab540 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AppCacheManifestTransformer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AppCacheManifestTransformer.java @@ -58,7 +58,7 @@ import org.springframework.util.StringUtils; * applications spec * @since 4.1 */ -public class AppCacheManifestTransformer implements ResourceTransformer { +public class AppCacheManifestTransformer extends ResourceTransformerSupport { private static final String MANIFEST_HEADER = "CACHE MANIFEST"; @@ -129,7 +129,7 @@ public class AppCacheManifestTransformer implements ResourceTransformer { hashBuilder.appendString(line); } else { - contentWriter.write(currentTransformer.transform(line, hashBuilder, resource, transformerChain) + "\n"); + contentWriter.write(currentTransformer.transform(line, hashBuilder, resource, transformerChain, request) + "\n"); } } @@ -151,14 +151,14 @@ public class AppCacheManifestTransformer implements ResourceTransformer { * for the current manifest section (CACHE, NETWORK, FALLBACK, etc). */ String transform(String line, HashBuilder builder, Resource resource, - ResourceTransformerChain transformerChain) throws IOException; + ResourceTransformerChain transformerChain, HttpServletRequest request) throws IOException; } private static class NoOpSection implements SectionTransformer { - public String transform(String line, HashBuilder builder, Resource resource, ResourceTransformerChain transformerChain) - throws IOException { + public String transform(String line, HashBuilder builder, Resource resource, + ResourceTransformerChain transformerChain, HttpServletRequest request) throws IOException { builder.appendString(line); return line; @@ -166,17 +166,18 @@ public class AppCacheManifestTransformer implements ResourceTransformer { } - private static class CacheSection implements SectionTransformer { + private class CacheSection implements SectionTransformer { private final String COMMENT_DIRECTIVE = "#"; @Override - public String transform(String line, HashBuilder builder, - Resource resource, ResourceTransformerChain transformerChain) throws IOException { + public String transform(String line, HashBuilder builder, Resource resource, + ResourceTransformerChain transformerChain, HttpServletRequest request) throws IOException { if (isLink(line) && !hasScheme(line)) { - Resource appCacheResource = transformerChain.getResolverChain().resolveResource(null, line, Arrays.asList(resource)); - String path = transformerChain.getResolverChain().resolveUrlPath(line, Arrays.asList(resource)); + ResourceResolverChain resolverChain = transformerChain.getResolverChain(); + Resource appCacheResource = resolverChain.resolveResource(null, line, Arrays.asList(resource)); + String path = resolveUrlPath(line, request, resource, transformerChain); builder.appendResource(appCacheResource); if (logger.isTraceEnabled()) { logger.trace("Link modified: " + path + " (original: " + line + ")"); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java index 83be432b6d2..7fd4be40a58 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java @@ -27,7 +27,6 @@ import java.io.IOException; import java.io.StringWriter; import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -47,7 +46,7 @@ import java.util.Set; * @author Rossen Stoyanchev * @since 4.1 */ -public class CssLinkResourceTransformer implements ResourceTransformer { +public class CssLinkResourceTransformer extends ResourceTransformerSupport { private static final Log logger = LogFactory.getLog(CssLinkResourceTransformer.class); @@ -103,7 +102,7 @@ public class CssLinkResourceTransformer implements ResourceTransformer { String link = content.substring(info.getStart(), info.getEnd()); String newLink = null; if (!hasScheme(link)) { - newLink = transformerChain.getResolverChain().resolveUrlPath(link, Arrays.asList(resource)); + newLink = resolveUrlPath(link, request, resource, transformerChain); } if (logger.isTraceEnabled()) { if (newLink != null && !link.equals(newLink)) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformerSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformerSupport.java new file mode 100644 index 00000000000..a3826974769 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformerSupport.java @@ -0,0 +1,98 @@ +/* + * Copyright 2002-2014 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.Arrays; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.core.io.Resource; + +/** + * A base class for a {@code ResourceTransformer} with an optional helper method + * for resolving public links within a transformed resource. + * + * @author Brian Clozel + * @author Rossen Stoyanchev + * @since 4.1 + */ +public abstract class ResourceTransformerSupport implements ResourceTransformer { + + private ResourceUrlProvider resourceUrlProvider; + + + /** + * Configure a {@link ResourceUrlProvider} to use when resolving the public + * URL of links in a transformed resource (e.g. import links in a CSS file). + * This is required only for links expressed as full paths, i.e. including + * context and servlet path, and not for relative links. + * + *
By default this property is not set. In that case if a
+ * {@code ResourceUrlProvider} is needed an attempt is made to find the
+ * {@code ResourceUrlProvider} exposed through the
+ * {@link org.springframework.web.servlet.resource.ResourceUrlProviderExposingInterceptor
+ * ResourceUrlProviderExposingInterceptor} (configured by default in the MVC
+ * Java config and XML namespace). Therefore explicitly configuring this
+ * property should not be needed in most cases.
+ * @param resourceUrlProvider the URL provider to use
+ */
+ public void setResourceUrlProvider(ResourceUrlProvider resourceUrlProvider) {
+ this.resourceUrlProvider = resourceUrlProvider;
+ }
+
+ /**
+ * @return the configured {@code ResourceUrlProvider}.
+ */
+ public ResourceUrlProvider getResourceUrlProvider() {
+ return this.resourceUrlProvider;
+ }
+
+
+ /**
+ * A transformer can use this method when a resource being transformed
+ * contains links to other resources. Such links need to be replaced with the
+ * public facing link as determined by the resource resolver chain (e.g. the
+ * public URL may have a version inserted).
+ *
+ * @param resourcePath the path to a resource that needs to be re-written
+ * @param request the current request
+ * @param resource the resource being transformed
+ * @param transformerChain the transformer chain
+ * @return the resolved URL or null
+ */
+ protected String resolveUrlPath(String resourcePath, HttpServletRequest request,
+ Resource resource, ResourceTransformerChain transformerChain) {
+
+ if (!resourcePath.startsWith("/")) {
+ // try resolving as relative path
+ return transformerChain.getResolverChain().resolveUrlPath(resourcePath, Arrays.asList(resource));
+ }
+ else {
+ // full resource path
+ ResourceUrlProvider urlProvider = findResourceUrlProvider(request);
+ return (urlProvider != null ? urlProvider.getForRequestUrl(request, resourcePath) : null);
+ }
+ }
+
+ private ResourceUrlProvider findResourceUrlProvider(HttpServletRequest request) {
+ if (this.resourceUrlProvider != null) {
+ return this.resourceUrlProvider;
+ }
+ return (ResourceUrlProvider) request.getAttribute(
+ ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR);
+ }
+
+}
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/LinkRewriteTransformerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/LinkRewriteTransformerTests.java
new file mode 100644
index 00000000000..8751e87d73e
--- /dev/null
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/LinkRewriteTransformerTests.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2002-2014 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 static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.mock.web.test.MockHttpServletRequest;
+import org.springframework.web.servlet.HandlerMapping;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Unit tests for {@code LinkRewriteTransformer}
+ *
+ * @author Brian Clozel
+ * @author Rossen Stoyanchev
+ */
+public class LinkRewriteTransformerTests {
+
+ private ResourceTransformerChain transformerChain;
+
+ private TestTransformer transformer;
+
+ private MockHttpServletRequest request;
+
+
+ @Before
+ public void setUp() {
+ VersionResourceResolver versionResolver = new VersionResourceResolver();
+ versionResolver.setStrategyMap(Collections.singletonMap("/**", new ContentVersionStrategy()));
+
+ List