Update resource handler XML Namespace

This change introduces a new <mvc:resource-chain/> tag that mirrors
the ResourceChainRegistration java config counterpart.

Resolvers and Transformers can be registered with bean/ref tags, and
specific tags have been created for <mvc:version-resovlver> and
<mvc:resource-cache> in order to make common configurations easier.

Note that a specific "auto-configuration" attribute on the
resource-chain allows to completely disable default registration of
Resolvers and Transformers (sane defaults considered by the Framework).

  <mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/">
      <mvc:resource-chain>
        <mvc:resource-cache cache="resourceCache"/>
        <mvc:resolvers>
          <mvc:version-resolver>
            <mvc:fixed-version-strategy version="abc" patterns="/**/*.js"/>
            <mvc:content-version-strategy patterns="/**"/>
          </mvc:version-resolver>
        </mvc:resolvers>
        <mvc:transformers>
          <bean class="org.springframework.web.servlet.resource.AppCacheManifestTransformer"/>
        </mvc:transformers>
      </mvc:resource-chain>
  </mvc:resources>

This also fixes a typo in the class name of
AppCacheManifestResourceTransfo*r*mer.

Issue: SPR-12129
This commit is contained in:
Brian Clozel
2014-08-29 15:36:51 +02:00
parent 8008e466aa
commit d9f4016c35
9 changed files with 386 additions and 68 deletions

View File

