Introduce Freemarker/Velocity/TilesWebMvcConfigurer

This change improves the support for auto-registration of FreeMarker,
Velocity, and Tiles configuration.

The configuration is now conditional not only based on the classpath
but also based on whether a FreeMarkerConfigurer for example is already
present in the configuration.

This change also introduces FreeMarker~, Velocity~, and
TilesWebMvcConfigurer interfaces for customizing each view technology.

The WebMvcConfigurer can still be used to configure all view resolvers
centrally (including FreeMarker, Velocity, and Tiles) without some
default conifguration, i.e. without the need to use the new
~WebMvcConfigurer interfaces until customizations are required.

Issue: SPR-7093
This commit is contained in:
Rossen Stoyanchev
2014-07-12 00:28:27 -04:00
parent cc7e8f5558
commit 5bc793768c
26 changed files with 796 additions and 394 deletions

View File

@@ -0,0 +1,239 @@
/*
* 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.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
import org.springframework.mock.web.test.MockServletConfig;
import org.springframework.mock.web.test.MockServletContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
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 javax.servlet.ServletException;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
/**
* Integration tests for view resolution with {@code @EnableWebMvc}.
*
* @author Rossen Stoyanchev
* @since 4.1
*/
public class ViewResolutionIntegrationTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
@Test
public void minimalFreemarkerConfig() throws Exception {
MockHttpServletResponse response = runTest(MinimalFreeMarkerWebConfig.class);
assertEquals("<html><body>Hello World!</body></html>", response.getContentAsString());
}
@Test
public void minimalVelocityConfig() throws Exception {
MockHttpServletResponse response = runTest(MinimalVelocityWebConfig.class);
assertEquals("<html><body>Hello World!</body></html>", response.getContentAsString());
}
@Test
public void minimalTilesConfig() throws Exception {
MockHttpServletResponse response = runTest(MinimalTilesWebConfig.class);
assertEquals("/WEB-INF/index.jsp", response.getForwardedUrl());
}
@Test
public void freemarker() throws Exception {
MockHttpServletResponse response = runTest(FreeMarkerWebConfig.class);
assertEquals("<html><body>Hello World!</body></html>", response.getContentAsString());
}
@Test
public void velocity() throws Exception {
MockHttpServletResponse response = runTest(VelocityWebConfig.class);
assertEquals("<html><body>Hello World!</body></html>", response.getContentAsString());
}
@Test
public void tiles() throws Exception {
MockHttpServletResponse response = runTest(TilesWebConfig.class);
assertEquals("/WEB-INF/index.jsp", response.getForwardedUrl());
}
@Test
public void freemarkerInvalidConfig() throws Exception {
this.thrown.expectMessage("It looks like you're trying to configure FreeMarker view resolution.");
runTest(InvalidFreeMarkerWebConfig.class);
}
@Test
public void velocityInvalidConfig() throws Exception {
this.thrown.expectMessage("It looks like you're trying to configure Velocity view resolution.");
runTest(InvalidVelocityWebConfig.class);
}
@Test
public void tilesInvalidConfig() throws Exception {
this.thrown.expectMessage("It looks like you're trying to configure Tiles view resolution.");
runTest(InvalidTilesWebConfig.class);
}
private MockHttpServletResponse runTest(Class<?> configClass) throws ServletException, IOException {
String basePath = "org/springframework/web/servlet/config/annotation";
MockServletContext servletContext = new MockServletContext(basePath);
MockServletConfig servletConfig = new MockServletConfig(servletContext);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
MockHttpServletResponse response = new MockHttpServletResponse();
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClass);
context.setServletContext(servletContext);
context.refresh();
DispatcherServlet servlet = new DispatcherServlet(context);
servlet.init(servletConfig);
servlet.service(request, response);
return response;
}
@Controller
static class SampleController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String tiles(@ModelAttribute("model") ModelMap model) {
model.addAttribute("hello", "Hello World!");
return "index";
}
}
@EnableWebMvc
static abstract class AbstractWebConfig extends WebMvcConfigurerAdapter {
@Bean
public SampleController sampleController() {
return new SampleController();
}
}
@Configuration
static class MinimalFreeMarkerWebConfig extends AbstractWebConfig {
@Override
public void configureViewResolution(ViewResolutionRegistry registry) {
registry.freemarker();
}
}
@Configuration
static class MinimalVelocityWebConfig extends AbstractWebConfig {
@Override
public void configureViewResolution(ViewResolutionRegistry registry) {
registry.velocity();
}
}
@Configuration
static class MinimalTilesWebConfig extends AbstractWebConfig {
@Override
public void configureViewResolution(ViewResolutionRegistry registry) {
registry.tiles();
}
}
@Configuration
static class FreeMarkerWebConfig extends AbstractWebConfig implements FreeMarkerWebMvcConfigurer {
@Override
public void configureViewResolution(ViewResolutionRegistry registry) {
registry.freemarker();
}
@Override
public void configureFreeMarker(FreeMarkerConfigurer configurer) {
configurer.setTemplateLoaderPath("/WEB-INF/");
}
}
@Configuration
static class VelocityWebConfig extends AbstractWebConfig implements VelocityWebMvcConfigurer {
@Override
public void configureViewResolution(ViewResolutionRegistry registry) {
registry.velocity();
}
@Override
public void configureVelocity(VelocityConfigurer configurer) {
configurer.setResourceLoaderPath("/WEB-INF/");
}
}
@Configuration
static class TilesWebConfig extends AbstractWebConfig implements TilesWebMvcConfigurer {
@Override
public void configureViewResolution(ViewResolutionRegistry registry) {
registry.tiles();
}
@Override
public void configureTiles(TilesConfigurer configurer) {
configurer.setDefinitions("/WEB-INF/tiles.xml");
}
}
@Configuration
static class InvalidFreeMarkerWebConfig extends WebMvcConfigurationSupport {
// No @EnableWebMvc and no FreeMarkerConfigurer bean
@Override
public void configureViewResolution(ViewResolutionRegistry registry) {
registry.freemarker();
}
}
@Configuration
static class InvalidVelocityWebConfig extends WebMvcConfigurationSupport {
// No @EnableWebMvc and no VelocityConfigurer bean
@Override
public void configureViewResolution(ViewResolutionRegistry registry) {
registry.velocity();
}
}
@Configuration
static class InvalidTilesWebConfig extends WebMvcConfigurationSupport {
// No @EnableWebMvc and no TilesConfigurer bean
@Override
public void configureViewResolution(ViewResolutionRegistry registry) {
registry.tiles();
}
}
}

