diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 7b40548dfb..80cd968d9f 100644 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -66,6 +66,11 @@ tomcat-jdbc true + + org.apache.velocity + velocity + true + org.codehaus.groovy groovy-templates diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfiguration.java index ac8f4c77e0..1ed1c44593 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfiguration.java @@ -28,11 +28,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.template.TemplateViewResolverConfigurer; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -45,7 +45,7 @@ import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; /** * {@link EnableAutoConfiguration Auto-configuration} for FreeMarker. - * + * * @author Andy Wilkinson * @author Dave Syer * @since 1.1.0 @@ -65,8 +65,8 @@ public class FreeMarkerAutoConfiguration { @PostConstruct public void checkTemplateLocationExists() { if (this.properties.isCheckTemplateLocation()) { - Resource resource = this.resourceLoader - .getResource(this.properties.getTemplateLoaderPath()); + Resource resource = this.resourceLoader.getResource(this.properties + .getTemplateLoaderPath()); Assert.state(resource.exists(), "Cannot find template location: " + resource + " (please add some templates " + "or check your FreeMarker configuration)"); @@ -125,26 +125,10 @@ public class FreeMarkerAutoConfiguration { @ConditionalOnMissingBean(name = "freeMarkerViewResolver") public FreeMarkerViewResolver freeMarkerViewResolver() { FreeMarkerViewResolver resolver = new FreeMarkerViewResolver(); - resolver.setPrefix(this.properties.getPrefix()); - resolver.setSuffix(this.properties.getSuffix()); - resolver.setCache(this.properties.isCache()); - resolver.setContentType(this.properties.getContentType()); - resolver.setViewNames(this.properties.getViewNames()); - resolver.setExposeRequestAttributes(this.properties - .isExposeRequestAttributes()); - resolver.setExposeRequestAttributes(this.properties.isAllowRequestOverride()); - resolver.setExposeRequestAttributes(this.properties - .isExposeSessionAttributes()); - resolver.setExposeRequestAttributes(this.properties - .isExposeSpringMacroHelpers()); - resolver.setRequestContextAttribute(this.properties - .getRequestContextAttribute()); - - // This resolver acts as a fallback resolver (e.g. like a - // InternalResourceViewResolver) so it needs to have low precedence - resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5); - + new TemplateViewResolverConfigurer().configureTemplateViewResolver(resolver, + this.properties); return resolver; } + } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerProperties.java index 6dfe4dad5b..f662891985 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerProperties.java @@ -19,15 +19,19 @@ package org.springframework.boot.autoconfigure.freemarker; import java.util.HashMap; import java.util.Map; +import org.springframework.boot.autoconfigure.template.AbstractTemplateViewResolverProperties; import org.springframework.boot.context.properties.ConfigurationProperties; /** + * {@link ConfigurationProperties} for configuring FreeMarker + * * @author Dave Syer - * + * @author Andy Wilkinson + * * @since 1.1.0 */ @ConfigurationProperties(prefix = "spring.freemarker") -public class FreeMarkerProperties { +public class FreeMarkerProperties extends AbstractTemplateViewResolverProperties { public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/"; @@ -35,138 +39,12 @@ public class FreeMarkerProperties { public static final String DEFAULT_SUFFIX = ".ftl"; - private String prefix = DEFAULT_PREFIX; - - private String suffix = DEFAULT_SUFFIX; + private Map settings = new HashMap(); private String templateLoaderPath = DEFAULT_TEMPLATE_LOADER_PATH; - private boolean cache; - - private String contentType = "text/html"; - - private String charSet = "UTF-8"; - - private String[] viewNames; - - private boolean checkTemplateLocation = true; - - private String requestContextAttribute; - - private boolean exposeRequestAttributes = false; - - private boolean exposeSessionAttributes = false; - - private boolean allowRequestOverride = false; - - private boolean exposeSpringMacroHelpers = true; - - private Map settings = new HashMap(); - - public void setCheckTemplateLocation(boolean checkTemplateLocation) { - this.checkTemplateLocation = checkTemplateLocation; - } - - public boolean isCheckTemplateLocation() { - return this.checkTemplateLocation; - } - - public String[] getViewNames() { - return this.viewNames; - } - - public void setViewNames(String[] viewNames) { - this.viewNames = viewNames; - } - - public boolean isCache() { - return this.cache; - } - - public void setCache(boolean cache) { - this.cache = cache; - } - - public String getContentType() { - return this.contentType - + (this.contentType.contains(";charset=") ? "" : ";charset=" - + this.charSet); - } - - public void setContentType(String contentType) { - this.contentType = contentType; - } - - public String getCharSet() { - return this.charSet; - } - - public void setCharSet(String charSet) { - this.charSet = charSet; - } - - public String getPrefix() { - return this.prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public String getSuffix() { - return this.suffix; - } - - public void setSuffix(String suffix) { - this.suffix = suffix; - } - - public String getTemplateLoaderPath() { - return this.templateLoaderPath; - } - - public void setTemplateLoaderPath(String templateLoaderPath) { - this.templateLoaderPath = templateLoaderPath; - } - - public String getRequestContextAttribute() { - return this.requestContextAttribute; - } - - public void setRequestContextAttribute(String requestContextAttribute) { - this.requestContextAttribute = requestContextAttribute; - } - - public boolean isExposeRequestAttributes() { - return this.exposeRequestAttributes; - } - - public void setExposeRequestAttributes(boolean exposeRequestAttributes) { - this.exposeRequestAttributes = exposeRequestAttributes; - } - - public boolean isExposeSessionAttributes() { - return this.exposeSessionAttributes; - } - - public void setExposeSessionAttributes(boolean exposeSessionAttributes) { - this.exposeSessionAttributes = exposeSessionAttributes; - } - - public boolean isAllowRequestOverride() { - return this.allowRequestOverride; - } - - public void setAllowRequestOverride(boolean allowRequestOverride) { - this.allowRequestOverride = allowRequestOverride; - } - - public boolean isExposeSpringMacroHelpers() { - return this.exposeSpringMacroHelpers; - } - - public void setExposeSpringMacroHelpers(boolean exposeSpringMacroHelpers) { - this.exposeSpringMacroHelpers = exposeSpringMacroHelpers; + public FreeMarkerProperties() { + super(DEFAULT_PREFIX, DEFAULT_SUFFIX); } public Map getSettings() { @@ -177,4 +55,11 @@ public class FreeMarkerProperties { this.settings = settings; } + public String getTemplateLoaderPath() { + return this.templateLoaderPath; + } + + public void setTemplateLoaderPath(String templateLoaderPath) { + this.templateLoaderPath = templateLoaderPath; + } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/AbstractTemplateViewResolverProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/AbstractTemplateViewResolverProperties.java new file mode 100644 index 0000000000..bba7ca3094 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/AbstractTemplateViewResolverProperties.java @@ -0,0 +1,158 @@ +/* + * Copyright 2012-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.boot.autoconfigure.template; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.web.servlet.view.AbstractTemplateViewResolver; + +/** + * Abstract base class for {@link ConfigurationProperties} for + * {@link AbstractTemplateViewResolver view resolvers}. + * + * @author Andy Wilkinson + * @since 1.1.0 + */ +public abstract class AbstractTemplateViewResolverProperties { + + private String prefix; + + private String suffix; + + private boolean cache; + + private String contentType = "text/html"; + + private String charSet = "UTF-8"; + + private String[] viewNames; + + private boolean checkTemplateLocation = true; + + private String requestContextAttribute; + + private boolean exposeRequestAttributes = false; + + private boolean exposeSessionAttributes = false; + + private boolean allowRequestOverride = false; + + private boolean exposeSpringMacroHelpers = true; + + protected AbstractTemplateViewResolverProperties(String defaultPrefix, + String defaultSuffix) { + this.prefix = defaultPrefix; + this.suffix = defaultSuffix; + } + + public void setCheckTemplateLocation(boolean checkTemplateLocation) { + this.checkTemplateLocation = checkTemplateLocation; + } + + public boolean isCheckTemplateLocation() { + return this.checkTemplateLocation; + } + + public String[] getViewNames() { + return this.viewNames; + } + + public void setViewNames(String[] viewNames) { + this.viewNames = viewNames; + } + + public boolean isCache() { + return this.cache; + } + + public void setCache(boolean cache) { + this.cache = cache; + } + + public String getContentType() { + return this.contentType + + (this.contentType.contains(";charset=") ? "" : ";charset=" + + this.charSet); + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public String getCharSet() { + return this.charSet; + } + + public void setCharSet(String charSet) { + this.charSet = charSet; + } + + public String getPrefix() { + return this.prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getSuffix() { + return this.suffix; + } + + public void setSuffix(String suffix) { + this.suffix = suffix; + } + + public String getRequestContextAttribute() { + return this.requestContextAttribute; + } + + public void setRequestContextAttribute(String requestContextAttribute) { + this.requestContextAttribute = requestContextAttribute; + } + + public boolean isExposeRequestAttributes() { + return this.exposeRequestAttributes; + } + + public void setExposeRequestAttributes(boolean exposeRequestAttributes) { + this.exposeRequestAttributes = exposeRequestAttributes; + } + + public boolean isExposeSessionAttributes() { + return this.exposeSessionAttributes; + } + + public void setExposeSessionAttributes(boolean exposeSessionAttributes) { + this.exposeSessionAttributes = exposeSessionAttributes; + } + + public boolean isAllowRequestOverride() { + return this.allowRequestOverride; + } + + public void setAllowRequestOverride(boolean allowRequestOverride) { + this.allowRequestOverride = allowRequestOverride; + } + + public boolean isExposeSpringMacroHelpers() { + return this.exposeSpringMacroHelpers; + } + + public void setExposeSpringMacroHelpers(boolean exposeSpringMacroHelpers) { + this.exposeSpringMacroHelpers = exposeSpringMacroHelpers; + } +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateViewResolverConfigurer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateViewResolverConfigurer.java new file mode 100644 index 0000000000..25128b4f7c --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/template/TemplateViewResolverConfigurer.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012-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.boot.autoconfigure.template; + +import org.springframework.core.Ordered; +import org.springframework.web.servlet.view.AbstractTemplateViewResolver; + +/** + * Helper class for use by configuration classes that provide an + * {@link AbstractTemplateViewResolver} bean. + * + * @author Andy Wilkinson + */ +public class TemplateViewResolverConfigurer { + + /** + * Configures the {@code resolver} using the given {@code properties} and defaults. + * + * @param resolver The resolver to configure + * @param properties The properties to use to configure the resolver + */ + public void configureTemplateViewResolver(AbstractTemplateViewResolver resolver, + AbstractTemplateViewResolverProperties properties) { + + resolver.setPrefix(properties.getPrefix()); + resolver.setSuffix(properties.getSuffix()); + resolver.setCache(properties.isCache()); + resolver.setContentType(properties.getContentType()); + resolver.setViewNames(properties.getViewNames()); + resolver.setExposeRequestAttributes(properties.isExposeRequestAttributes()); + resolver.setAllowRequestOverride(properties.isAllowRequestOverride()); + resolver.setExposeSessionAttributes(properties.isExposeSessionAttributes()); + resolver.setExposeSpringMacroHelpers(properties.isExposeSpringMacroHelpers()); + resolver.setRequestContextAttribute(properties.getRequestContextAttribute()); + + // This resolver acts as a fallback resolver (e.g. like a + // InternalResourceViewResolver) so it needs to have low precedence + resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfiguration.java new file mode 100644 index 0000000000..689152cbfc --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfiguration.java @@ -0,0 +1,136 @@ +/* + * Copyright 2012-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.boot.autoconfigure.velocity; + +import java.io.IOException; +import java.util.Properties; + +import javax.annotation.PostConstruct; +import javax.servlet.Servlet; + +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.VelocityException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.template.TemplateViewResolverConfigurer; +import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.ui.velocity.VelocityEngineFactory; +import org.springframework.ui.velocity.VelocityEngineFactoryBean; +import org.springframework.util.Assert; +import org.springframework.web.servlet.view.velocity.VelocityConfig; +import org.springframework.web.servlet.view.velocity.VelocityConfigurer; +import org.springframework.web.servlet.view.velocity.VelocityViewResolver; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Velocity. + * + * @author Andy Wilkinson + * @since 1.1.0 + */ +@Configuration +@ConditionalOnClass(VelocityEngine.class) +@AutoConfigureAfter(WebMvcAutoConfiguration.class) +@EnableConfigurationProperties(VelocityProperties.class) +public class VelocityAutoConfiguration { + + @Autowired + private final ResourceLoader resourceLoader = new DefaultResourceLoader(); + + @Autowired + private VelocityProperties properties; + + @PostConstruct + public void checkTemplateLocationExists() { + if (this.properties.isCheckTemplateLocation()) { + Resource resource = this.resourceLoader.getResource(this.properties + .getResourceLoaderPath()); + Assert.state(resource.exists(), "Cannot find template location: " + resource + + " (please add some templates " + + "or check your Velocity configuration)"); + } + } + + protected static class VelocityConfiguration { + + @Autowired + protected VelocityProperties properties; + + protected void applyProperties(VelocityEngineFactory factory) { + factory.setResourceLoaderPath(this.properties.getResourceLoaderPath()); + Properties velocityProperties = new Properties(); + velocityProperties.putAll(this.properties.getProperties()); + factory.setVelocityProperties(velocityProperties); + } + } + + @Configuration + @ConditionalOnNotWebApplication + public static class VelocityNonWebConfiguration extends VelocityConfiguration { + + @Bean + @ConditionalOnMissingBean + public VelocityEngineFactoryBean velocityConfiguration() { + VelocityEngineFactoryBean velocityEngineFactoryBean = new VelocityEngineFactoryBean(); + applyProperties(velocityEngineFactoryBean); + return velocityEngineFactoryBean; + } + + } + + @Configuration + @ConditionalOnClass(Servlet.class) + @ConditionalOnWebApplication + public static class VelocityWebConfiguration extends VelocityConfiguration { + + @Bean + @ConditionalOnMissingBean(VelocityConfig.class) + public VelocityConfigurer velocityConfigurer() { + VelocityConfigurer configurer = new VelocityConfigurer(); + applyProperties(configurer); + return configurer; + } + + @Bean + public VelocityEngine velocityEngine(VelocityConfigurer configurer) + throws VelocityException, IOException { + return configurer.createVelocityEngine(); + } + + @Bean + @ConditionalOnMissingBean(name = "velocityViewResolver") + public VelocityViewResolver velocityViewResolver() { + VelocityViewResolver resolver = new VelocityViewResolver(); + new TemplateViewResolverConfigurer().configureTemplateViewResolver(resolver, + this.properties); + resolver.setToolboxConfigLocation(this.properties.getToolboxConfigLocation()); + resolver.setDateToolAttribute(this.properties.getDateToolAttribute()); + resolver.setNumberToolAttribute(this.properties.getNumberToolAttribute()); + return resolver; + } + } +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityProperties.java new file mode 100644 index 0000000000..1c9c0450cb --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityProperties.java @@ -0,0 +1,94 @@ +/* + * Copyright 2012-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.boot.autoconfigure.velocity; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.boot.autoconfigure.template.AbstractTemplateViewResolverProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * {@link ConfigurationProperties} for configuring Velocity + * + * @author Andy Wilkinson + * + * @since 1.1.0 + */ +@ConfigurationProperties(prefix = "spring.velocity") +public class VelocityProperties extends AbstractTemplateViewResolverProperties { + + public static final String DEFAULT_RESOURCE_LOADER_PATH = "classpath:/templates/"; + + public static final String DEFAULT_PREFIX = ""; + + public static final String DEFAULT_SUFFIX = ".vm"; + + private String dateToolAttribute; + + private String numberToolAttribute; + + private Map properties = new HashMap(); + + private String resourceLoaderPath = DEFAULT_RESOURCE_LOADER_PATH; + + private String toolboxConfigLocation; + + public VelocityProperties() { + super(DEFAULT_PREFIX, DEFAULT_SUFFIX); + } + + public String getDateToolAttribute() { + return this.dateToolAttribute; + } + + public void setDateToolAttribute(String dateToolAttribute) { + this.dateToolAttribute = dateToolAttribute; + } + + public String getNumberToolAttribute() { + return this.numberToolAttribute; + } + + public void setNumberToolAttribute(String numberToolAttribute) { + this.numberToolAttribute = numberToolAttribute; + } + + public Map getProperties() { + return this.properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public String getResourceLoaderPath() { + return this.resourceLoaderPath; + } + + public void setResourceLoaderPath(String resourceLoaderPath) { + this.resourceLoaderPath = resourceLoaderPath; + } + + public String getToolboxConfigLocation() { + return this.toolboxConfigLocation; + } + + public void setToolboxConfigLocation(String toolboxConfigLocation) { + this.toolboxConfigLocation = toolboxConfigLocation; + } +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityTemplateAvailabilityProvider.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityTemplateAvailabilityProvider.java new file mode 100644 index 0000000000..79aa84de46 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityTemplateAvailabilityProvider.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-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.boot.autoconfigure.velocity; + +import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.ClassUtils; + +/** + * {@link TemplateAvailabilityProvider} that provides availability information for + * Velocity view templates + * + * @author Andy Wilkinson + * @since 1.1.0 + */ +public class VelocityTemplateAvailabilityProvider implements TemplateAvailabilityProvider { + + @Override + public boolean isTemplateAvailable(String view, Environment environment, + ClassLoader classLoader, ResourceLoader resourceLoader) { + if (ClassUtils.isPresent("org.apache.velocity.app.VelocityEngine", classLoader)) { + String loaderPath = environment.getProperty( + "spring.velocity.resourceLoaderPath", + VelocityProperties.DEFAULT_RESOURCE_LOADER_PATH); + String prefix = environment.getProperty("spring.velocity.prefix", + VelocityProperties.DEFAULT_PREFIX); + String suffix = environment.getProperty("spring.velocity.suffix", + VelocityProperties.DEFAULT_SUFFIX); + return resourceLoader.getResource(loaderPath + prefix + view + suffix) + .exists(); + } + return false; + } + +} diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index f3c9555e22..cdd3343ee3 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -31,6 +31,7 @@ org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\ org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\ +org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ @@ -46,4 +47,5 @@ org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\ +org.springframework.boot.autoconfigure.velocity.VelocityTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.web.JspTemplateAvailabilityProvider diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfigurationTests.java new file mode 100644 index 0000000000..c4682beb05 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfigurationTests.java @@ -0,0 +1,198 @@ +/* + * Copyright 2012-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.boot.autoconfigure.velocity; + +import java.io.File; +import java.io.StringWriter; +import java.util.Locale; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockServletContext; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.View; +import org.springframework.web.servlet.support.RequestContext; +import org.springframework.web.servlet.view.velocity.VelocityConfigurer; +import org.springframework.web.servlet.view.velocity.VelocityViewResolver; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link VelocityAutoConfiguration}. + * + * @author Andy Wilkinson + */ +public class VelocityAutoConfigurationTests { + + private AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + + @Before + public void setupContext() { + this.context.setServletContext(new MockServletContext()); + } + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void defaultConfiguration() { + registerAndRefreshContext(); + assertThat(this.context.getBean(VelocityViewResolver.class), notNullValue()); + assertThat(this.context.getBean(VelocityConfigurer.class), notNullValue()); + } + + @Test(expected = BeanCreationException.class) + public void nonExistentTemplateLocation() { + registerAndRefreshContext("spring.velocity.resourceLoaderPath:" + + "classpath:/does-not-exist/"); + } + + @Test + public void emptyTemplateLocation() { + new File("target/test-classes/templates/empty-directory").mkdir(); + registerAndRefreshContext("spring.velocity.resourceLoaderPath:" + + "classpath:/templates/empty-directory/"); + } + + @Test + public void defaultViewResolution() throws Exception { + registerAndRefreshContext(); + MockHttpServletResponse response = render("home"); + String result = response.getContentAsString(); + assertThat(result, containsString("home")); + assertThat(response.getContentType(), equalTo("text/html;charset=UTF-8")); + } + + @Test + public void customContentType() throws Exception { + registerAndRefreshContext("spring.velocity.contentType:application/json"); + MockHttpServletResponse response = render("home"); + String result = response.getContentAsString(); + assertThat(result, containsString("home")); + assertThat(response.getContentType(), equalTo("application/json;charset=UTF-8")); + } + + @Test + public void customPrefix() throws Exception { + registerAndRefreshContext("spring.velocity.prefix:prefix/"); + MockHttpServletResponse response = render("prefixed"); + String result = response.getContentAsString(); + assertThat(result, containsString("prefixed")); + } + + @Test + public void customSuffix() throws Exception { + registerAndRefreshContext("spring.velocity.suffix:.freemarker"); + MockHttpServletResponse response = render("suffixed"); + String result = response.getContentAsString(); + assertThat(result, containsString("suffixed")); + } + + @Test + public void customTemplateLoaderPath() throws Exception { + registerAndRefreshContext("spring.velocity.resourceLoaderPath:classpath:/custom-templates/"); + MockHttpServletResponse response = render("custom"); + String result = response.getContentAsString(); + assertThat(result, containsString("custom")); + } + + @Test + public void disableCache() { + registerAndRefreshContext("spring.velocity.cache:false"); + assertThat(this.context.getBean(VelocityViewResolver.class).getCacheLimit(), + equalTo(0)); + } + + @Test + public void customFreeMarkerSettings() { + registerAndRefreshContext("spring.velocity.properties.directive.parse.max.depth:10"); + assertThat(this.context.getBean(VelocityConfigurer.class).getVelocityEngine() + .getProperty("directive.parse.max.depth"), equalTo((Object) "10")); + } + + @Test + public void renderTemplate() throws Exception { + registerAndRefreshContext(); + VelocityConfigurer velocity = this.context.getBean(VelocityConfigurer.class); + StringWriter writer = new StringWriter(); + Template template = velocity.getVelocityEngine().getTemplate("message.vm"); + template.process(); + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("greeting", "Hello World"); + template.merge(velocityContext, writer); + assertThat(writer.toString(), containsString("Hello World")); + } + + @Test + public void renderNonWebAppTemplate() throws Exception { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + VelocityAutoConfiguration.class); + try { + VelocityEngine velocity = context.getBean(VelocityEngine.class); + StringWriter writer = new StringWriter(); + Template template = velocity.getTemplate("message.vm"); + template.process(); + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("greeting", "Hello World"); + template.merge(velocityContext, writer); + assertThat(writer.toString(), containsString("Hello World")); + } + finally { + context.close(); + } + } + + private void registerAndRefreshContext(String... env) { + EnvironmentTestUtils.addEnvironment(this.context, env); + this.context.register(VelocityAutoConfiguration.class); + this.context.refresh(); + } + + public String getGreeting() { + return "Hello World"; + } + + private MockHttpServletResponse render(String viewName) throws Exception { + VelocityViewResolver resolver = this.context.getBean(VelocityViewResolver.class); + View view = resolver.resolveViewName(viewName, Locale.UK); + assertThat(view, notNullValue()); + HttpServletRequest request = new MockHttpServletRequest(); + request.setAttribute(RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE, + this.context); + MockHttpServletResponse response = new MockHttpServletResponse(); + view.render(null, request, response); + return response; + } +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/velocity/VelocityTemplateAvailabilityProviderTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/velocity/VelocityTemplateAvailabilityProviderTests.java new file mode 100644 index 0000000000..1804a6c987 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/velocity/VelocityTemplateAvailabilityProviderTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-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.boot.autoconfigure.velocity; + +import org.junit.Test; +import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.ResourceLoader; +import org.springframework.mock.env.MockEnvironment; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link VelocityTemplateAvailabilityProvider}. + * + * @author Andy Wilkinson + */ +public class VelocityTemplateAvailabilityProviderTests { + + private final TemplateAvailabilityProvider provider = new VelocityTemplateAvailabilityProvider(); + + private final ResourceLoader resourceLoader = new DefaultResourceLoader(); + + private final MockEnvironment environment = new MockEnvironment(); + + @Test + public void availabilityOfTemplateInDefaultLocation() { + assertTrue(this.provider.isTemplateAvailable("home", this.environment, getClass() + .getClassLoader(), this.resourceLoader)); + } + + @Test + public void availabilityOfTemplateThatDoesNotExist() { + assertFalse(this.provider.isTemplateAvailable("whatever", this.environment, + getClass().getClassLoader(), this.resourceLoader)); + } + + @Test + public void availabilityOfTemplateWithCustomLoaderPath() { + this.environment.setProperty("spring.velocity.resourceLoaderPath", + "classpath:/custom-templates/"); + + assertTrue(this.provider.isTemplateAvailable("custom", this.environment, + getClass().getClassLoader(), this.resourceLoader)); + } + + @Test + public void availabilityOfTemplateWithCustomPrefix() { + this.environment.setProperty("spring.velocity.prefix", "prefix/"); + + assertTrue(this.provider.isTemplateAvailable("prefixed", this.environment, + getClass().getClassLoader(), this.resourceLoader)); + } + + @Test + public void availabilityOfTemplateWithCustomSuffix() { + this.environment.setProperty("spring.velocity.suffix", ".freemarker"); + + assertTrue(this.provider.isTemplateAvailable("suffixed", this.environment, + getClass().getClassLoader(), this.resourceLoader)); + } +} diff --git a/spring-boot-autoconfigure/src/test/resources/custom-templates/custom.vm b/spring-boot-autoconfigure/src/test/resources/custom-templates/custom.vm new file mode 100644 index 0000000000..8f4052a5c9 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/custom-templates/custom.vm @@ -0,0 +1 @@ +custom \ No newline at end of file diff --git a/spring-boot-autoconfigure/src/test/resources/templates/home.vm b/spring-boot-autoconfigure/src/test/resources/templates/home.vm new file mode 100644 index 0000000000..0247178b6e --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/templates/home.vm @@ -0,0 +1 @@ +home \ No newline at end of file diff --git a/spring-boot-autoconfigure/src/test/resources/templates/message.vm b/spring-boot-autoconfigure/src/test/resources/templates/message.vm new file mode 100644 index 0000000000..3908877ab6 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/templates/message.vm @@ -0,0 +1 @@ +Message: ${greeting} \ No newline at end of file diff --git a/spring-boot-autoconfigure/src/test/resources/templates/prefix/prefixed.vm b/spring-boot-autoconfigure/src/test/resources/templates/prefix/prefixed.vm new file mode 100644 index 0000000000..ba8db465da --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/templates/prefix/prefixed.vm @@ -0,0 +1 @@ +prefixed \ No newline at end of file diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index f76fa40804..b88e203436 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -114,6 +114,8 @@ 2.1.1.RELEASE 1.2.4 7.0.53 + 1.7 + 2.0 3.0.0 @@ -267,6 +269,11 @@ spring-boot-starter-tomcat 1.1.0.BUILD-SNAPSHOT + + org.springframework.boot + spring-boot-starter-velocity + ${spring-boot.version} + org.springframework.boot spring-boot-starter-web @@ -450,6 +457,16 @@ tomcat-jsp-api ${tomcat.version} + + org.apache.velocity + velocity + ${velocity.version} + + + org.apache.velocity + velocity-tools + ${velocity-tools.version} + org.aspectj aspectjrt diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index dc753604b9..c85ad208b1 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -94,6 +94,7 @@ content into your application; rather pick only the properties that you need. spring.freemarker.exposeSpringMacroHelpers=false spring.freemarker.prefix= spring.freemarker.requestContextAttribute= + spring.freemarker.settings.*= spring.freemarker.suffix=.ftl spring.freemarker.templateEncoding=UTF-8 spring.freemarker.templateLoaderPath=classpath:/templates/ @@ -103,17 +104,36 @@ content into your application; rather pick only the properties that you need. spring.groovy.template.allowRequestOverride=false spring.groovy.template.allowSessionOverride=false spring.groovy.template.cache=true + spring.groovy.template.configuration.*= # See Groovy's TemplateConfiguration spring.groovy.template.contentType=text/html spring.groovy.template.prefix=classpath:/templates/ spring.groovy.template.suffix=.tpl spring.groovy.template.templateEncoding=UTF-8 spring.groovy.template.viewNames= # whitelist of view names that can be resolved - spring.groovy.template.configuration.*= # See Groovy's TemplateConfiguration + + # VELOCITY TEMPLATES ({sc-spring-boot-autoconfigure}}/velocity/VelocityAutoConfiguration.{sc-ext}[VelocityAutoConfiguration]) + spring.velocity.allowRequestOverride=false + spring.velocity.allowSessionOverride=false + spring.velocity.cache=true + spring.velocity.checkTemplateLocation=true + spring.velocity.contentType=text/html + spring.velocity.dateToolAttribute= + spring.velocity.exposeRequestAttributes=false + spring.velocity.exposeSessionAttributes=false + spring.velocity.exposeSpringMacroHelpers=false + spring.velocity.numberToolAttribute= + spring.velocity.prefix= + spring.velocity.properties.*= + spring.velocity.requestContextAttribute= + spring.velocity.resourceLoaderPath=classpath:/templates/ + spring.velocity.suffix=.ftl + spring.velocity.templateEncoding=UTF-8 + spring.velocity.viewNames= # whitelist of view names that can be resolved # INTERNATIONALIZATION ({sc-spring-boot-autoconfigure}/MessageSourceAutoConfiguration.{sc-ext}[MessageSourceAutoConfiguration]) spring.messages.basename=messages + spring.messages.cacheSeconds=-1 spring.messages.encoding=UTF-8 - spring.messages.cacheSeconds=-1 [[common-application-properties-security]] # SECURITY ({sc-spring-boot-autoconfigure}/security/SecurityProperties.{sc-ext}[SecurityProperties]) diff --git a/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc b/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc index 3aef5ae839..4e30347f1d 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc @@ -41,6 +41,9 @@ The following auto-configuration classes are from the `spring-boot-autoconfigure |{sc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}[FreeMarkerAutoConfiguration] |{dc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{dc-ext}[javadoc] +|{sc-spring-boot-autoconfigure}/groovy/templates/GroovyTemplateAutoConfiguration.{sc-ext}[GroovyTemplateAutoConfiguration] +|{dc-spring-boot-autoconfigure}/groovy/templates/GroovyTemplateAutoConfiguration.{dc-ext}[javadoc] + |{sc-spring-boot-autoconfigure}/orm/jpa/HibernateJpaAutoConfiguration.{sc-ext}[HibernateJpaAutoConfiguration] |{dc-spring-boot-autoconfigure}/orm/jpa/HibernateJpaAutoConfiguration.{dc-ext}[javadoc] @@ -95,6 +98,9 @@ The following auto-configuration classes are from the `spring-boot-autoconfigure |{sc-spring-boot-autoconfigure}/thymeleaf/ThymeleafAutoConfiguration.{sc-ext}[ThymeleafAutoConfiguration] |{dc-spring-boot-autoconfigure}/thymeleaf/ThymeleafAutoConfiguration.{dc-ext}[javadoc] +|{sc-spring-boot-autoconfigure}/velocity/VelocityAutoConfiguration.{sc-ext}[VelocityAutoConfiguration] +|{dc-spring-boot-autoconfigure}/velocity/VelocityAutoConfiguration.{dc-ext}[javadoc] + |{sc-spring-boot-autoconfigure}/web/WebMvcAutoConfiguration.{sc-ext}[WebMvcAutoConfiguration] |{dc-spring-boot-autoconfigure}/web/WebMvcAutoConfiguration.{dc-ext}[javadoc] diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc index f48917e1d5..60da5cef0a 100644 --- a/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -813,20 +813,28 @@ added. ``freeMarkerViewResolver''. It looks for resources in a loader path (externalized to `spring.freemarker.templateLoaderPath`, default ``classpath:/templates/'') by surrounding the view name with a prefix and suffix (externalized to `spring.freemarker.prefix` - and `spring.freemarker.suffix`, defaults ``'' and ``.ftl'' respectively). It can be overriden + and `spring.freemarker.suffix`, with empty and ``.ftl'' defaults respectively). It can be overriden by providing a bean of the same name. -* If you use Groovy templates (actually if groovy-templates is on your classpath) you -also have a `Groovy TemplateViewResolver` with id +* If you use Groovy templates (actually if groovy-templates is on your classpath) you will + also have a `Groovy TemplateViewResolver` with id ``groovyTemplateViewResolver''. It looks for resources in a loader path by - surrounding the view name with a prefix and suffix (externalized to `spring.groovy.template.prefix` - and `spring.groovy.template.suffix`, defaults ``classpath:/templates/'' and ``.tpl'' respectively). It can be overriden - by providing a bean of the same name. + surrounding the view name with a prefix and suffix (externalized to + `spring.groovy.template.prefix` and `spring.groovy.template.suffix`, defaults + ``classpath:/templates/'' and ``.tpl'' respectively). It can be overriden by providing a bean of + the same name. + +* If you use Velocity you will also have a `VelocityViewResolver` with id ``velocityViewResolver''. + It looks for resources in a loader path (externalized to `spring.velocity.resourceLoaderPath`, + default ``classpath:/templates/'') by surrounding the view name with a prefix and suffix + (externalized to `spring.velocity.prefix' and `spring.velocity.suffix`, with empty and ``.vm'' + defaults respectively). It can be overridden by providing a bean of the same name. Check out {sc-spring-boot-autoconfigure}/web/WebMvcAutoConfiguration.{sc-ext}[`WebMvcAutoConfiguration`], {sc-spring-boot-autoconfigure}/thymeleaf/ThymeleafAutoConfiguration.{sc-ext}[`ThymeleafAutoConfiguration`], -{sc-spring-boot-autoconfigure}/groovy/template/GroovyTemplateAutoConfiguration.{sc-ext}['GroovyTemplateAutoConfiguration'] and -{sc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}['FreeMarkerAutoConfiguration'] +{sc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}[`FreeMarkerAutoConfiguration`], +{sc-spring-boot-autoconfigure}/groovy/template/GroovyTemplateAutoConfiguration.{sc-ext}[`GroovyTemplateAutoConfiguration`] and +{sc-spring-boot-autoconfigure}/velocity/VelocityAutoConfiguration.{sc-ext}[`VelocityAutoConfiguration`] @@ -1279,12 +1287,13 @@ The Actuator installs a ``whitelabel'' error page that you will see in browser c you encounter a server error (machine clients consuming JSON and other media types should see a sensible response with the right error code). To switch it off you can set `error.whitelabel.enabled=false`, but normally in addition or alternatively to that you -will want to add your own error page replacing the whitelabel one. If you are using -Thymeleaf or FreeMarker you can do this by adding an `error.html` or `error.ftl` template -respectively. In general what you need is a `View` that resolves with a name of `error`, -and/or a `@Controller` that handles the `/error` path. Unless you replaced some of the -default configuration you should find a `BeanNameViewResolver` in your `ApplicationContext` -so a `@Bean` with id `error` would be a simple way of doing that. +will want to add your own error page replacing the whitelabel one. Exactly how you do this +dependns on the templating technology that you are using. For example, if you are using +Thymeleaf you would add an `error.html` template and if you are using FreeMarker you would +add an `error.ftl` template. In general what you need is a `View` that resolves with a name +of `error`, and/or a `@Controller` that handles the `/error` path. Unless you replaced some +of the default configuration you should find a `BeanNameViewResolver` in your +`ApplicationContext` so a `@Bean` with id `error` would be a simple way of doing that. Look at {sc-spring-boot-autoconfigure}/web/ErrorMvcAutoConfiguration.{sc-ext}[`ErrorMvcAutoConfiguration`] for more options. @@ -1403,6 +1412,13 @@ If you are using Groovy templates, then set `spring.groovy.template.cache` to `f for other Groovy customization options. +[[howto-reload-velocity-content]] +=== Reload Velocity templates without restarting the container +If you are using Velocity, then set `spring.velocity.cache` to `false`. See +{sc-spring-boot-autoconfigure}/velocity/VelocityAutoConfiguration.{sc-ext}[`VelocityAutoConfiguration`] +for other Velocity customization options. + + [[howto-reload-java-classes-without-restarting]] === Reload Java classes without restarting the container Modern IDEs (Eclipse, IDEA, etc.) all support hot swapping of bytecode, so if you make a diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index a10fd3b6de..7142b94e97 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -906,27 +906,26 @@ and it will be silently ignored by most build tools if you generate a jar. [[boot-features-spring-mvc-template-engines]] ==== Template engines -As well as REST web services, you can also use Spring MVC to serve -dynamic HTML content. Spring MVC supports a variety of templating -technologies including: Velocity, FreeMarker and JSPs. Many +As well as REST web services, you can also use Spring MVC to serve dynamic HTML content. Spring +MVC supports a variety of templating technologies including Velocity, FreeMarker and JSPs. Many other templating engines also ship their own Spring MVC integrations. -Spring Boot includes auto-configuration support for the Thymeleaf, -[FreeMarker](http://freemarker.org/docs/) and -[Groovy](http://beta.groovy-lang.org/docs/groovy-2.3.0/html/documentation/markup-template-engine.html) -templating engines. Thymeleaf is an XML/XHTML/HTML5 template engine -that can work both in web and non-web environments. If allows you to -create natural templates that can be correctly displayed by browsers -and therefore work also as static prototypes. Your FreeMarker, Groovy -and Thymeleaf templates will be picked up automatically from -`src/main/resources/templates`. +Spring Boot includes auto-configuration support for the following templating engines: + - http://freemarker.org/docs/[FreeMarker] + - http://beta.groovy-lang.org/docs/groovy-2.3.0/html/documentation/markup-template-engine.html[Groovy] + - http://www.thymeleaf.org[Thymeleaf] + - http://velocity.apache.org[Velocity] + +When you're using one of these templating engines with the default configuration, your templates +will be picked up automatically from `src/main/resources/templates`. TIP: JSPs should be avoided if possible, there are several <> when using them with embedded servlet containers. + [[boot-features-error-handling]] ==== Error Handling Spring Boot provides an `/error` mapping by default that handles all errors in a diff --git a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc index ef4efd4ff0..177277d5eb 100644 --- a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc +++ b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc @@ -258,6 +258,9 @@ and Hibernate. |`spring-boot-starter-thymeleaf` |Support for the Thymeleaf templating engine, including integration with Spring. +|`spring-boot-starter-velocity` +|Support for the Velocity templating engine + |`spring-boot-starter-web` |Support for full-stack web development, including Tomcat and `spring-webmvc`. diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index b44e8ca3af..9dadffac02 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -49,6 +49,7 @@ spring-boot-sample-web-static spring-boot-sample-web-jsp spring-boot-sample-web-ui + spring-boot-sample-web-velocity spring-boot-sample-websocket spring-boot-sample-xml diff --git a/spring-boot-samples/spring-boot-sample-web-velocity/pom.xml b/spring-boot-samples/spring-boot-sample-web-velocity/pom.xml new file mode 100644 index 0000000000..b99c7392b2 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-web-velocity/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 1.1.0.BUILD-SNAPSHOT + + spring-boot-sample-web-velocity + Spring Boot Web Velocity Sample + Spring Boot Web Velocity Sample + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + / + + + + org.springframework.boot + spring-boot-starter-velocity + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + diff --git a/spring-boot-samples/spring-boot-sample-web-velocity/src/main/java/sample/velocity/SampleWebVelocityApplication.java b/spring-boot-samples/spring-boot-sample-web-velocity/src/main/java/sample/velocity/SampleWebVelocityApplication.java new file mode 100644 index 0000000000..3c51c49cc0 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-web-velocity/src/main/java/sample/velocity/SampleWebVelocityApplication.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012-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 sample.velocity; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableAutoConfiguration +@ComponentScan +public class SampleWebVelocityApplication { + + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleWebVelocityApplication.class, args); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-web-velocity/src/main/java/sample/velocity/WelcomeController.java b/spring-boot-samples/spring-boot-sample-web-velocity/src/main/java/sample/velocity/WelcomeController.java new file mode 100644 index 0000000000..0c9f449233 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-web-velocity/src/main/java/sample/velocity/WelcomeController.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-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 sample.velocity; + +import java.util.Date; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class WelcomeController { + + @Value("${application.message:Hello World}") + private String message = "Hello World"; + + @RequestMapping("/") + public String welcome(Map model) { + model.put("time", new Date()); + model.put("message", this.message); + return "welcome"; + } + +} diff --git a/spring-boot-samples/spring-boot-sample-web-velocity/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-web-velocity/src/main/resources/application.properties new file mode 100644 index 0000000000..11927c0966 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-web-velocity/src/main/resources/application.properties @@ -0,0 +1,2 @@ +application.message: Hello, Andy +spring.velocity.dateToolAttribute: dateTool \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-web-velocity/src/main/resources/templates/error.vm b/spring-boot-samples/spring-boot-sample-web-velocity/src/main/resources/templates/error.vm new file mode 100644 index 0000000000..ace4211012 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-web-velocity/src/main/resources/templates/error.vm @@ -0,0 +1,9 @@ + + + + + + Something went wrong: ${status} ${error} + + + diff --git a/spring-boot-samples/spring-boot-sample-web-velocity/src/main/resources/templates/welcome.vm b/spring-boot-samples/spring-boot-sample-web-velocity/src/main/resources/templates/welcome.vm new file mode 100644 index 0000000000..85f409d0f0 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-web-velocity/src/main/resources/templates/welcome.vm @@ -0,0 +1,14 @@ + + + + + + Time: +
    +
  • From controller: $time
  • +
  • From velocity: $dateTool
  • +
+ Message: $message + + + diff --git a/spring-boot-samples/spring-boot-sample-web-velocity/src/test/java/sample/velocity/SampleWebVelocityApplicationTests.java b/spring-boot-samples/spring-boot-sample-web-velocity/src/test/java/sample/velocity/SampleWebVelocityApplicationTests.java new file mode 100644 index 0000000000..b3032d8623 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-web-velocity/src/test/java/sample/velocity/SampleWebVelocityApplicationTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2012-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 sample.velocity; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.boot.test.TestRestTemplate; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Basic integration tests for Velocity application. + * + * @author Phillip Webb + * @author Andy Wilkinson + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = SampleWebVelocityApplication.class) +@WebAppConfiguration +@IntegrationTest("server.port=0") +@DirtiesContext +public class SampleWebVelocityApplicationTests { + + @Value("${local.server.port}") + private int port; + + @Test + public void testVelocityTemplate() throws Exception { + ResponseEntity entity = new TestRestTemplate().getForEntity( + "http://localhost:" + this.port, String.class); + assertEquals(HttpStatus.OK, entity.getStatusCode()); + assertTrue("Wrong body:\n" + entity.getBody(), + entity.getBody().contains("Hello, Andy")); + } + + @Test + public void testVelocityErrorTemplate() throws Exception { + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); + HttpEntity requestEntity = new HttpEntity(headers); + + ResponseEntity responseEntity = new TestRestTemplate().exchange( + "http://localhost:" + this.port + "/does-not-exist", HttpMethod.GET, + requestEntity, String.class); + + assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode()); + assertTrue("Wrong body:\n" + responseEntity.getBody(), responseEntity.getBody() + .contains("Something went wrong: 404 Not Found")); + } + +} diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index 5edd70fc8b..65a2fe3442 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -43,6 +43,7 @@ spring-boot-starter-test spring-boot-starter-thymeleaf spring-boot-starter-tomcat + spring-boot-starter-velocity spring-boot-starter-web spring-boot-starter-websocket diff --git a/spring-boot-starters/spring-boot-starter-velocity/pom.xml b/spring-boot-starters/spring-boot-starter-velocity/pom.xml new file mode 100644 index 0000000000..eba972e381 --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-velocity/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starters + 1.1.0.BUILD-SNAPSHOT + + spring-boot-starter-velocity + Spring Boot Velocity Starter + Spring Boot Velocity Starter + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.velocity + velocity + + + org.apache.velocity + velocity-tools + + + org.springframework + spring-context-support + + +