@@ -22,6 +22,7 @@ import java.util.Map;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.ManagedList;
@@ -29,25 +30,43 @@ import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
import org.springframework.web.servlet.resource.CachingResourceResolver;
import org.springframework.web.servlet.resource.CachingResourceTransformer;
import org.springframework.web.servlet.resource.ContentVersionStrategy;
import org.springframework.web.servlet.resource.CssLinkResourceTransformer;
import org.springframework.web.servlet.resource.FixedVersionStrategy;
import org.springframework.web.servlet.resource.PathResourceResolver;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import org.springframework.web.servlet.resource.ResourceResolver;
import org.springframework.web.servlet.resource.ResourceTransformer;
import org.springframework.web.servlet.resource.VersionResourceResolver;
/**
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a
* {@code resources} element to register a {@link ResourceHttpRequestHandler}.
* Will also register a {@link SimpleUrlHandlerMapping} for mapping resource requests,
* {@code resources} element to register a {@link ResourceHttpRequestHandler} and
* register a {@link SimpleUrlHandlerMapping} for mapping resource requests,
* and a {@link HttpRequestHandlerAdapter}.
* Will also create a resource handling chain with {@link ResourceResolver}s and {@link ResourceTransformer}s.
*
* @author Keith Donald
* @author Jeremy Grelle
* @author Brian Clozel
* @since 3.0.4
*/
class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
private static final String RESOURCE_CHAIN_CACHE = "spring-resource-chain-cache";
private static final String VERSION_RESOLVER_ELEMENT = "version-resolver";
private static final String VERSION_STRATEGY_ELEMENT = "version-strategy";
private static final String FIXED_VERSION_STRATEGY_ELEMENT = "fixed-version-strategy";
private static final String CONTENT_VERSION_STRATEGY_ELEMENT = "content-version-strategy";
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
@@ -109,14 +128,9 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
resourceHandlerDef.getPropertyValues().add("cacheSeconds", cacheSeconds);
}
ManagedList<? super Object> resourceResolvers = parseResourceResolvers(parserContext, element, source);
if (!resourceResolvers.isEmpty()) {
resourceHandlerDef.getPropertyValues().add("resourceResolvers", resourceResolvers);
}
ManagedList<? super Object> resourceTransformers = parseResourceTransformers(parserContext, element, source);
if (!resourceTransformers.isEmpty()) {
resourceHandlerDef.getPropertyValues().add("resourceTransformers", resourceTransformers);
Element resourceChainElement = DomUtils.getChildElementByTagName(element, "resource-chain");
if(resourceChainElement != null) {
parseResourceChain(resourceHandlerDef, parserContext, resourceChainElement, source);
}
String beanName = parserContext.getReaderContext().generateBeanName(resourceHandlerDef);
@@ -125,30 +139,145 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
return beanName;
}
private ManagedList<? super Object> parseResourceResolvers(ParserContext parserContext, Element element, Object source) {
Element resolversElement = DomUtils.getChildElementByTagName(element, "resolvers");
private void parseResourceChain(RootBeanDefinition resourceHandlerDef, ParserContext parserContext,
Element element, Object source) {
String autoRegistration = element.getAttribute("auto-registration");
boolean isAutoRegistration = !(StringUtils.hasText(autoRegistration) && "false".equals(autoRegistration));
ManagedList<? super Object> resourceResolvers = new ManagedList<Object>();
if (resolversElement != null) {
resourceResolvers.setSource(source);
for (Element beanElement : DomUtils.getChildElementsByTagName(resolversElement, "bean", "ref")) {
Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null);
resourceResolvers.add(object);
}
resourceResolvers.setSource(source);
ManagedList<? super Object> resourceTransformers = new ManagedList<Object>();
resourceTransformers.setSource(source);
parseResourceCache(resourceResolvers, resourceTransformers, element, source);
parseResourceResolversTransformers(isAutoRegistration, resourceResolvers
, resourceTransformers, parserContext, element, source);
if (!resourceResolvers.isEmpty()) {
resourceHandlerDef.getPropertyValues().add("resourceResolvers", resourceResolvers);
}
if (!resourceTransformers.isEmpty()) {
resourceHandlerDef.getPropertyValues().add("resourceTransformers", resourceTransformers);
}
return resourceResolvers;
}
private ManagedList<? super Object> parseResourceTransformers(ParserContext parserContext, Element element, Object source) {
private void parseResourceCache(ManagedList<? super Object> resourceResolvers,
ManagedList<? super Object> resourceTransformers,
Element element, Object source) {
Element resourceCacheElement = DomUtils.getChildElementByTagName(element, "resource-cache");
if(resourceCacheElement != null) {
ConstructorArgumentValues cavs = new ConstructorArgumentValues();
RootBeanDefinition cachingResolverDef = new RootBeanDefinition(CachingResourceResolver.class);
cachingResolverDef.setSource(source);
cachingResolverDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
cachingResolverDef.setConstructorArgumentValues(cavs);
RootBeanDefinition cachingTransformerDef = new RootBeanDefinition(CachingResourceTransformer.class);
cachingTransformerDef.setSource(source);
cachingTransformerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
cachingTransformerDef.setConstructorArgumentValues(cavs);
String cacheBeanName = resourceCacheElement.getAttribute("cache");
if (StringUtils.hasText(cacheBeanName)) {
RuntimeBeanReference cacheRef = new RuntimeBeanReference(cacheBeanName);
cavs.addIndexedArgumentValue(0, cacheRef);
}
else {
ConstructorArgumentValues cacheCavs = new ConstructorArgumentValues();
cacheCavs.addIndexedArgumentValue(0, RESOURCE_CHAIN_CACHE);
RootBeanDefinition cacheDef = new RootBeanDefinition(ConcurrentMapCache.class);
cacheDef.setSource(source);
cacheDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
cacheDef.setConstructorArgumentValues(cacheCavs);
cavs.addIndexedArgumentValue(0, cacheDef);
}
resourceResolvers.add(cachingResolverDef);
resourceTransformers.add(cachingTransformerDef);
}
}
private void parseResourceResolversTransformers(boolean isAutoRegistration,
ManagedList<? super Object> resourceResolvers, ManagedList<? super Object> resourceTransformers,
ParserContext parserContext, Element element, Object source) {
Element resolversElement = DomUtils.getChildElementByTagName(element, "resolvers");
if (resolversElement != null) {
for (Element beanElement : DomUtils.getChildElements(resolversElement)) {
if(VERSION_RESOLVER_ELEMENT.equals(beanElement.getLocalName())) {
RootBeanDefinition versionResolverDef = parseVersionResolver(parserContext, beanElement, source);
versionResolverDef.setSource(source);
resourceResolvers.add(versionResolverDef);
if(isAutoRegistration) {
RootBeanDefinition cssLinkTransformerDef = new RootBeanDefinition(CssLinkResourceTransformer.class);
cssLinkTransformerDef.setSource(source);
cssLinkTransformerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
resourceTransformers.add(cssLinkTransformerDef);
}
} else {
Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null);
resourceResolvers.add(object);
}
}
}
if(isAutoRegistration) {
RootBeanDefinition pathResolverDef = new RootBeanDefinition(PathResourceResolver.class);
pathResolverDef.setSource(source);
pathResolverDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
resourceResolvers.add(pathResolverDef);
}
Element transformersElement = DomUtils.getChildElementByTagName(element, "transformers");
ManagedList<? super Object> resourceTransformers = new ManagedList<Object>();
if (transformersElement != null) {
resourceTransformers.setSource(source);
for (Element beanElement : DomUtils.getChildElementsByTagName(transformersElement, "bean", "ref")) {
Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null);
resourceTransformers.add(object);
}
}
return resourceTransformers;
}
private RootBeanDefinition parseVersionResolver(ParserContext parserContext, Element element, Object source) {
ManagedMap<String, ? super Object> strategyMap = new ManagedMap<String, Object>();
strategyMap.setSource(source);
RootBeanDefinition versionResolverDef = new RootBeanDefinition(VersionResourceResolver.class);
versionResolverDef.setSource(source);
versionResolverDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
versionResolverDef.getPropertyValues().addPropertyValue("strategyMap", strategyMap);
for (Element beanElement : DomUtils.getChildElements(element)) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(beanElement.getAttribute("patterns"));
Object strategy = null;
if(FIXED_VERSION_STRATEGY_ELEMENT.equals(beanElement.getLocalName())) {
ConstructorArgumentValues cavs = new ConstructorArgumentValues();
cavs.addIndexedArgumentValue(0, beanElement.getAttribute("version"));
RootBeanDefinition strategyDef = new RootBeanDefinition(FixedVersionStrategy.class);
strategyDef.setSource(source);
strategyDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
strategyDef.setConstructorArgumentValues(cavs);
strategy = strategyDef;
}
else if (CONTENT_VERSION_STRATEGY_ELEMENT.equals(beanElement.getLocalName())) {
RootBeanDefinition strategyDef = new RootBeanDefinition(ContentVersionStrategy.class);
strategyDef.setSource(source);
strategyDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
strategy = strategyDef;
}
else if (VERSION_STRATEGY_ELEMENT.equals(beanElement.getLocalName())) {
Element childElement = DomUtils.getChildElementsByTagName(beanElement, "bean", "ref").get(0);
strategy = parserContext.getDelegate().parsePropertySubElement(childElement, null);
}
for(String pattern : patterns) {
strategyMap.put(pattern, strategy);
}
}
return versionResolverDef;
}
}

