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
+
+
+