View File

@@ -19,15 +19,13 @@ package org.springframework.web.servlet.config.annotation;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.web.context.support.StaticWebApplicationContext;
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.*;
@@ -44,7 +42,7 @@ public class ViewResolutionRegistryTests {
@Before
public void setUp() {
registry = new ViewResolutionRegistry();
registry = new ViewResolutionRegistry(new StaticWebApplicationContext());
}
@Test
@@ -100,25 +98,15 @@ public class ViewResolutionRegistryTests {
@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]);
this.registry.tiles();
assertNotNull(this.registry.getViewResolvers());
assertEquals(1, this.registry.getViewResolvers().size());
assertEquals(TilesViewResolver.class, this.registry.getViewResolvers().get(0).getClass());
}
@Test
public void velocityViewResolution() {
registry.velocity().prefix("/").suffix(".vm").cache(true).resourceLoaderPath("testResourceLoaderPath");
registry.velocity().prefix("/").suffix(".vm").cache(true);
assertNotNull(registry.getViewResolvers());
assertEquals(1, registry.getViewResolvers().size());
assertEquals(VelocityViewResolver.class, registry.getViewResolvers().get(0).getClass());
@@ -127,11 +115,6 @@ public class ViewResolutionRegistryTests {
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
@@ -144,16 +127,11 @@ public class ViewResolutionRegistryTests {
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");
registry.freemarker().prefix("/").suffix(".fmt").cache(false);
assertNotNull(registry.getViewResolvers());
assertEquals(1, registry.getViewResolvers().size());
assertEquals(FreeMarkerViewResolver.class, registry.getViewResolvers().get(0).getClass());
@@ -162,15 +140,6 @@ public class ViewResolutionRegistryTests {
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
@@ -183,8 +152,6 @@ public class ViewResolutionRegistryTests {
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