From cc7e8f55583b91c29dc1375160d7f7d2403590ff Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Thu, 19 Jun 2014 11:40:14 +0200 Subject: [PATCH] Support Java and MVC namespace view resolution config This commit improves and completes the initial MVC namespace view resolution implementation. ContentNegotiatingViewResolver registration is now also supported. Java Config view resolution support has been added. FreeMarker, Velocity and Tiles view configurers are registered depending on the classpath thanks to an ImportSelector. For both, a default configuration is provided and documented. Issue: SPR-7093 --- .../samples/context/JavaConfigTests.java | 26 +- .../servlet/samples/spr/EncodedUriTests.java | 6 + .../web/servlet/ViewResolverComposite.java | 100 +++++++ .../servlet/config/MvcNamespaceHandler.java | 6 +- .../ViewResolutionBeanDefinitionParser.java | 252 ++++++++++++++++++ .../ViewResolversBeanDefinitionParser.java | 158 ----------- .../annotation/BeanNameRegistration.java | 34 +++ .../ContentNegotiatingRegistration.java | 71 +++++ .../DelegatingWebMvcConfiguration.java | 5 + .../config/annotation/EnableWebMvc.java | 5 +- ...eMarkerConfigurerConfigurationSupport.java | 59 ++++ .../annotation/FreeMarkerRegistration.java | 94 +++++++ .../config/annotation/JspRegistration.java | 40 +++ .../TilesConfigurerConfigurationSupport.java | 59 ++++ .../config/annotation/TilesRegistration.java | 99 +++++++ ...elocityConfigurerConfigurationSupport.java | 59 ++++ .../annotation/VelocityRegistration.java | 86 ++++++ .../ViewConfigurationsImportSelector.java | 58 ++++ .../ViewResolutionRegistration.java | 48 ++++ .../annotation/ViewResolutionRegistry.java | 164 ++++++++++++ .../WebMvcConfigurationSupport.java | 66 ++++- .../config/annotation/WebMvcConfigurer.java | 5 + .../annotation/WebMvcConfigurerAdapter.java | 8 + .../annotation/WebMvcConfigurerComposite.java | 7 + .../web/servlet/config/spring-mvc-4.0.xsd | 28 -- .../web/servlet/config/spring-mvc-4.1.xsd | 216 +++++++++++++++ .../web/servlet/config/MvcNamespaceTests.java | 100 +++++-- .../ViewResolutionRegistryTests.java | 205 ++++++++++++++ ...MvcConfigurationSupportExtensionTests.java | 38 ++- .../WebMvcConfigurationSupportTests.java | 13 +- ...ontent-negotiating-with-default-values.xml | 26 ++ .../config/mvc-config-content-negotiating.xml | 53 ++++ .../config/mvc-config-view-resolution.xml | 26 ++ .../config/mvc-config-view-resolvers.xml | 16 -- 34 files changed, 1984 insertions(+), 252 deletions(-) create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/ViewResolverComposite.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewResolutionBeanDefinitionParser.java delete mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewResolversBeanDefinitionParser.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/BeanNameRegistration.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiatingRegistration.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/FreeMarkerConfigurerConfigurationSupport.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/FreeMarkerRegistration.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/JspRegistration.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/TilesConfigurerConfigurationSupport.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/TilesRegistration.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/VelocityConfigurerConfigurationSupport.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/VelocityRegistration.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewConfigurationsImportSelector.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistration.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistry.java create mode 100644 spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistryTests.java create mode 100644 spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-content-negotiating-with-default-values.xml create mode 100644 spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-content-negotiating.xml create mode 100644 spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-resolution.xml delete mode 100644 spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-resolvers.xml diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/JavaConfigTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/JavaConfigTests.java index 86144325f1d..076ce79b252 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/JavaConfigTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/JavaConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * 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. @@ -34,14 +34,7 @@ import org.springframework.test.web.servlet.samples.context.JavaConfigTests.Root import org.springframework.test.web.servlet.samples.context.JavaConfigTests.WebConfig; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; -import org.springframework.web.servlet.view.UrlBasedViewResolver; -import org.springframework.web.servlet.view.tiles3.TilesConfigurer; -import org.springframework.web.servlet.view.tiles3.TilesView; +import org.springframework.web.servlet.config.annotation.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; @@ -54,6 +47,7 @@ import static org.mockito.Mockito.*; * * @author Rossen Stoyanchev * @author Sam Brannen + * @author Sebastien Deleuze */ @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration("src/test/resources/META-INF/web-resources") @@ -130,19 +124,11 @@ public class JavaConfigTests { configurer.enable(); } - @Bean - public UrlBasedViewResolver urlBasedViewResolver() { - UrlBasedViewResolver resolver = new UrlBasedViewResolver(); - resolver.setViewClass(TilesView.class); - return resolver; + @Override + public void configureViewResolution(ViewResolutionRegistry registry) { + registry.tiles().definition("/WEB-INF/**/tiles.xml"); } - @Bean - public TilesConfigurer tilesConfigurer() { - TilesConfigurer configurer = new TilesConfigurer(); - configurer.setDefinitions(new String[] {"/WEB-INF/**/tiles.xml"}); - return configurer; - } } } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/spr/EncodedUriTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/spr/EncodedUriTests.java index b1fa6931ff6..cf3e373c8d1 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/spr/EncodedUriTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/spr/EncodedUriTests.java @@ -40,6 +40,7 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ViewResolutionRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.util.UriComponentsBuilder; @@ -94,6 +95,11 @@ public class EncodedUriTests { public HandlerMappingConfigurer myHandlerMappingConfigurer() { return new HandlerMappingConfigurer(); } + + @Override + public void configureViewResolution(ViewResolutionRegistry registry) { + registry.jsp("", ""); + } } @Controller diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/ViewResolverComposite.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/ViewResolverComposite.java new file mode 100644 index 00000000000..4f2e3241354 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/ViewResolverComposite.java @@ -0,0 +1,100 @@ +/* + * 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; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.Ordered; +import org.springframework.web.context.ServletContextAware; + +import javax.servlet.ServletContext; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +/** + * A {@link ViewResolverComposite} that delegates to a list of other {@link ViewResolver}s. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public class ViewResolverComposite implements ApplicationContextAware, ServletContextAware, ViewResolver, Ordered { + + private List viewResolvers; + + private int order = Ordered.LOWEST_PRECEDENCE; + + public void setOrder(int order) { + this.order = order; + } + + @Override + public int getOrder() { + return this.order; + } + + /** + * Set the list of view viewResolvers to delegate to. + */ + public void setViewResolvers(List viewResolvers) { + this.viewResolvers = viewResolvers; + } + + /** + * Return the list of view viewResolvers to delegate to. + */ + public List getViewResolvers() { + return Collections.unmodifiableList(viewResolvers); + } + + @Override + public View resolveViewName(String viewName, Locale locale) throws Exception { + if (viewResolvers != null) { + for (ViewResolver viewResolver : viewResolvers) { + View v = viewResolver.resolveViewName(viewName, locale); + if (v != null) { + return v; + } + } + } + + return null; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (viewResolvers != null) { + for (ViewResolver viewResolver : viewResolvers) { + if(viewResolver instanceof ApplicationContextAware) { + ((ApplicationContextAware)viewResolver).setApplicationContext(applicationContext); + } + } + } + } + + @Override + public void setServletContext(ServletContext servletContext) { + if (viewResolvers != null) { + for (ViewResolver viewResolver : viewResolvers) { + if(viewResolver instanceof ServletContextAware) { + ((ServletContextAware)viewResolver).setServletContext(servletContext); + } + } + } + } +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java index 16c48d081f3..1f7d81d2866 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * 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. @@ -28,14 +28,14 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport; */ public class MvcNamespaceHandler extends NamespaceHandlerSupport { - + @Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser()); registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser()); registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser()); - registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser()); + registerBeanDefinitionParser("view-resolution", new ViewResolutionBeanDefinitionParser()); } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewResolutionBeanDefinitionParser.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewResolutionBeanDefinitionParser.java new file mode 100644 index 00000000000..12234e13f56 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewResolutionBeanDefinitionParser.java @@ -0,0 +1,252 @@ +/* + * 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.config; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.parsing.CompositeComponentDefinition; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.springframework.web.servlet.view.BeanNameViewResolver; +import org.springframework.web.servlet.view.ContentNegotiatingViewResolver; +import org.springframework.web.servlet.view.InternalResourceViewResolver; +import org.springframework.web.servlet.ViewResolver; + +import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; +import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; +import org.springframework.web.servlet.view.tiles3.TilesConfigurer; +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 org.w3c.dom.Element; + +/** + * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a + * {@code view-resolution} element to register a set of {@link ViewResolver} definitions. + * + * @author Sivaprasad Valluru + * @author Sebastien Deleuze + * @since 4.1 + */ +public class ViewResolutionBeanDefinitionParser implements BeanDefinitionParser { + + private Object source; + + public BeanDefinition parse(Element element, ParserContext parserContext) { + + source= parserContext.extractSource(element); + CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(),source); + parserContext.pushContainingComponent(compDefinition); + + List viewResolverElements = + DomUtils.getChildElementsByTagName(element, new String[] { "jsp", "tiles", "bean-name", "freemarker", "velocity", "bean", "ref" }); + ManagedList viewResolvers = new ManagedList(); + viewResolvers.setSource(parserContext.extractSource(element)); + int order = 0; + + for (Element viewResolverElement : viewResolverElements) { + if ("jsp".equals(viewResolverElement.getLocalName())) { + viewResolvers.add(registerInternalResourceViewResolverBean(viewResolverElement, parserContext, order)); + } + if("bean-name".equals(viewResolverElement.getLocalName())){ + viewResolvers.add(registerBeanNameViewResolverBean(viewResolverElement, parserContext, order)); + } + if ("tiles".equals(viewResolverElement.getLocalName())) { + viewResolvers.add(registerTilesViewResolverBean(viewResolverElement, parserContext, order)); + registerTilesConfigurerBean(viewResolverElement, parserContext); + } + if("freemarker".equals(viewResolverElement.getLocalName())){ + viewResolvers.add(registerFreemarkerViewResolverBean(viewResolverElement, parserContext, order)); + registerFreemarkerConfigurerBean(viewResolverElement, parserContext); + } + if("velocity".equals(viewResolverElement.getLocalName())){ + viewResolvers.add(registerVelocityViewResolverBean(viewResolverElement, parserContext, order)); + registerVelocityConfigurerBean(viewResolverElement, parserContext); + } + if("bean".equals(viewResolverElement.getLocalName()) || "ref".equals(viewResolverElement.getLocalName())){ + viewResolvers.add(parserContext.getDelegate().parsePropertySubElement(viewResolverElement, null)); + } + + order++; + } + viewResolverElements = DomUtils.getChildElementsByTagName(element, new String[] { "content-negotiating" }); + if(!viewResolverElements.isEmpty()) { + registerContentNegotiatingViewResolverBean(viewResolverElements.get(0), parserContext, viewResolvers); + } + + parserContext.popAndRegisterContainingComponent(); + return null; + } + + + private BeanDefinition registerBean(Map propertyMap,Class beanClass, ParserContext parserContext ){ + RootBeanDefinition beanDef = new RootBeanDefinition(beanClass); + beanDef.setSource(source); + beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + + for(String propertyName:propertyMap.keySet()){ + beanDef.getPropertyValues().add(propertyName, propertyMap.get(propertyName)); + } + String beanName = parserContext.getReaderContext().generateBeanName(beanDef); + parserContext.getRegistry().registerBeanDefinition(beanName, beanDef); + parserContext.registerComponent(new BeanComponentDefinition(beanDef, beanName)); + return beanDef; + } + + private BeanDefinition registerContentNegotiatingViewResolverBean(Element viewResolverElement, ParserContext parserContext, ManagedList viewResolvers) { + Map propertyMap = new HashMap(); + ManagedList defaultViewBeanDefinitions = new ManagedList(); + List defaultViewElements = + DomUtils.getChildElementsByTagName(viewResolverElement, new String[] { "default-views" }); + if(!defaultViewElements.isEmpty()) { + for(Element beanElem : DomUtils.getChildElementsByTagName(defaultViewElements.get(0), "bean", "ref")) { + defaultViewBeanDefinitions.add(parserContext.getDelegate().parsePropertySubElement(beanElem, null)); + } + } + if(viewResolverElement.hasAttribute("use-not-acceptable")) { + propertyMap.put("useNotAcceptableStatusCode", viewResolverElement.getAttribute("use-not-acceptable")); + } + if(viewResolverElement.hasAttribute("manager")) { + propertyMap.put("contentNegotiationManager", new RuntimeBeanReference(viewResolverElement.getAttribute("manager"))); + } else { + propertyMap.put("contentNegotiationManager", new RuntimeBeanReference("mvcContentNegotiationManager")); + } + if(viewResolvers != null && !viewResolvers.isEmpty()) { + propertyMap.put("viewResolvers", viewResolvers); + } + if(defaultViewBeanDefinitions != null && !defaultViewBeanDefinitions.isEmpty()) { + propertyMap.put("defaultViews", defaultViewBeanDefinitions); + } + return registerBean(propertyMap, ContentNegotiatingViewResolver.class, parserContext); + } + + private BeanDefinition registerFreemarkerConfigurerBean(Element viewResolverElement, ParserContext parserContext) { + Map propertyMap = new HashMap(); + if(viewResolverElement.hasAttribute("template-loader-paths")) { + String[] paths = StringUtils.commaDelimitedListToStringArray(viewResolverElement.getAttribute("template-loader-paths")); + propertyMap.put("templateLoaderPaths", paths); + } else { + propertyMap.put("templateLoaderPaths", new String[]{"/WEB-INF/"}); + } + return registerBean(propertyMap, FreeMarkerConfigurer.class, parserContext); + } + + private BeanDefinition registerFreemarkerViewResolverBean(Element viewResolverElement, ParserContext parserContext, int order) { + Map propertyMap = new HashMap(); + if(viewResolverElement.hasAttribute("prefix")) { + propertyMap.put("prefix", viewResolverElement.getAttribute("prefix")); + } + if(viewResolverElement.hasAttribute("suffix")) { + propertyMap.put("suffix", viewResolverElement.getAttribute("suffix")); + } + else { + propertyMap.put("suffix", ".ftl"); + } + if(viewResolverElement.hasAttribute("cache")) { + propertyMap.put("cache", viewResolverElement.getAttribute("cache")); + } + propertyMap.put("order", order); + return registerBean(propertyMap, FreeMarkerViewResolver.class, parserContext); + } + + private BeanDefinition registerVelocityConfigurerBean(Element viewResolverElement, ParserContext parserContext) { + String resourceLoaderPath = viewResolverElement.getAttribute("resource-loader-path"); + Map propertyMap = new HashMap(); + if(viewResolverElement.hasAttribute("resource-loader-path")) { + propertyMap.put("resourceLoaderPath", resourceLoaderPath); + } else { + propertyMap.put("resourceLoaderPath", "/WEB-INF/"); + } + return registerBean(propertyMap, VelocityConfigurer.class, parserContext); + } + + private BeanDefinition registerVelocityViewResolverBean(Element viewResolverElement, ParserContext parserContext, int order) { + Map propertyMap = new HashMap(); + if(viewResolverElement.hasAttribute("prefix")) { + propertyMap.put("prefix", viewResolverElement.getAttribute("prefix")); + } + if(viewResolverElement.hasAttribute("suffix")) { + propertyMap.put("suffix", viewResolverElement.getAttribute("suffix")); + } + else { + propertyMap.put("suffix", ".vm"); + } + if(viewResolverElement.hasAttribute("cache")) { + propertyMap.put("cache", viewResolverElement.getAttribute("cache")); + } + propertyMap.put("order", order); + return registerBean(propertyMap, VelocityViewResolver.class, parserContext); + } + + private BeanDefinition registerBeanNameViewResolverBean(Element viewResolverElement, ParserContext parserContext, int order) { + Map propertyMap = new HashMap(); + propertyMap.put("order", order); + return registerBean(propertyMap, BeanNameViewResolver.class, parserContext); + } + + private BeanDefinition registerTilesConfigurerBean(Element viewResolverElement, ParserContext parserContext) { + Map propertyMap = new HashMap(); + if(viewResolverElement.hasAttribute("definitions")) { + String[] definitions = StringUtils.commaDelimitedListToStringArray(viewResolverElement.getAttribute("definitions")); + propertyMap.put("definitions", definitions); + } + if(viewResolverElement.hasAttribute("check-refresh")) { + propertyMap.put("checkRefresh", viewResolverElement.getAttribute("check-refresh")); + } + return registerBean(propertyMap, TilesConfigurer.class, parserContext); + } + + private BeanDefinition registerTilesViewResolverBean(Element viewResolverElement, ParserContext parserContext, int order) { + Map propertyMap = new HashMap(); + if(viewResolverElement.hasAttribute("prefix")) { + propertyMap.put("prefix", viewResolverElement.getAttribute("prefix")); + } + if(viewResolverElement.hasAttribute("suffix")) { + propertyMap.put("suffix", viewResolverElement.getAttribute("suffix")); + } + propertyMap.put("order", order); + return registerBean(propertyMap, TilesViewResolver.class, parserContext); + } + + private BeanDefinition registerInternalResourceViewResolverBean(Element viewResolverElement, ParserContext parserContext, int order) { + Map propertyMap = new HashMap(); + if(viewResolverElement.hasAttribute("prefix")) { + propertyMap.put("prefix", viewResolverElement.getAttribute("prefix")); + } + else { + propertyMap.put("prefix", "/WEB-INF/"); + } + if(viewResolverElement.hasAttribute("suffix")) { + propertyMap.put("suffix", viewResolverElement.getAttribute("suffix")); + } + else { + propertyMap.put("suffix", ".jsp"); + } + propertyMap.put("order", order); + return registerBean(propertyMap, InternalResourceViewResolver.class, parserContext); + } + +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewResolversBeanDefinitionParser.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewResolversBeanDefinitionParser.java deleted file mode 100644 index 152090acd5c..00000000000 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewResolversBeanDefinitionParser.java +++ /dev/null @@ -1,158 +0,0 @@ -package org.springframework.web.servlet.config; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.parsing.BeanComponentDefinition; -import org.springframework.beans.factory.parsing.CompositeComponentDefinition; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.factory.xml.BeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.util.xml.DomUtils; -import org.springframework.web.servlet.view.BeanNameViewResolver; -import org.springframework.web.servlet.view.InternalResourceViewResolver; - -import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; -import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; -import org.springframework.web.servlet.view.tiles3.TilesConfigurer; -import org.springframework.web.servlet.view.tiles3.TilesView; -import org.springframework.web.servlet.view.tiles3.TilesViewResolver; -import org.w3c.dom.Element; - - -public class ViewResolversBeanDefinitionParser implements BeanDefinitionParser { - - private static final String INTERNAL_VIEW_RESOLVER_BEAN_NAME = - "org.springframework.web.servlet.view.InternalResourceViewResolver"; - private static final String TILES3_VIEW_RESOLVER_BEAN_NAME = - "org.springframework.web.servlet.view.tiles3.TilesViewResolver"; - private static final String TILES3_CONFIGURER_BEAN_NAME = - "org.springframework.web.servlet.view.tiles3.TilesConfigurer"; - private static final String BEANNAME_VIEW_RESOLVER_BEAN_NAME = - "org.springframework.web.servlet.view.BeanNameViewResolver"; - private static final String FREEMARKER_CONFIGURER_BEAN_NAME = - "org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"; - private static final String FREEMARKER_VIEW_RESOLVER_BEAN_NAME = - "org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"; - - private ParserContext parserContext; - private Object source; - - public BeanDefinition parse(Element element, ParserContext parserContext) { - - this.parserContext=parserContext; - - source= parserContext.extractSource(element); - CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(),source); - parserContext.pushContainingComponent(compDefinition); - - - - List viewResolverElements = //DomUtils.getChildElements(element); - DomUtils.getChildElementsByTagName(element, new String[] { "jsp", "tiles","bean-name","freemarker" }); - for (Element viewResolverElement : viewResolverElements) { - - if ("jsp".equals(viewResolverElement.getLocalName())) { - registerInternalResourceViewResolverBean(parserContext,viewResolverElement); - System.out.println("Registered Internalresource view resolver"); - } - - if("bean-name".equals(viewResolverElement.getLocalName())){ - registerBeanNameViewResolverBean(parserContext,viewResolverElement); - System.out.println("Registered BeanNameViewResolverBean view resolver"); - } - if ("tiles".equals(viewResolverElement.getLocalName())) { - registerTilesViewResolverBean(parserContext,viewResolverElement); - registerTilesConfigurerBean(parserContext,viewResolverElement); - } - if("freemarker".equals(viewResolverElement.getLocalName())){ - registerFreemarkerViewResolverBean(parserContext,viewResolverElement); - registerFreemarkerConfigurerBean(parserContext,viewResolverElement); - } - - } - - // MvcNamespaceUtils.registerDefaultComponents(parserContext, source); - parserContext.popAndRegisterContainingComponent(); - return null; - - - } - - private void registerBean(String beanName,Map propertyMap,Class beanClass ){ - RootBeanDefinition beanDef = new RootBeanDefinition(beanClass); - beanDef.setSource(source); - beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - - for(String propertyName:propertyMap.keySet()){ - beanDef.getPropertyValues().add(propertyName, propertyMap.get(propertyName)); - } - parserContext.getRegistry().registerBeanDefinition(beanName, beanDef); - parserContext.registerComponent(new BeanComponentDefinition(beanDef, beanName)); - - - } - - private void registerFreemarkerConfigurerBean(ParserContext parserContext, Element viewResolverElement) { - String templateLoaderPath=viewResolverElement.getAttribute("templateLoaderPath"); - Map propertyMap= new HashMap(); - propertyMap.put("templateLoaderPath", templateLoaderPath); - - registerBean(FREEMARKER_CONFIGURER_BEAN_NAME, propertyMap, FreeMarkerConfigurer.class); - - } - - private void registerFreemarkerViewResolverBean(ParserContext parserContext, Element viewResolverElement) { - if (!parserContext.getRegistry().containsBeanDefinition(FREEMARKER_VIEW_RESOLVER_BEAN_NAME)) { - - Map propertyMap= new HashMap(); - propertyMap.put("prefix", viewResolverElement.getAttribute("prefix")); - propertyMap.put("suffix", viewResolverElement.getAttribute("suffix")); - propertyMap.put("order", 4); - registerBean(FREEMARKER_VIEW_RESOLVER_BEAN_NAME, propertyMap, FreeMarkerViewResolver.class); - } - } - - private void registerBeanNameViewResolverBean(ParserContext parserContext, Element viewResolverElement) { - if (!parserContext.getRegistry().containsBeanDefinition(BEANNAME_VIEW_RESOLVER_BEAN_NAME)) { - Map propertyMap= new HashMap(); - propertyMap.put("order", 3); - registerBean(BEANNAME_VIEW_RESOLVER_BEAN_NAME, propertyMap, BeanNameViewResolver.class); - System.out.println("Registered BeanNameViewResolverBean view resolver"); - } - } - - private void registerTilesConfigurerBean(ParserContext parserContext,Element viewResolverElement) { - if (!parserContext.getRegistry().containsBeanDefinition(TILES3_CONFIGURER_BEAN_NAME)) { - Map propertyMap= new HashMap(); - propertyMap.put("definitions", viewResolverElement.getAttribute("definitions")); - registerBean(TILES3_CONFIGURER_BEAN_NAME, propertyMap, TilesConfigurer.class); - } - } - - private void registerTilesViewResolverBean(ParserContext parserContext, Element viewResolverElement) { - - if (!parserContext.getRegistry().containsBeanDefinition(TILES3_VIEW_RESOLVER_BEAN_NAME)) { - Map propertyMap= new HashMap(); - propertyMap.put("viewClass", TilesView.class); - propertyMap.put("order", 1); - registerBean(TILES3_VIEW_RESOLVER_BEAN_NAME, propertyMap, TilesViewResolver.class); - } - } - private void registerInternalResourceViewResolverBean(ParserContext parserContext, Element viewResolverElement) { - if (!parserContext.getRegistry().containsBeanDefinition(INTERNAL_VIEW_RESOLVER_BEAN_NAME)) { - Map propertyMap= new HashMap(); - propertyMap.put("prefix", viewResolverElement.getAttribute("prefix")); - propertyMap.put("suffix", viewResolverElement.getAttribute("suffix")); - propertyMap.put("order", 2); - registerBean(INTERNAL_VIEW_RESOLVER_BEAN_NAME, propertyMap, InternalResourceViewResolver.class); - } - - - } - - - -} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/BeanNameRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/BeanNameRegistration.java new file mode 100644 index 00000000000..9aff9fe1ee3 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/BeanNameRegistration.java @@ -0,0 +1,34 @@ +/* + * 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.config.annotation; + +import org.springframework.web.servlet.view.BeanNameViewResolver; + +/** + * Encapsulates information required to create a + * {@link org.springframework.web.servlet.view.BeanNameViewResolver} bean. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public class BeanNameRegistration extends ViewResolutionRegistration { + + public BeanNameRegistration(ViewResolutionRegistry registry) { + super(registry, new BeanNameViewResolver()); + } + +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiatingRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiatingRegistration.java new file mode 100644 index 00000000000..1bc875eb2a5 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiatingRegistration.java @@ -0,0 +1,71 @@ +/* + * 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.config.annotation; + +import org.springframework.web.servlet.View; +import org.springframework.web.servlet.view.ContentNegotiatingViewResolver; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Encapsulates information required to create a {@link ContentNegotiatingViewResolver} bean. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public class ContentNegotiatingRegistration extends ViewResolutionRegistration { + + private List defaultViews; + + public ContentNegotiatingRegistration(ViewResolutionRegistry registry) { + super(registry, new ContentNegotiatingViewResolver()); + } + + /** + * Indicate whether a {@link javax.servlet.http.HttpServletResponse#SC_NOT_ACCEPTABLE 406 Not Acceptable} + * status code should be returned if no suitable view can be found. + * + * @see ContentNegotiatingViewResolver#setUseNotAcceptableStatusCode(boolean) + */ + public ContentNegotiatingRegistration useNotAcceptable(boolean useNotAcceptable) { + this.viewResolver.setUseNotAcceptableStatusCode(useNotAcceptable); + return this; + } + + /** + * + * Set the default views to use when a more specific view can not be obtained + * from the {@link org.springframework.web.servlet.ViewResolver} chain. + * + * @see ContentNegotiatingViewResolver#setDefaultViews(java.util.List) + */ + public ContentNegotiatingRegistration defaultViews(View... defaultViews) { + if(this.defaultViews == null) { + this.defaultViews = new ArrayList(); + } + this.defaultViews.addAll(Arrays.asList(defaultViews)); + return this; + } + + @Override + protected ContentNegotiatingViewResolver getViewResolver() { + this.viewResolver.setDefaultViews(this.defaultViews); + return super.getViewResolver(); + } +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java index fef35bcec8d..c8fce889af3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java @@ -75,6 +75,11 @@ public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { this.configurers.addViewControllers(registry); } + @Override + protected void configureViewResolution(ViewResolutionRegistry registry) { + this.configurers.configureViewResolution(registry); + } + @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { this.configurers.addResourceHandlers(registry); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java index 178569b4c4c..fc585fd72e5 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * 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 @@ -84,11 +84,12 @@ import org.springframework.context.annotation.Import; * * @author Dave Syer * @author Rossen Stoyanchev + * @author Sebastien Deleuze * @since 3.1 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented -@Import(DelegatingWebMvcConfiguration.class) +@Import({DelegatingWebMvcConfiguration.class, ViewConfigurationsImportSelector.class}) public @interface EnableWebMvc { } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/FreeMarkerConfigurerConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/FreeMarkerConfigurerConfigurationSupport.java new file mode 100644 index 00000000000..bb5bce33146 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/FreeMarkerConfigurerConfigurationSupport.java @@ -0,0 +1,59 @@ +/* + * 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.config.annotation; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; + +import java.util.List; + +/** + * This class creates a FreeMarkerConfigurer bean. + * It is typically imported by adding {@link EnableWebMvc @EnableWebMvc} to an + * application {@link Configuration @Configuration} class when FreeMarker is + * in the classpath. + * + * @author Sebastien Deleuze + * @since 4.1 + * @see org.springframework.web.servlet.config.annotation.ViewConfigurationsImportSelector + */ +@Configuration +public class FreeMarkerConfigurerConfigurationSupport { + + private List webMvcConfigurationSupports; + + @Autowired(required = false) + public void setWebMvcConfigurationSupports(List webMvcConfigurationSupports) { + this.webMvcConfigurationSupports = webMvcConfigurationSupports; + } + + @Bean + public FreeMarkerConfigurer freeMarkerConfigurer() { + FreeMarkerConfigurer configurer = null; + if(webMvcConfigurationSupports != null) { + for(WebMvcConfigurationSupport configurationSupport : webMvcConfigurationSupports) { + configurer = configurationSupport.getViewResolutionRegistry().getFreeMarkerConfigurer(); + if(configurer != null) { + break; + } + } + } + return configurer; + } +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/FreeMarkerRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/FreeMarkerRegistration.java new file mode 100644 index 00000000000..65cdfda8809 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/FreeMarkerRegistration.java @@ -0,0 +1,94 @@ +/* + * 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.config.annotation; + +import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; +import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; + +import java.util.Arrays; +import java.util.List; + +/** + * Encapsulates information required to create a + * {@link org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver} and a + * {@link org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer} beans. + * Default configuration is "" prefix, ".ftl" suffix and "/WEB-INF/" templateLoaderPath. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public class FreeMarkerRegistration extends ViewResolutionRegistration { + + private final FreeMarkerConfigurer configurer; + private List templateLoaderPaths; + + public FreeMarkerRegistration(ViewResolutionRegistry registry) { + super(registry, new FreeMarkerViewResolver()); + this.configurer = new FreeMarkerConfigurer(); + this.prefix(""); + this.suffix(".ftl"); + this.templateLoaderPath("/WEB-INF/"); + } + + /** + * Set the prefix that gets prepended to view names when building a URL. + * + * @see org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver#setPrefix(String) + */ + public FreeMarkerRegistration prefix(String prefix) { + this.viewResolver.setPrefix(prefix); + return this; + } + + /** + * Set the suffix that gets appended to view names when building a URL. + * + * @see org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver#setSuffix(String) + */ + public FreeMarkerRegistration suffix(String suffix) { + this.viewResolver.setSuffix(suffix); + return this; + } + + /** + * Enable or disable caching. + * + * @see org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver#setCache(boolean) + */ + public FreeMarkerRegistration cache(boolean cache) { + this.viewResolver.setCache(cache); + return this; + } + + /** + * Set a List of {@code TemplateLoader}s that will be used to search + * for templates. + * + * @see org.springframework.ui.freemarker.FreeMarkerConfigurationFactory#setTemplateLoaderPaths(String...) + */ + public FreeMarkerRegistration templateLoaderPath(String... templateLoaderPath) { + this.templateLoaderPaths= Arrays.asList(templateLoaderPath); + return this; + } + + protected FreeMarkerConfigurer getConfigurer() { + if(this.templateLoaderPaths != null && !this.templateLoaderPaths.isEmpty()) { + this.configurer.setTemplateLoaderPaths(this.templateLoaderPaths.toArray(new String[0])); + } + return this.configurer; + } +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/JspRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/JspRegistration.java new file mode 100644 index 00000000000..bfa401320f1 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/JspRegistration.java @@ -0,0 +1,40 @@ +/* + * 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.config.annotation; + +import org.springframework.web.servlet.view.InternalResourceViewResolver; + +/** + * Encapsulates information required to create an {@link InternalResourceViewResolver} bean. + * Default configuration is "/WEB-INF/" prefix and ".jsp" suffix. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public class JspRegistration extends ViewResolutionRegistration { + + public JspRegistration(ViewResolutionRegistry registry) { + this(registry, "/WEB-INF/", ".jsp"); + } + + public JspRegistration(ViewResolutionRegistry registry, String prefix, String suffix) { + super(registry, new InternalResourceViewResolver()); + this.viewResolver.setPrefix(prefix); + this.viewResolver.setSuffix(suffix); + } + +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/TilesConfigurerConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/TilesConfigurerConfigurationSupport.java new file mode 100644 index 00000000000..c41627fd5bd --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/TilesConfigurerConfigurationSupport.java @@ -0,0 +1,59 @@ +/* + * 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.config.annotation; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.view.tiles3.TilesConfigurer; + +import java.util.List; + +/** + * This class creates a TilesConfigurer bean. + * It is typically imported by adding {@link EnableWebMvc @EnableWebMvc} to an + * application {@link Configuration @Configuration} class when Tiles 3 is + * in the classpath. + * + * @author Sebastien Deleuze + * @since 4.1 + * @see org.springframework.web.servlet.config.annotation.ViewConfigurationsImportSelector + */ +@Configuration +public class TilesConfigurerConfigurationSupport { + + private List webMvcConfigurationSupports; + + @Autowired(required = false) + public void setWebMvcConfigurationSupports(List webMvcConfigurationSupports) { + this.webMvcConfigurationSupports = webMvcConfigurationSupports; + } + + @Bean + public TilesConfigurer tilesConfigurer() { + TilesConfigurer configurer = null; + if(webMvcConfigurationSupports != null) { + for(WebMvcConfigurationSupport configurationSupport : webMvcConfigurationSupports) { + configurer = configurationSupport.getViewResolutionRegistry().getTilesConfigurer(); + if(configurer != null) { + break; + } + } + } + return configurer; + } +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/TilesRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/TilesRegistration.java new file mode 100644 index 00000000000..718ee4e260d --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/TilesRegistration.java @@ -0,0 +1,99 @@ +/* + * 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.config.annotation; + +import org.springframework.web.servlet.view.tiles3.TilesConfigurer; +import org.springframework.web.servlet.view.tiles3.TilesViewResolver; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Encapsulates information required to create a + * {@link org.springframework.web.servlet.view.tiles3.TilesViewResolver} and a + * {@link org.springframework.web.servlet.view.tiles3.TilesConfigurer} beans. + * + * Default definition is "/WEB-INF/tiles.xml" and no Tiles definition check refresh. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public class TilesRegistration extends ViewResolutionRegistration { + + private List definitions; + private Boolean checkRefresh; + + public TilesRegistration(ViewResolutionRegistry registry) { + super(registry, new TilesViewResolver()); + } + + /** + * Set the prefix that gets prepended to view names when building a URL. + * + * @see TilesViewResolver#setPrefix(String) + */ + public TilesRegistration prefix(String prefix) { + this.viewResolver.setPrefix(prefix); + return this; + } + + /** + * Set the suffix that gets appended to view names when building a URL. + * + * @see TilesViewResolver#setSuffix(String) + */ + public TilesRegistration suffix(String suffix) { + this.viewResolver.setSuffix(suffix); + return this; + } + + /** + * Set the Tiles definitions, i.e. a single value or a list of files containing the definitions. + * + * @see TilesConfigurer#setDefinitions(String...) + */ + public TilesRegistration definition(String... definitions) { + if(this.definitions == null) { + this.definitions = new ArrayList(); + } + this.definitions.addAll(Arrays.asList(definitions)); + return this; + } + + /** + * Set whether to check Tiles definition files for a refresh at runtime. + * + * @see TilesConfigurer#setCheckRefresh(boolean) + */ + public TilesRegistration checkRefresh(boolean checkRefresh) { + this.checkRefresh = checkRefresh; + return this; + } + + protected TilesConfigurer getTilesConfigurer() { + TilesConfigurer tilesConfigurer = new TilesConfigurer(); + if(this.definitions != null && !this.definitions.isEmpty()) { + tilesConfigurer.setDefinitions(this.definitions.toArray(new String[0])); + } + if(this.checkRefresh != null) { + tilesConfigurer.setCheckRefresh(this.checkRefresh); + } + return tilesConfigurer; + } + +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/VelocityConfigurerConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/VelocityConfigurerConfigurationSupport.java new file mode 100644 index 00000000000..dee57694a44 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/VelocityConfigurerConfigurationSupport.java @@ -0,0 +1,59 @@ +/* + * 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.config.annotation; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.view.velocity.VelocityConfigurer; + +import java.util.List; + +/** + * This class creates the VelocityConfigurer bean. + * It is typically imported by adding {@link EnableWebMvc @EnableWebMvc} to an + * application {@link Configuration @Configuration} class when Velocity in the classpath. + * + * @author Sebastien Deleuze + * @since 4.1 + * @see org.springframework.web.servlet.config.annotation.ViewConfigurationsImportSelector + */ +@Configuration +public class VelocityConfigurerConfigurationSupport { + + private List webMvcConfigurationSupports; + + @Autowired(required = false) + public void setWebMvcConfigurationSupports(List webMvcConfigurationSupports) { + this.webMvcConfigurationSupports = webMvcConfigurationSupports; + } + + @Bean + public VelocityConfigurer velocityConfigurer() { + VelocityConfigurer configurer = null; + if(webMvcConfigurationSupports != null) { + for(WebMvcConfigurationSupport configurationSupport : webMvcConfigurationSupports) { + configurer = configurationSupport.getViewResolutionRegistry().getVelocityConfigurer(); + if(configurer != null) { + break; + } + } + } + return configurer; + } + +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/VelocityRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/VelocityRegistration.java new file mode 100644 index 00000000000..bbf4e758435 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/VelocityRegistration.java @@ -0,0 +1,86 @@ +/* + * 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.config.annotation; + +import org.springframework.web.servlet.view.velocity.VelocityConfigurer; +import org.springframework.web.servlet.view.velocity.VelocityViewResolver; + +/** + * Encapsulates information required to create a + * {@link org.springframework.web.servlet.view.velocity.VelocityViewResolver} and a + * {@link org.springframework.web.servlet.view.velocity.VelocityConfigurer beans}. + * Default configuration is "" prefix, ".vm" suffix and "/WEB-INF/" resourceLoaderPath. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public class VelocityRegistration extends ViewResolutionRegistration { + + private final VelocityConfigurer configurer; + + public VelocityRegistration(ViewResolutionRegistry registry) { + super(registry, new VelocityViewResolver()); + this.configurer = new VelocityConfigurer(); + this.prefix(""); + this.suffix(".vm"); + this.resourceLoaderPath("/WEB-INF/"); + } + + /** + * Set the Velocity resource loader path via a Spring resource location. + * + * @see org.springframework.web.servlet.view.velocity.VelocityConfigurer#setResourceLoaderPath(String) + */ + public VelocityRegistration resourceLoaderPath(String resourceLoaderPath) { + this.configurer.setResourceLoaderPath(resourceLoaderPath); + return this; + } + + /** + * Set the prefix that gets prepended to view names when building a URL. + * + * @see org.springframework.web.servlet.view.velocity.VelocityViewResolver#setPrefix(String) + */ + public VelocityRegistration prefix(String prefix) { + this.viewResolver.setPrefix(prefix); + return this; + } + + /** + * Set the suffix that gets appended to view names when building a URL. + * + * @see org.springframework.web.servlet.view.velocity.VelocityViewResolver#setSuffix(String) + */ + public VelocityRegistration suffix(String suffix) { + this.viewResolver.setSuffix(suffix); + return this; + } + + /** + * Enable or disable caching. + * + * @see org.springframework.web.servlet.view.velocity.VelocityViewResolver#setCache(boolean) + */ + public VelocityRegistration cache(boolean cache) { + this.viewResolver.setCache(cache); + return this; + } + + protected VelocityConfigurer getConfigurer() { + return this.configurer; + } +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewConfigurationsImportSelector.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewConfigurationsImportSelector.java new file mode 100644 index 00000000000..7d801a9e3c7 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewConfigurationsImportSelector.java @@ -0,0 +1,58 @@ +/* + * 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.config.annotation; + +import org.springframework.context.annotation.ImportSelector; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.util.ClassUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class imports @{@link org.springframework.context.annotation.Configuration} + * classes for view configurers based on a classpath criteria. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public class ViewConfigurationsImportSelector implements ImportSelector { + + private static final boolean tilesPresent = + ClassUtils.isPresent("org.apache.tiles.startup.TilesInitializer", ViewConfigurationsImportSelector.class.getClassLoader()); + + private static final boolean velocityPresent = + ClassUtils.isPresent("org.apache.velocity.app.VelocityEngine", ViewConfigurationsImportSelector.class.getClassLoader()); + + private static final boolean freeMarkerPresent = + ClassUtils.isPresent("freemarker.template.Configuration", ViewConfigurationsImportSelector.class.getClassLoader()); + + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + List configurationClasses = new ArrayList(); + if(tilesPresent) { + configurationClasses.add(TilesConfigurerConfigurationSupport.class.getName()); + } + if(velocityPresent) { + configurationClasses.add(VelocityConfigurerConfigurationSupport.class.getName()); + } + if(freeMarkerPresent) { + configurationClasses.add(FreeMarkerConfigurerConfigurationSupport.class.getName()); + } + return configurationClasses.toArray(new String[0]); + } +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistration.java new file mode 100644 index 00000000000..c15b7392479 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistration.java @@ -0,0 +1,48 @@ +/* + * 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.config.annotation; + +import org.springframework.web.servlet.ViewResolver; + +/** + * Encapsulates information required to create a view resolver. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public class ViewResolutionRegistration { + + protected final ViewResolutionRegistry registry; + + protected final T viewResolver; + + public ViewResolutionRegistration(ViewResolutionRegistry registry, T viewResolver) { + this.registry = registry; + this.viewResolver = viewResolver; + } + + public ViewResolutionRegistry and() { + return this.registry; + } + + protected T getViewResolver() { + return this.viewResolver; + } + +} + + diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistry.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistry.java new file mode 100644 index 00000000000..489bbfea423 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistry.java @@ -0,0 +1,164 @@ +/* + * 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.config.annotation; + +import org.springframework.web.servlet.View; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; +import org.springframework.web.servlet.view.tiles3.TilesConfigurer; +import org.springframework.web.servlet.view.velocity.VelocityConfigurer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Helps with configuring a list of view resolvers. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public class ViewResolutionRegistry { + + private final List> registrations = new ArrayList>(); + + /** + * Register a custom {@link ViewResolver} bean. + */ + public ViewResolutionRegistration addViewResolver(ViewResolver viewResolver) { + ViewResolutionRegistration registration = new ViewResolutionRegistration(this, viewResolver); + registrations.add(registration); + return registration; + } + + /** + * Register an {@link org.springframework.web.servlet.view.InternalResourceViewResolver} + * bean with default "/WEB-INF/" prefix and ".jsp" suffix. + */ + public JspRegistration jsp() { + JspRegistration registration = new JspRegistration(this); + addAndCheckViewResolution(registration); + return registration; + } + + /** + * Register an {@link org.springframework.web.servlet.view.InternalResourceViewResolver} + * bean with specified prefix and suffix. + */ + public JspRegistration jsp(String prefix, String suffix) { + JspRegistration registration = new JspRegistration(this, prefix, suffix); + addAndCheckViewResolution(registration); + return registration; + } + + /** + * Register a {@link org.springframework.web.servlet.view.BeanNameViewResolver} bean. + */ + public BeanNameRegistration beanName() { + BeanNameRegistration registration = new BeanNameRegistration(this); + addAndCheckViewResolution(registration); + return registration; + } + + /** + * Register a {@link org.springframework.web.servlet.view.tiles3.TilesViewResolver} and + * a {@link org.springframework.web.servlet.view.tiles3.TilesConfigurer} with + * default "/WEB-INF/tiles.xml" definition and no Tiles definition check refresh. + */ + public TilesRegistration tiles() { + TilesRegistration registration = new TilesRegistration(this); + addAndCheckViewResolution(registration); + return registration; + } + + /** + * Register a {@link org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver} + * and a {@link org.springframework.web.servlet.view.velocity.VelocityConfigurer} beans with + * default "" prefix, ".vm" suffix and "/WEB-INF/" resourceLoaderPath. + */ + public VelocityRegistration velocity() { + VelocityRegistration registration = new VelocityRegistration(this); + addAndCheckViewResolution(registration); + return registration; + } + + /** + * Register a {@link org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver} + * and a {@link org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer} beans with + * "" prefix, ".ftl" suffix and "/WEB-INF/" templateLoaderPath. + */ + public FreeMarkerRegistration freemarker() { + FreeMarkerRegistration registration = new FreeMarkerRegistration(this); + addAndCheckViewResolution(registration); + return registration; + } + + /** + * Register a {@link org.springframework.web.servlet.view.ContentNegotiatingViewResolver} bean. + */ + public ContentNegotiatingRegistration contentNegotiating(View... defaultViews) { + ContentNegotiatingRegistration registration = new ContentNegotiatingRegistration(this); + registration.defaultViews(defaultViews); + addAndCheckViewResolution(registration); + return registration; + } + + protected List getViewResolvers() { + List viewResolvers = new ArrayList(); + + for(ViewResolutionRegistration registration : this.registrations) { + viewResolvers.add(registration.getViewResolver()); + } + return viewResolvers; + } + + protected TilesConfigurer getTilesConfigurer() { + for(ViewResolutionRegistration registration : this.registrations) { + if(registration instanceof TilesRegistration) { + return ((TilesRegistration) registration).getTilesConfigurer(); + } + } + return null; + } + + protected FreeMarkerConfigurer getFreeMarkerConfigurer() { + for(ViewResolutionRegistration registration : this.registrations) { + if(registration instanceof FreeMarkerRegistration) { + return ((FreeMarkerRegistration) registration).getConfigurer(); + } + } + return null; + } + + protected VelocityConfigurer getVelocityConfigurer() { + for(ViewResolutionRegistration registration : this.registrations) { + if(registration instanceof VelocityRegistration) { + return ((VelocityRegistration) registration).getConfigurer(); + } + } + return null; + } + + private void addAndCheckViewResolution(ViewResolutionRegistration registration) { + for(ViewResolutionRegistration existingRegistration : this.registrations) { + if(existingRegistration.getClass().equals(registration.getClass())) { + throw new IllegalStateException("An instance of " + registration.getClass().getSimpleName() + + " is already registered, and multiple view resolvers and configurers beans are not supported by this registry"); + } + } + registrations.add(registration); + } +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index cd706bcb2f6..24e7e858484 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -64,9 +64,7 @@ import org.springframework.web.context.ServletContextAware; import org.springframework.web.method.support.CompositeUriComponentsContributor; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; -import org.springframework.web.servlet.HandlerAdapter; -import org.springframework.web.servlet.HandlerExceptionResolver; -import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.*; import org.springframework.web.servlet.handler.AbstractHandlerMapping; import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping; import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor; @@ -83,6 +81,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; import org.springframework.web.servlet.resource.ResourceUrlProvider; import org.springframework.web.servlet.resource.ResourceUrlProviderExposingInterceptor; +import org.springframework.web.servlet.view.ContentNegotiatingViewResolver; import org.springframework.web.util.UrlPathHelper; /** @@ -150,12 +149,25 @@ import org.springframework.web.util.UrlPathHelper; * libraries available on the classpath. * * + *

When extending directly from this class instead of using + * {@link EnableWebMvc @EnableWebMvc}, an extra step is needed if you want to use Tiles, FreeMarker + * or Velocity view resolution configuration. Since view configurer beans are registered in their own + * {@link org.springframework.web.servlet.config.annotation.TilesConfigurerConfigurationSupport}, + * {@link org.springframework.web.servlet.config.annotation.FreeMarkerConfigurerConfigurationSupport} + * and {@link org.springframework.web.servlet.config.annotation.VelocityConfigurerConfigurationSupport} + * classes, you should also extend those configuration classes (only the ones + * related to the view technology you are using), or register your own + * {@link org.springframework.web.servlet.view.tiles3.TilesConfigurer}, + * {@link org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer} or + * {@link org.springframework.web.servlet.view.velocity.VelocityConfigurer} beans. + * * @see EnableWebMvc * @see WebMvcConfigurer * @see WebMvcConfigurerAdapter * * @author Rossen Stoyanchev * @author Brian Clozel + * @author Sebastien Deleuze * @since 3.1 */ public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware { @@ -186,6 +198,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv private PathMatchConfigurer pathMatchConfigurer; + private ViewResolutionRegistry viewResolutionRegistry; /** * Set the {@link javax.servlet.ServletContext}, e.g. for resource handling, @@ -342,6 +355,13 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv protected void addViewControllers(ViewControllerRegistry registry) { } + /** + * Override this method to configure view resolution. + * @see ViewResolutionRegistry + */ + protected void configureViewResolution(ViewResolutionRegistry registry) { + } + /** * Return a {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL * paths to controller bean names. @@ -771,6 +791,46 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv exceptionResolvers.add(new DefaultHandlerExceptionResolver()); } + /** + * Register a {@link ViewResolverComposite} that contains an ordered list of + * view resolvers obtained either through + * {@link #configureViewResolution(ViewResolutionRegistry)}. + */ + @Bean + public ViewResolverComposite viewResolverComposite() { + ViewResolutionRegistry registry = getViewResolutionRegistry(); + ViewResolverComposite compositeViewResolver = new ViewResolverComposite(); + List viewResolvers = registry.getViewResolvers(); + ContentNegotiatingViewResolver contentNegotiatingViewResolver = null; + List filteredViewResolvers = new ArrayList(); + for(ViewResolver viewResolver : viewResolvers) { + if(viewResolver instanceof ContentNegotiatingViewResolver) { + contentNegotiatingViewResolver = (ContentNegotiatingViewResolver)viewResolver; + contentNegotiatingViewResolver.setContentNegotiationManager(this.mvcContentNegotiationManager()); + } else { + filteredViewResolvers.add(viewResolver); + } + } + if(contentNegotiatingViewResolver != null) { + contentNegotiatingViewResolver.setViewResolvers(filteredViewResolvers); + viewResolvers = new ArrayList(); + viewResolvers.add(contentNegotiatingViewResolver); + } + compositeViewResolver.setViewResolvers(viewResolvers); + compositeViewResolver.setApplicationContext(this.applicationContext); + compositeViewResolver.setServletContext(this.servletContext); + + return compositeViewResolver; + } + + protected ViewResolutionRegistry getViewResolutionRegistry() { + if(this.viewResolutionRegistry == null) { + this.viewResolutionRegistry = new ViewResolutionRegistry(); + configureViewResolution(this.viewResolutionRegistry); + } + return this.viewResolutionRegistry; + } + private final static class EmptyHandlerMapping extends AbstractHandlerMapping { @Override diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java index 6af00bf29b9..80ec304b380 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java @@ -138,6 +138,11 @@ public interface WebMvcConfigurer { */ void addViewControllers(ViewControllerRegistry registry); + /** + * Configure view resolution to translate view names to view implementations. + */ + void configureViewResolution(ViewResolutionRegistry registry); + /** * Add handlers to serve static resources such as images, js, and, css * files from specific locations under web application root, the classpath, diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java index 69d43fb0156..2c8bb9d0320 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java @@ -133,6 +133,14 @@ public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer { public void addViewControllers(ViewControllerRegistry registry) { } + /** + * {@inheritDoc} + *

This implementation is empty. + */ + @Override + public void configureViewResolution(ViewResolutionRegistry registry) { + } + /** * {@inheritDoc} *

This implementation is empty. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java index 2cea34d6b12..d01bc4fc466 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java @@ -113,6 +113,13 @@ class WebMvcConfigurerComposite implements WebMvcConfigurer { } } + @Override + public void configureViewResolution(ViewResolutionRegistry registry) { + for (WebMvcConfigurer delegate : this.delegates) { + delegate.configureViewResolution(registry); + } + } + @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { for (WebMvcConfigurer delegate : this.delegates) { diff --git a/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.0.xsd b/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.0.xsd index 81ddc92171b..5846e0f3914 100644 --- a/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.0.xsd +++ b/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.0.xsd @@ -515,33 +515,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.1.xsd b/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.1.xsd index a12832ed220..be2cc1cacf7 100644 --- a/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.1.xsd +++ b/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-4.1.xsd @@ -598,5 +598,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java index f64c8a8c34b..7b11724d078 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java @@ -51,7 +51,7 @@ import org.springframework.validation.Errors; import org.springframework.validation.Validator; import org.springframework.validation.annotation.Validated; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; -import org.springframework.web.accept.ContentNegotiationManager; +import org.springframework.web.accept.*; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.context.request.NativeWebRequest; @@ -84,13 +84,13 @@ import org.springframework.web.servlet.resource.ResourceResolver; import org.springframework.web.servlet.resource.ResourceTransformer; import org.springframework.web.servlet.theme.ThemeChangeInterceptor; import org.springframework.web.util.UrlPathHelper; -import org.springframework.web.servlet.view.BeanNameViewResolver; -import org.springframework.web.servlet.view.InternalResourceView; -import org.springframework.web.servlet.view.InternalResourceViewResolver; +import org.springframework.web.servlet.view.*; import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; import org.springframework.web.servlet.view.tiles3.TilesConfigurer; 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.*; @@ -99,6 +99,7 @@ import static org.junit.Assert.*; * @author Arjen Poutsma * @author Jeremy Grelle * @author Brian Clozel + * @author Sebastien Deleuze */ public class MvcNamespaceTests { @@ -556,32 +557,95 @@ public class MvcNamespaceTests { } @Test - public void testViewResolvers() throws Exception{ - loadBeanDefinitions("mvc-config-view-resolvers.xml", 6); - InternalResourceViewResolver internalResourceViewResolver=appContext.getBean(InternalResourceViewResolver.class); + public void testViewResolvers() throws Exception { + loadBeanDefinitions("mvc-config-view-resolution.xml", 8); + + InternalResourceViewResolver internalResourceViewResolver = appContext.getBean(InternalResourceViewResolver.class); assertNotNull(internalResourceViewResolver); - InternalResourceView jstlView=(InternalResourceView) internalResourceViewResolver.resolveViewName("xyz", Locale.ENGLISH); - assertEquals(jstlView.getUrl(), "/WEB-INF/xyz.jsp"); + assertEquals(0, internalResourceViewResolver.getOrder()); + DirectFieldAccessor internalResourceViewResolverFieldAccessor = new DirectFieldAccessor(internalResourceViewResolver); + assertEquals("/WEB-INF/", internalResourceViewResolverFieldAccessor.getPropertyValue("prefix")); + assertEquals(".jsp", internalResourceViewResolverFieldAccessor.getPropertyValue("suffix")); - BeanNameViewResolver beanNameUrlHandlerMapping=appContext.getBean(BeanNameViewResolver.class); - assertNotNull(beanNameUrlHandlerMapping); + BeanNameViewResolver beanNameViewResolver = appContext.getBean(BeanNameViewResolver.class); + assertNotNull(beanNameViewResolver); + assertEquals(1, beanNameViewResolver.getOrder()); - TilesConfigurer tilesConfigurer=appContext.getBean(TilesConfigurer.class); + TilesConfigurer tilesConfigurer = appContext.getBean(TilesConfigurer.class); assertNotNull(tilesConfigurer); + DirectFieldAccessor tilesConfigurerFieldAccessor = new DirectFieldAccessor(tilesConfigurer); + assertArrayEquals(new String[]{"/org/springframework/web/servlet/resource/tiles/tiles1.xml","/org/springframework/web/servlet/resource/tiles/tiles2.xml"}, + (String[])tilesConfigurerFieldAccessor.getPropertyValue("definitions")); + assertTrue((boolean)tilesConfigurerFieldAccessor.getPropertyValue("checkRefresh")); - TilesViewResolver tilesViewResolver=appContext.getBean(TilesViewResolver.class); + TilesViewResolver tilesViewResolver = appContext.getBean(TilesViewResolver.class); assertNotNull(tilesViewResolver); + assertEquals(2, tilesViewResolver.getOrder()); - FreeMarkerConfigurer freeMarkerConfigurer=appContext.getBean(FreeMarkerConfigurer.class); + FreeMarkerConfigurer freeMarkerConfigurer = appContext.getBean(FreeMarkerConfigurer.class); assertNotNull(freeMarkerConfigurer); + DirectFieldAccessor freeMarkerConfigurerFieldAccessor = new DirectFieldAccessor(freeMarkerConfigurer); + assertArrayEquals(new String[]{"/","/test"}, + (String[])freeMarkerConfigurerFieldAccessor.getPropertyValue("templateLoaderPaths")); - FreeMarkerViewResolver freeMarkerViewResolver=appContext.getBean(FreeMarkerViewResolver.class); + FreeMarkerViewResolver freeMarkerViewResolver = appContext.getBean(FreeMarkerViewResolver.class); assertNotNull(freeMarkerViewResolver); - - + assertEquals(3, freeMarkerViewResolver.getOrder()); + DirectFieldAccessor freeMarkerViewResolverFieldAccessor = new DirectFieldAccessor(freeMarkerViewResolver); + assertEquals("", freeMarkerViewResolverFieldAccessor.getPropertyValue("prefix")); + assertEquals(".ftl", freeMarkerViewResolverFieldAccessor.getPropertyValue("suffix")); + assertEquals(0, freeMarkerViewResolverFieldAccessor.getPropertyValue("cacheLimit")); + + VelocityConfigurer velocityConfigurer = appContext.getBean(VelocityConfigurer.class); + assertNotNull(velocityConfigurer); + DirectFieldAccessor velocityConfigurerFieldAccessor = new DirectFieldAccessor(velocityConfigurer); + assertEquals("/", velocityConfigurerFieldAccessor.getPropertyValue("resourceLoaderPath")); + + VelocityViewResolver velocityViewResolver = appContext.getBean(VelocityViewResolver.class); + assertNotNull(velocityViewResolver); + assertEquals(4, velocityViewResolver.getOrder()); + DirectFieldAccessor velocityViewResolverFieldAccessor = new DirectFieldAccessor(velocityViewResolver); + assertEquals("", velocityViewResolverFieldAccessor.getPropertyValue("prefix")); + assertEquals(".vm", velocityViewResolverFieldAccessor.getPropertyValue("suffix")); + assertEquals(0, velocityViewResolverFieldAccessor.getPropertyValue("cacheLimit")); } - + @Test + public void testContentNegotiating() throws Exception { + loadBeanDefinitions("mvc-config-content-negotiating.xml", 11); + + ContentNegotiatingViewResolver contentNegotiatingViewResolver = appContext.getBean(ContentNegotiatingViewResolver.class); + assertNotNull(contentNegotiatingViewResolver); + DirectFieldAccessor contentNegotiatingViewResolverFieldAccessor = new DirectFieldAccessor(contentNegotiatingViewResolver); + assertTrue((boolean)contentNegotiatingViewResolverFieldAccessor.getPropertyValue("useNotAcceptableStatusCode")); + assertEquals(1, ((List)contentNegotiatingViewResolverFieldAccessor.getPropertyValue("defaultViews")).size()); + assertEquals(7, ((List)contentNegotiatingViewResolverFieldAccessor.getPropertyValue("viewResolvers")).size()); + ContentNegotiationManager contentNegotiationManagerProperty = + (ContentNegotiationManager)contentNegotiatingViewResolverFieldAccessor.getPropertyValue("contentNegotiationManager"); + assertNotNull(contentNegotiationManagerProperty); + ContentNegotiationManager contentNegotiationManager = appContext.getBean(ContentNegotiationManager.class); + assertNotNull(contentNegotiationManager); + assertEquals(contentNegotiationManagerProperty.getClass(), contentNegotiationManager.getClass()); + } + + @Test + public void testContentNegotiatingWithDefaultValues() throws Exception { + loadBeanDefinitions("mvc-config-content-negotiating-with-default-values.xml", 19); + + ContentNegotiatingViewResolver contentNegotiatingViewResolver = appContext.getBean(ContentNegotiatingViewResolver.class); + assertNotNull(contentNegotiatingViewResolver); + DirectFieldAccessor contentNegotiatingViewResolverFieldAccessor = new DirectFieldAccessor(contentNegotiatingViewResolver); + assertEquals(1, ((List)contentNegotiatingViewResolverFieldAccessor.getPropertyValue("defaultViews")).size()); + assertEquals(3, ((List)contentNegotiatingViewResolverFieldAccessor.getPropertyValue("viewResolvers")).size()); + ContentNegotiationManager contentNegotiationManagerProperty = + (ContentNegotiationManager)contentNegotiatingViewResolverFieldAccessor.getPropertyValue("contentNegotiationManager"); + assertNotNull(contentNegotiationManagerProperty); + ContentNegotiationManager contentNegotiationManager = appContext.getBean(ContentNegotiationManager.class); + assertNotNull(contentNegotiationManager); + assertEquals(contentNegotiationManagerProperty.getClass(), contentNegotiationManager.getClass()); + } + + @Test public void testPathMatchingHandlerMappings() throws Exception { loadBeanDefinitions("mvc-config-path-matching-mappings.xml", 20); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistryTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistryTests.java new file mode 100644 index 00000000000..666f941d473 --- /dev/null +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ViewResolutionRegistryTests.java @@ -0,0 +1,205 @@ +/* + * 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.config.annotation; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.web.servlet.view.BeanNameViewResolver; +import org.springframework.web.servlet.view.ContentNegotiatingViewResolver; +import org.springframework.web.servlet.view.InternalResourceViewResolver; +import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; +import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; +import org.springframework.web.servlet.view.json.MappingJackson2JsonView; +import org.springframework.web.servlet.view.tiles3.TilesConfigurer; +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.*; +import static org.junit.Assert.assertEquals; + +/** + * Test fixture with a {@link ViewResolutionRegistry}. + * + * @author Sebastien Deleuze + */ +public class ViewResolutionRegistryTests { + + private ViewResolutionRegistry registry; + + @Before + public void setUp() { + registry = new ViewResolutionRegistry(); + } + + @Test + public void noViewResolution() { + assertNotNull(registry.getViewResolvers()); + assertEquals(0, registry.getViewResolvers().size()); + } + + @Test + public void customViewResolution() { + InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); + viewResolver.setPrefix("/"); + viewResolver.setSuffix(".jsp"); + registry.addViewResolver(viewResolver); + assertEquals(InternalResourceViewResolver.class, registry.getViewResolvers().get(0).getClass()); + InternalResourceViewResolver resolver = (InternalResourceViewResolver)registry.getViewResolvers().get(0); + DirectFieldAccessor resolverDirectFieldAccessor = new DirectFieldAccessor(resolver); + assertEquals("/", resolverDirectFieldAccessor.getPropertyValue("prefix")); + assertEquals(".jsp", resolverDirectFieldAccessor.getPropertyValue("suffix")); + } + + @Test + public void beanNameViewResolution() { + registry.beanName(); + assertNotNull(registry.getViewResolvers()); + assertEquals(1, registry.getViewResolvers().size()); + assertEquals(BeanNameViewResolver.class, registry.getViewResolvers().get(0).getClass()); + } + + @Test + public void jspViewResolution() { + registry.jsp("/", ".jsp"); + assertNotNull(registry.getViewResolvers()); + assertEquals(1, registry.getViewResolvers().size()); + assertEquals(InternalResourceViewResolver.class, registry.getViewResolvers().get(0).getClass()); + InternalResourceViewResolver resolver = (InternalResourceViewResolver)registry.getViewResolvers().get(0); + DirectFieldAccessor resolverDirectFieldAccessor = new DirectFieldAccessor(resolver); + assertEquals("/", resolverDirectFieldAccessor.getPropertyValue("prefix")); + assertEquals(".jsp", resolverDirectFieldAccessor.getPropertyValue("suffix")); + } + + @Test + public void defaultJspViewResolution() { + registry.jsp(); + assertNotNull(registry.getViewResolvers()); + assertEquals(1, registry.getViewResolvers().size()); + assertEquals(InternalResourceViewResolver.class, registry.getViewResolvers().get(0).getClass()); + InternalResourceViewResolver resolver = (InternalResourceViewResolver)registry.getViewResolvers().get(0); + DirectFieldAccessor resolverDirectFieldAccessor = new DirectFieldAccessor(resolver); + assertEquals("/WEB-INF/", resolverDirectFieldAccessor.getPropertyValue("prefix")); + assertEquals(".jsp", resolverDirectFieldAccessor.getPropertyValue("suffix")); + } + + @Test + public void tilesViewResolution() { + registry.tiles().checkRefresh(true).definition("def1").definition("def2"); + assertNotNull(registry.getViewResolvers()); + assertEquals(1, registry.getViewResolvers().size()); + assertEquals(TilesViewResolver.class, registry.getViewResolvers().get(0).getClass()); + + assertNotNull(registry.getTilesConfigurer()); + TilesConfigurer configurer = registry.getTilesConfigurer(); + DirectFieldAccessor configurerDirectFieldAccessor = new DirectFieldAccessor(configurer); + assertTrue((boolean) configurerDirectFieldAccessor.getPropertyValue("checkRefresh")); + assertNotNull(configurerDirectFieldAccessor.getPropertyValue("definitions")); + String[] definitions = (String[])configurerDirectFieldAccessor.getPropertyValue("definitions"); + assertEquals(2, definitions.length); + assertEquals("def1", definitions[0]); + assertEquals("def2", definitions[1]); + } + + @Test + public void velocityViewResolution() { + registry.velocity().prefix("/").suffix(".vm").cache(true).resourceLoaderPath("testResourceLoaderPath"); + assertNotNull(registry.getViewResolvers()); + assertEquals(1, registry.getViewResolvers().size()); + assertEquals(VelocityViewResolver.class, registry.getViewResolvers().get(0).getClass()); + VelocityViewResolver resolver = (VelocityViewResolver)registry.getViewResolvers().get(0); + DirectFieldAccessor resolverDirectFieldAccessor = new DirectFieldAccessor(resolver); + assertEquals("/", resolverDirectFieldAccessor.getPropertyValue("prefix")); + assertEquals(".vm", resolverDirectFieldAccessor.getPropertyValue("suffix")); + assertEquals(1024, resolverDirectFieldAccessor.getPropertyValue("cacheLimit")); + + assertNotNull(registry.getVelocityConfigurer()); + VelocityConfigurer configurer = registry.getVelocityConfigurer(); + DirectFieldAccessor configurerDirectFieldAccessor = new DirectFieldAccessor(configurer); + assertEquals("testResourceLoaderPath", configurerDirectFieldAccessor.getPropertyValue("resourceLoaderPath")); + } + + @Test + public void defaultVelocityViewResolution() { + registry.velocity(); + assertNotNull(registry.getViewResolvers()); + assertEquals(1, registry.getViewResolvers().size()); + assertEquals(VelocityViewResolver.class, registry.getViewResolvers().get(0).getClass()); + VelocityViewResolver resolver = (VelocityViewResolver)registry.getViewResolvers().get(0); + DirectFieldAccessor resolverDirectFieldAccessor = new DirectFieldAccessor(resolver); + assertEquals("", resolverDirectFieldAccessor.getPropertyValue("prefix")); + assertEquals(".vm", resolverDirectFieldAccessor.getPropertyValue("suffix")); + + assertNotNull(registry.getVelocityConfigurer()); + VelocityConfigurer configurer = registry.getVelocityConfigurer(); + DirectFieldAccessor configurerDirectFieldAccessor = new DirectFieldAccessor(configurer); + assertEquals("/WEB-INF/", configurerDirectFieldAccessor.getPropertyValue("resourceLoaderPath")); + } + + @Test + public void freeMarkerViewResolution() { + registry.freemarker().prefix("/").suffix(".fmt").cache(false).templateLoaderPath("path1", "path2"); + assertNotNull(registry.getViewResolvers()); + assertEquals(1, registry.getViewResolvers().size()); + assertEquals(FreeMarkerViewResolver.class, registry.getViewResolvers().get(0).getClass()); + FreeMarkerViewResolver resolver = (FreeMarkerViewResolver)registry.getViewResolvers().get(0); + DirectFieldAccessor resolverDirectFieldAccessor = new DirectFieldAccessor(resolver); + assertEquals("/", resolverDirectFieldAccessor.getPropertyValue("prefix")); + assertEquals(".fmt", resolverDirectFieldAccessor.getPropertyValue("suffix")); + assertEquals(0, resolverDirectFieldAccessor.getPropertyValue("cacheLimit")); + + assertNotNull(registry.getFreeMarkerConfigurer()); + FreeMarkerConfigurer configurer = registry.getFreeMarkerConfigurer(); + DirectFieldAccessor configurerDirectFieldAccessor = new DirectFieldAccessor(configurer); + assertNotNull(configurerDirectFieldAccessor.getPropertyValue("templateLoaderPaths")); + String[] templateLoaderPaths = (String[])configurerDirectFieldAccessor.getPropertyValue("templateLoaderPaths"); + assertEquals(2, templateLoaderPaths.length); + assertEquals("path1", templateLoaderPaths[0]); + assertEquals("path2", templateLoaderPaths[1]); + } + + @Test + public void defaultFreeMarkerViewResolution() { + registry.freemarker(); + assertNotNull(registry.getViewResolvers()); + assertEquals(1, registry.getViewResolvers().size()); + assertEquals(FreeMarkerViewResolver.class, registry.getViewResolvers().get(0).getClass()); + FreeMarkerViewResolver resolver = (FreeMarkerViewResolver)registry.getViewResolvers().get(0); + DirectFieldAccessor resolverDirectFieldAccessor = new DirectFieldAccessor(resolver); + assertEquals("", resolverDirectFieldAccessor.getPropertyValue("prefix")); + assertEquals(".ftl", resolverDirectFieldAccessor.getPropertyValue("suffix")); + DirectFieldAccessor configurerDirectFieldAccessor = new DirectFieldAccessor(registry.getFreeMarkerConfigurer()); + assertArrayEquals(new String[]{"/WEB-INF/"}, (String[])configurerDirectFieldAccessor.getPropertyValue("templateLoaderPaths")); + } + + @Test + public void contentNegotiatingViewResolution() { + registry.contentNegotiating().useNotAcceptable(false).defaultViews(new MappingJackson2JsonView()); + assertNotNull(registry.getViewResolvers()); + assertEquals(1, registry.getViewResolvers().size()); + assertEquals(ContentNegotiatingViewResolver.class, registry.getViewResolvers().get(0).getClass()); + } + + @Test + public void multipleViewResolutions() { + registry.jsp().and().beanName(); + assertNotNull(registry.getViewResolvers()); + assertEquals(2, registry.getViewResolvers().size()); + } + +} diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java index c17da91504c..a1cc57a3d72 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java @@ -17,6 +17,7 @@ package org.springframework.web.servlet.config.annotation; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import java.util.Arrays; @@ -24,6 +25,7 @@ import java.util.List; import org.junit.Before; import org.junit.Test; import org.springframework.beans.DirectFieldAccessor; +import org.springframework.core.Ordered; import org.springframework.tests.sample.beans.TestBean; import org.springframework.core.convert.converter.Converter; import org.springframework.core.io.FileSystemResourceLoader; @@ -54,8 +56,7 @@ import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.method.annotation.ModelAttributeMethodProcessor; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; -import org.springframework.web.servlet.HandlerExceptionResolver; -import org.springframework.web.servlet.HandlerExecutionChain; +import org.springframework.web.servlet.*; import org.springframework.web.servlet.handler.AbstractHandlerMapping; import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor; import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite; @@ -65,12 +66,16 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.servlet.resource.ResourceUrlProviderExposingInterceptor; import org.springframework.web.util.UrlPathHelper; +import org.springframework.web.servlet.view.ContentNegotiatingViewResolver; +import org.springframework.web.servlet.view.InternalResourceViewResolver; +import org.springframework.web.servlet.view.json.MappingJackson2JsonView; /** * A test fixture with a sub-class of {@link WebMvcConfigurationSupport} that * implements the various {@link WebMvcConfigurer} extension points. * * @author Rossen Stoyanchev + * @author Sebastien Deleuze */ public class WebMvcConfigurationSupportExtensionTests { @@ -78,7 +83,6 @@ public class WebMvcConfigurationSupportExtensionTests { private StaticWebApplicationContext webAppContext; - @Before public void setUp() { this.webAppContext = new StaticWebApplicationContext(); @@ -211,6 +215,28 @@ public class WebMvcConfigurationSupportExtensionTests { assertEquals(1, composite.getExceptionResolvers().size()); } + @Test + public void viewResolvers() throws Exception { + ViewResolverComposite viewResolver = webConfig.viewResolverComposite(); + assertEquals(Ordered.LOWEST_PRECEDENCE, viewResolver.getOrder()); + List viewResolvers = viewResolver.getViewResolvers(); + DirectFieldAccessor viewResolverFieldAccessor = new DirectFieldAccessor(viewResolvers.get(0)); + assertEquals(1, viewResolvers.size()); + assertEquals(ContentNegotiatingViewResolver.class, viewResolvers.get(0).getClass()); + assertFalse((Boolean)viewResolverFieldAccessor.getPropertyValue("useNotAcceptableStatusCode")); + List defaultViews = (List)viewResolverFieldAccessor.getPropertyValue("defaultViews"); + assertNotNull(defaultViews); + assertEquals(1, defaultViews.size()); + assertEquals(MappingJackson2JsonView.class, defaultViews.get(0).getClass()); + assertNotNull(viewResolverFieldAccessor.getPropertyValue("contentNegotiationManager")); + viewResolvers = (List)viewResolverFieldAccessor.getPropertyValue("viewResolvers"); + assertNotNull(viewResolvers); + assertEquals(1, viewResolvers.size()); + assertEquals(InternalResourceViewResolver.class, viewResolvers.get(0).getClass()); + viewResolverFieldAccessor = new DirectFieldAccessor(viewResolvers.get(0)); + assertEquals("/", viewResolverFieldAccessor.getPropertyValue("prefix")); + assertEquals(".jsp", viewResolverFieldAccessor.getPropertyValue("suffix")); + } @Controller private static class TestController { @@ -312,6 +338,12 @@ public class WebMvcConfigurationSupportExtensionTests { registry.addViewController("/path"); } + @Override + public void configureViewResolution(ViewResolutionRegistry registry) { + registry.jsp("/", ".jsp"); + registry.contentNegotiating().useNotAcceptable(false).defaultViews(new MappingJackson2JsonView()); + } + @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("src/test/java"); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java index 7d513d08972..f1a0b83027f 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java @@ -22,11 +22,11 @@ import javax.servlet.http.HttpServletRequest; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; - import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; +import org.springframework.core.Ordered; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.core.convert.ConversionService; import org.springframework.format.annotation.DateTimeFormat; @@ -48,6 +48,8 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon import org.springframework.web.method.support.CompositeUriComponentsContributor; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerExecutionChain; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.ViewResolverComposite; import org.springframework.web.servlet.handler.AbstractHandlerMapping; import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping; import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor; @@ -69,6 +71,7 @@ import static org.junit.Assert.*; * * @author Rossen Stoyanchev * @author Juergen Hoeller + * @author Sebastien Deleuze */ public class WebMvcConfigurationSupportTests { @@ -199,6 +202,14 @@ public class WebMvcConfigurationSupportTests { assertEquals(1, interceptors.size()); assertEquals(JsonViewResponseBodyAdvice.class, interceptors.get(0).getClass()); } + + @Test + public void viewResolvers() throws Exception { + ViewResolverComposite compositeResolver = this.wac.getBean(ViewResolverComposite.class); + assertEquals(Ordered.LOWEST_PRECEDENCE, compositeResolver.getOrder()); + List viewResolvers = compositeResolver.getViewResolvers(); + assertEquals(0, viewResolvers.size()); + } @Test public void defaultPathMatchConfiguration() throws Exception { diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-content-negotiating-with-default-values.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-content-negotiating-with-default-values.xml new file mode 100644 index 00000000000..3735139a785 --- /dev/null +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-content-negotiating-with-default-values.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-content-negotiating.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-content-negotiating.xml new file mode 100644 index 00000000000..5b6c4613470 --- /dev/null +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-content-negotiating.xml @@ -0,0 +1,53 @@ + + + + + + /WEB-INF/pages/ + + + .jsp + + + + + + + xml=application/rss+xml + + + + + + + + + + + + + + + + + + + + + /WEB-INF/pages/ + + + .jsp + + + + + + + + \ No newline at end of file diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-resolution.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-resolution.xml new file mode 100644 index 00000000000..f39c3dd89ec --- /dev/null +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-resolution.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-resolvers.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-resolvers.xml deleted file mode 100644 index 827440d5506..00000000000 --- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-view-resolvers.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file