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();