View File

@@ -58,13 +58,13 @@ import org.springframework.util.StringUtils;
* applications spec</a>
* @since 4.1
*/
public class AppCacheManifestTransfomer implements ResourceTransformer {
public class AppCacheManifestTransformer implements ResourceTransformer {
private static final String MANIFEST_HEADER = "CACHE MANIFEST";
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private static final Log logger = LogFactory.getLog(AppCacheManifestTransfomer.class);
private static final Log logger = LogFactory.getLog(AppCacheManifestTransformer.class);
private final Map<String, SectionTransformer> sectionTransformers = new HashMap<String, SectionTransformer>();
@@ -74,7 +74,7 @@ public class AppCacheManifestTransfomer implements ResourceTransformer {
/**
* Create an AppCacheResourceTransformer that transforms files with extension ".manifest"
*/
public AppCacheManifestTransfomer() {
public AppCacheManifestTransformer() {
this("manifest");
}
@@ -82,7 +82,7 @@ public class AppCacheManifestTransfomer implements ResourceTransformer {
* Create an AppCacheResourceTransformer that transforms files with the extension
* given as a parameter.
*/
public AppCacheManifestTransfomer(String fileExtension) {
public AppCacheManifestTransformer(String fileExtension) {
this.fileExtension = fileExtension;
SectionTransformer noOpSection = new NoOpSection();

View File

@@ -338,6 +338,77 @@
</xsd:complexType>
</xsd:element>
<xsd:complexType name="resource-cache">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.servlet.resource.CachingResourceResolver"><![CDATA[
A ResourceResolver that resolves resources from a Cache or otherwise delegates to the resolver chain
and saves the result in the cache. Can use a custom Cache if provided as a bean reference in the "cache" attribute.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="cache" type="xsd:string" use="optional"/>
</xsd:complexType>
<xsd:complexType name="content-version-strategy">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.servlet.resource.ContentVersionStrategy"><![CDATA[
A VersionStrategy that calculates an Hex MD5 hashes from the content of the resource and appends it
to the file name, e.g. "styles/main-e36d2e05253c6c7085a91522ce43a0b4.css".
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="patterns" type="xsd:string" use="required" />
</xsd:complexType>
<xsd:complexType name="fixed-version-strategy">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.servlet.resource.FixedVersionStrategy"><![CDATA[
A VersionStrategy that relies on a fixed version applied as a request path prefix,
e.g. reduced SHA, version name, release date, etc.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="version" type="xsd:string" use="required" />
<xsd:attribute name="patterns" type="xsd:string" use="required" />
</xsd:complexType>
<xsd:complexType name="resource-version-strategy">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.servlet.resource.VersionStrategy"><![CDATA[
A strategy for extracting and embedding a resource version in its URL path.
]]></xsd:documentation>
</xsd:annotation>
<xsd:choice minOccurs="1" maxOccurs="1">
<xsd:element ref="beans:bean">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.servlet.resource.VersionStrategy"><![CDATA[
A VersionStrategy bean definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element ref="beans:ref">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.servlet.resource.VersionStrategy"><![CDATA[
A reference to a VersionStrategy bean.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:choice>
<xsd:attribute name="patterns" type="xsd:string" use="required" />
</xsd:complexType>
<xsd:complexType name="version-resolver">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.servlet.resource.VersionResourceResolver"><![CDATA[
Resolves request paths containing a version string that can be used as part of an HTTP caching strategy
in which a resource is cached with a far future date (e.g. 1 year) and cached until the version,
and therefore the URL, is changed.
]]></xsd:documentation>
</xsd:annotation>
<xsd:choice maxOccurs="unbounded">
<xsd:element type="content-version-strategy" name="content-version-strategy" />
<xsd:element type="fixed-version-strategy" name="fixed-version-strategy" />
<xsd:element type="resource-version-strategy" name="version-strategy" />
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="resource-resolvers">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.servlet.resource.ResourceResolver"><![CDATA[
@@ -348,6 +419,7 @@
</xsd:annotation>
<xsd:sequence>
<xsd:choice maxOccurs="unbounded">
<xsd:element type="version-resolver" name="version-resolver" />
<xsd:element ref="beans:bean">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.servlet.resource.ResourceResolver"><![CDATA[
@@ -393,6 +465,22 @@
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="resource-chain">
<xsd:annotation>
<xsd:documentation source="org.springframework.web.servlet.config.annotation.ResourceChainRegistration"><![CDATA[
Assists with the registration of resource resolvers and transformers.
Unless set to "false", the auto-registration add default Resolvers (a PathResourceResolver) and Transformers
(CssLinkResourceTransformer, if a VersionResourceResolver has been manually registered).
]]></xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="resource-cache" type="resource-cache" minOccurs="0" maxOccurs="1"/>
<xsd:element name="resolvers" type="resource-resolvers" minOccurs="0" maxOccurs="1"/>
<xsd:element name="transformers" type="resource-transformers" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="auto-registration" type="xsd:boolean" default="true" use="optional"/>
</xsd:complexType>
<xsd:element name="resources">
<xsd:annotation>
<xsd:documentation
@@ -403,8 +491,7 @@
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="resolvers" type="resource-resolvers" minOccurs="0" maxOccurs="1"/>
<xsd:element name="transformers" type="resource-transformers" minOccurs="0" maxOccurs="1"/>
<xsd:element name="resource-chain" type="resource-chain" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="mapping" use="required" type="xsd:string">
<xsd:annotation>

View File

@@ -16,6 +16,8 @@
package org.springframework.web.servlet.config;
import static org.junit.Assert.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
@@ -30,6 +32,7 @@ import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.Ordered;
import org.springframework.core.convert.ConversionService;
@@ -76,14 +79,19 @@ import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.servlet.resource.AppCacheManifestTransformer;
import org.springframework.web.servlet.resource.CachingResourceResolver;
import org.springframework.web.servlet.resource.CachingResourceTransformer;
import org.springframework.web.servlet.resource.ContentVersionStrategy;
import org.springframework.web.servlet.resource.CssLinkResourceTransformer;
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;
import org.springframework.web.servlet.resource.FixedVersionStrategy;
import org.springframework.web.servlet.resource.GzipResourceResolver;
import org.springframework.web.servlet.resource.PathResourceResolver;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import org.springframework.web.servlet.resource.ResourceResolver;
import org.springframework.web.servlet.resource.ResourceTransformer;
import org.springframework.web.servlet.resource.VersionResourceResolver;
import org.springframework.web.servlet.theme.ThemeChangeInterceptor;
import org.springframework.web.servlet.view.groovy.GroovyMarkupConfigurer;
import org.springframework.web.servlet.view.groovy.GroovyMarkupViewResolver;
@@ -96,8 +104,6 @@ import org.springframework.web.servlet.view.tiles3.TilesViewResolver;
import org.springframework.web.servlet.view.velocity.VelocityConfigurer;
import org.springframework.web.servlet.view.velocity.VelocityViewResolver;
import static org.junit.Assert.*;
/**
* Tests loading actual MVC namespace configuration.
*
@@ -328,7 +334,7 @@ public class MvcNamespaceTests {
@Test
public void testResourcesWithResolversTransformers() throws Exception {
loadBeanDefinitions("mvc-config-resources-resolvers-transformers.xml", 10);
loadBeanDefinitions("mvc-config-resources-chain.xml", 8);
SimpleUrlHandlerMapping mapping = appContext.getBean(SimpleUrlHandlerMapping.class);
assertNotNull(mapping);
@@ -338,14 +344,54 @@ public class MvcNamespaceTests {
assertNotNull(handler);
List<ResourceResolver> resolvers = handler.getResourceResolvers();
assertThat(resolvers, Matchers.hasSize(2));
assertThat(resolvers.get(0), Matchers.instanceOf(PathResourceResolver.class));
assertThat(resolvers, Matchers.hasSize(3));
assertThat(resolvers.get(0), Matchers.instanceOf(CachingResourceResolver.class));
assertThat(resolvers.get(1), Matchers.instanceOf(VersionResourceResolver.class));
assertThat(resolvers.get(2), Matchers.instanceOf(PathResourceResolver.class));
CachingResourceResolver cachingResolver = (CachingResourceResolver) resolvers.get(0);
assertThat(cachingResolver.getCache(), Matchers.instanceOf(TestResourceCache.class));
VersionResourceResolver versionResolver = (VersionResourceResolver) resolvers.get(1);
assertThat(versionResolver.getStrategyMap().get("/**/*.js"),
Matchers.instanceOf(FixedVersionStrategy.class));
assertThat(versionResolver.getStrategyMap().get("/**"),
Matchers.instanceOf(ContentVersionStrategy.class));
List<ResourceTransformer> transformers = handler.getResourceTransformers();
assertThat(transformers, Matchers.hasSize(3));
assertThat(transformers.get(0), Matchers.instanceOf(CachingResourceTransformer.class));
assertThat(transformers.get(1), Matchers.instanceOf(CssLinkResourceTransformer.class));
assertThat(transformers.get(2), Matchers.instanceOf(AppCacheManifestTransformer.class));
}
@Test
public void testResourcesWithResolversTransformersCustom() throws Exception {
loadBeanDefinitions("mvc-config-resources-chain-no-auto.xml", 9);
SimpleUrlHandlerMapping mapping = appContext.getBean(SimpleUrlHandlerMapping.class);
assertNotNull(mapping);
assertNotNull(mapping.getUrlMap().get("/resources/**"));
ResourceHttpRequestHandler handler = appContext.getBean((String)mapping.getUrlMap().get("/resources/**"),
ResourceHttpRequestHandler.class);
assertNotNull(handler);
List<ResourceResolver> resolvers = handler.getResourceResolvers();
assertThat(resolvers, Matchers.hasSize(3));
assertThat(resolvers.get(0), Matchers.instanceOf(VersionResourceResolver.class));
assertThat(resolvers.get(1), Matchers.instanceOf(GzipResourceResolver.class));
assertThat(resolvers.get(2), Matchers.instanceOf(PathResourceResolver.class));
VersionResourceResolver versionResolver = (VersionResourceResolver) resolvers.get(0);
assertThat(versionResolver.getStrategyMap().get("/**/*.js"),
Matchers.instanceOf(FixedVersionStrategy.class));
assertThat(versionResolver.getStrategyMap().get("/**"),
Matchers.instanceOf(ContentVersionStrategy.class));
List<ResourceTransformer> transformers = handler.getResourceTransformers();
assertThat(transformers, Matchers.hasSize(2));
assertThat(transformers.get(0), Matchers.instanceOf(CachingResourceTransformer.class));
assertThat(transformers.get(1), Matchers.instanceOf(CssLinkResourceTransformer.class));
assertThat(transformers.get(1), Matchers.instanceOf(AppCacheManifestTransformer.class));
}
@Test
@@ -823,5 +869,11 @@ public class MvcNamespaceTests {
public static class TestPathHelper extends UrlPathHelper { }
public static class TestResourceCache extends ConcurrentMapCache {
public TestResourceCache(String name) {
super(name);
}
}
}

View File

@@ -30,7 +30,7 @@ import org.springframework.mock.web.test.MockServletContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.resource.AppCacheManifestTransfomer;
import org.springframework.web.servlet.resource.AppCacheManifestTransformer;
import org.springframework.web.servlet.resource.CachingResourceResolver;
import org.springframework.web.servlet.resource.CachingResourceTransformer;
import org.springframework.web.servlet.resource.CssLinkResourceTransformer;
@@ -145,7 +145,7 @@ public class ResourceHandlerRegistryTests {
.addContentVersionStrategy("/**");
this.registration.resourceChain(true).addResolver(versionResolver)
.addTransformer(new AppCacheManifestTransfomer());
.addTransformer(new AppCacheManifestTransformer());
ResourceHttpRequestHandler handler = getHandler("/resources/**");
List<ResourceResolver> resolvers = handler.getResourceResolvers();
@@ -158,7 +158,7 @@ public class ResourceHandlerRegistryTests {
assertThat(transformers, Matchers.hasSize(3));
assertThat(transformers.get(0), Matchers.instanceOf(CachingResourceTransformer.class));
assertThat(transformers.get(1), Matchers.instanceOf(CssLinkResourceTransformer.class));
assertThat(transformers.get(2), Matchers.instanceOf(AppCacheManifestTransfomer.class));
assertThat(transformers.get(2), Matchers.instanceOf(AppCacheManifestTransformer.class));
}
@Test
@@ -167,7 +167,7 @@ public class ResourceHandlerRegistryTests {
VersionResourceResolver versionResolver = Mockito.mock(VersionResourceResolver.class);
PathResourceResolver pathResourceResolver = new PathResourceResolver();
CachingResourceTransformer cachingTransformer = Mockito.mock(CachingResourceTransformer.class);
AppCacheManifestTransfomer appCacheTransformer = Mockito.mock(AppCacheManifestTransfomer.class);
AppCacheManifestTransformer appCacheTransformer = Mockito.mock(AppCacheManifestTransformer.class);
CssLinkResourceTransformer cssLinkTransformer = new CssLinkResourceTransformer();
this.registration.setCachePeriod(3600)

View File

@@ -34,13 +34,13 @@ import static org.mockito.BDDMockito.*;
/**
* Unit tests for
* {@link AppCacheManifestTransfomer}.
* {@link AppCacheManifestTransformer}.
*
* @author Brian Clozel
*/
public class AppCacheManifestTransformerTests {
private AppCacheManifestTransfomer transformer;
private AppCacheManifestTransformer transformer;
private ResourceTransformerChain chain;
@@ -48,7 +48,7 @@ public class AppCacheManifestTransformerTests {
@Before
public void setup() {
this.transformer = new AppCacheManifestTransfomer();
this.transformer = new AppCacheManifestTransformer();
this.chain = mock(ResourceTransformerChain.class);
this.request = mock(HttpServletRequest.class);
}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/">
<mvc:resource-chain auto-registration="false">
<mvc:resolvers>
<mvc:version-resolver>
<mvc:fixed-version-strategy version="abc" patterns="/**/*.js"/>
<mvc:version-strategy patterns="/**">
<bean class="org.springframework.web.servlet.resource.ContentVersionStrategy" />
</mvc:version-strategy>
</mvc:version-resolver>
<ref bean="gzipResourceResolver"/>
<bean class="org.springframework.web.servlet.resource.PathResourceResolver"/>
</mvc:resolvers>
<mvc:transformers>
<bean class="org.springframework.web.servlet.resource.CachingResourceTransformer">
<constructor-arg name="cache" ref="resourceCache" />
</bean>
<bean class="org.springframework.web.servlet.resource.AppCacheManifestTransformer"/>
</mvc:transformers>
</mvc:resource-chain>
</mvc:resources>
<bean id="gzipResourceResolver" class="org.springframework.web.servlet.resource.GzipResourceResolver" />
<bean id="resourceCache" class="org.springframework.cache.concurrent.ConcurrentMapCache">
<constructor-arg name="name" value="resourceCache"/>
</bean>
</beans>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/">
<mvc:resource-chain>
<mvc:resource-cache cache="resourceCache"/>
<mvc:resolvers>
<mvc:version-resolver>
<mvc:fixed-version-strategy version="abc" patterns="/**/*.js"/>
<mvc:content-version-strategy patterns="/**"/>
</mvc:version-resolver>
</mvc:resolvers>
<mvc:transformers>
<bean class="org.springframework.web.servlet.resource.AppCacheManifestTransformer"/>
</mvc:transformers>
</mvc:resource-chain>
</mvc:resources>
<bean id="resourceCache" class="org.springframework.web.servlet.config.MvcNamespaceTests$TestResourceCache">
<constructor-arg name="name" value="resourceCache"/>
</bean>
</beans>

View File

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/">
<mvc:resolvers>
<bean class="org.springframework.web.servlet.resource.PathResourceResolver"/>
<ref bean="gzipResourceResolver" />
</mvc:resolvers>
<mvc:transformers>
<ref bean="cachingResourceTransformer" />
<bean class="org.springframework.web.servlet.resource.CssLinkResourceTransformer" />
</mvc:transformers>
</mvc:resources>
<bean id="gzipResourceResolver" class="org.springframework.web.servlet.resource.GzipResourceResolver" />
<bean id="resourceCache" class="org.springframework.cache.concurrent.ConcurrentMapCache">
<constructor-arg name="name" value="resourceCache"/>
</bean>
<bean id="cachingResourceTransformer" class="org.springframework.web.servlet.resource.CachingResourceTransformer">
<constructor-arg name="cache" ref="resourceCache" />
</bean>
</beans>