Commit 7c911761 authored by Andy Wilkinson's avatar Andy Wilkinson

Add FreeMarker support

This commit adds auto-configuration and a starter,
spring-boot-starter-freemarker, for using FreeMarker view templates in
a web application.

A new abstraction, TemplateAvailabilityProvider, has been introduced.
This decouples ErrorMvcAutoConfiguration from the various view
technologies that Spring Boot now supports, allowing it to determine
when a custom error template is provided without knowing the details of
each view technology.

Closes #679
parent 1143f6db
......@@ -66,6 +66,11 @@
<artifactId>jetty-webapp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
......
/*
* 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.freemarker;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.servlet.Servlet;
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.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
/**
* {@link EnableAutoConfiguration Auto-configuration} for FreeMarker.
*
* @author Andy Wilkinson
*/
@Configuration
@ConditionalOnClass(freemarker.template.Configuration.class)
@ConditionalOnWebApplication
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
public class FreeMarkerAutoConfiguration {
public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/";
public static final String DEFAULT_PREFIX = "";
public static final String DEFAULT_SUFFIX = ".ftl";
@Configuration
public static class FreemarkerConfigurerConfiguration implements EnvironmentAware {
@Autowired
private final ResourceLoader resourceLoader = new DefaultResourceLoader();
private RelaxedPropertyResolver environment;
@Override
public void setEnvironment(Environment environment) {
this.environment = new RelaxedPropertyResolver(environment,
"spring.freeMarker.");
}
@PostConstruct
public void checkTemplateLocationExists() {
Boolean checkTemplateLocation = this.environment.getProperty(
"checkTemplateLocation", Boolean.class, true);
if (checkTemplateLocation) {
Resource resource = this.resourceLoader.getResource(this.environment
.getProperty("templateLoaderPath", DEFAULT_TEMPLATE_LOADER_PATH));
Assert.state(resource.exists(), "Cannot find template location: "
+ resource + " (please add some templates "
+ "or check your FreeMarker configuration)");
}
}
@Bean
@ConditionalOnMissingBean(name = "freeMarkerConfigurer")
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
freeMarkerConfigurer.setTemplateLoaderPath(this.environment.getProperty(
"templateLoaderPath", DEFAULT_TEMPLATE_LOADER_PATH));
freeMarkerConfigurer.setDefaultEncoding(this.environment.getProperty(
"templateEncoding", "UTF-8"));
Map<String, Object> settingsMap = this.environment
.getSubProperties("settings.");
Properties settings = new Properties();
settings.putAll(settingsMap);
freeMarkerConfigurer.setFreemarkerSettings(settings);
return freeMarkerConfigurer;
}
}
@Configuration
@ConditionalOnClass(Servlet.class)
public static class FreemarkerViewResolverConfiguration implements EnvironmentAware {
private RelaxedPropertyResolver environment;
@Override
public void setEnvironment(Environment environment) {
this.environment = new RelaxedPropertyResolver(environment,
"spring.freeMarker.");
}
@Bean
@ConditionalOnMissingBean(name = "freeMarkerViewResolver")
public FreeMarkerViewResolver freeMarkerViewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setPrefix(this.environment.getProperty("prefix", DEFAULT_PREFIX));
resolver.setSuffix(this.environment.getProperty("suffix", DEFAULT_SUFFIX));
resolver.setCache(this.environment.getProperty("cache", Boolean.class, true));
resolver.setContentType(this.environment.getProperty("contentType",
"text/html"));
resolver.setViewNames(this.environment.getProperty("viewNames",
String[].class));
resolver.setExposeRequestAttributes(this.environment.getProperty(
"exposeRequestAttributes", Boolean.class, false));
resolver.setAllowRequestOverride(this.environment.getProperty(
"allowRequestOverride", Boolean.class, false));
resolver.setExposeSessionAttributes(this.environment.getProperty(
"exposeSessionAttributes", Boolean.class, false));
resolver.setAllowSessionOverride(this.environment.getProperty(
"allowSessionOverride", Boolean.class, false));
resolver.setExposeSpringMacroHelpers(this.environment.getProperty(
"exposeSpringMacroHelpers", Boolean.class, false));
resolver.setRequestContextAttribute(this.environment
.getProperty("requestContextAttribute"));
// 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);
return resolver;
}
}
}
/*
* 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.freemarker;
import org.springframework.boot.web.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 FreeMarker view templates
*
* @author Andy Wilkinson
*
*/
public class FreeMarkerTemplateAvailabilityProvider implements
TemplateAvailabilityProvider {
@Override
public boolean isTemplateAvailable(String view, Environment environment,
ClassLoader classLoader, ResourceLoader resourceLoader) {
if (ClassUtils.isPresent("freemarker.template.Configuration",
classLoader)) {
String loaderPath = environment.getProperty("spring.freemarker.templateLoaderPath",
FreeMarkerAutoConfiguration.DEFAULT_TEMPLATE_LOADER_PATH);
String prefix = environment.getProperty("spring.freemarker.prefix",
FreeMarkerAutoConfiguration.DEFAULT_PREFIX);
String suffix = environment.getProperty("spring.freemarker.suffix",
FreeMarkerAutoConfiguration.DEFAULT_SUFFIX);
return resourceLoader.getResource(loaderPath + prefix + view + suffix).exists();
}
return false;
}
}
......@@ -50,8 +50,9 @@ import org.thymeleaf.templateresolver.TemplateResolver;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Thymeleaf.
*
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@Configuration
@ConditionalOnClass(SpringTemplateEngine.class)
......@@ -108,16 +109,6 @@ public class ThymeleafAutoConfiguration {
protected SpringResourceResourceResolver thymeleafResourceResolver() {
return new SpringResourceResourceResolver();
}
public static boolean templateExists(Environment environment,
ResourceLoader resourceLoader, String view) {
String prefix = environment.getProperty("spring.thymeleaf.prefix",
ThymeleafAutoConfiguration.DEFAULT_PREFIX);
String suffix = environment.getProperty("spring.thymeleaf.suffix",
ThymeleafAutoConfiguration.DEFAULT_SUFFIX);
return resourceLoader.getResource(prefix + view + suffix).exists();
}
}
@Configuration
......
/*
* 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.thymeleaf;
import org.springframework.boot.web.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
* Thymeleaf view templates
*
* @author Andy Wilkinson
*
*/
public class ThymeleafTemplateAvailabilityProvider implements
TemplateAvailabilityProvider {
@Override
public boolean isTemplateAvailable(String view, Environment environment,
ClassLoader classLoader, ResourceLoader resourceLoader) {
if (ClassUtils.isPresent("org.thymeleaf.spring4.SpringTemplateEngine",
classLoader)) {
String prefix = environment.getProperty("spring.thymeleaf.prefix",
ThymeleafAutoConfiguration.DEFAULT_PREFIX);
String suffix = environment.getProperty("spring.thymeleaf.suffix",
ThymeleafAutoConfiguration.DEFAULT_SUFFIX);
return resourceLoader.getResource(prefix + view + suffix).exists();
}
return false;
}
}
......@@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.web;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Servlet;
......@@ -33,21 +34,21 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration.DefaultTemplateResolverConfiguration;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.ErrorPage;
import org.springframework.boot.web.TemplateAvailabilityProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.expression.MapAccessor;
import org.springframework.core.Ordered;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
import org.springframework.web.servlet.DispatcherServlet;
......@@ -57,8 +58,9 @@ import org.springframework.web.servlet.view.BeanNameViewResolver;
/**
* {@link EnableAutoConfiguration Auto-configuration} to render errors via a MVC error
* controller.
*
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnWebApplication
......@@ -116,22 +118,20 @@ public class ErrorMvcAutoConfiguration implements EmbeddedServletContainerCustom
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
if (ClassUtils.isPresent("org.thymeleaf.spring4.SpringTemplateEngine",
context.getClassLoader())) {
if (DefaultTemplateResolverConfiguration.templateExists(
context.getEnvironment(), context.getResourceLoader(), "error")) {
return ConditionOutcome
.noMatch("Thymeleaf template found for error view");
}
}
if (ClassUtils.isPresent("org.apache.jasper.compiler.JspConfig",
context.getClassLoader())) {
if (WebMvcAutoConfiguration.templateExists(context.getEnvironment(),
context.getResourceLoader(), "error")) {
return ConditionOutcome.noMatch("JSP template found for error view");
List<TemplateAvailabilityProvider> availabilityProviders = SpringFactoriesLoader
.loadFactories(TemplateAvailabilityProvider.class,
context.getClassLoader());
for (TemplateAvailabilityProvider availabilityProvider : availabilityProviders) {
if (availabilityProvider.isTemplateAvailable("error",
context.getEnvironment(), context.getClassLoader(),
context.getResourceLoader())) {
return ConditionOutcome.noMatch("Template from "
+ availabilityProvider + " found for error view");
}
}
return ConditionOutcome.match("no error template view detected");
return ConditionOutcome.match("No error template view detected");
};
}
......
/*
* 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.web;
import org.springframework.boot.web.TemplateAvailabilityProvider;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ClassUtils;
public class JspTemplateAvailabilityProvider implements TemplateAvailabilityProvider {
@Override
public boolean isTemplateAvailable(String view, Environment environment,
ClassLoader classLoader, ResourceLoader resourceLoader) {
if (ClassUtils.isPresent("org.apache.jasper.compiler.JspConfig", classLoader)) {
String prefix = environment.getProperty("spring.view.prefix",
WebMvcAutoConfiguration.DEFAULT_PREFIX);
String suffix = environment.getProperty("spring.view.suffix",
WebMvcAutoConfiguration.DEFAULT_SUFFIX);
return resourceLoader.getResource(prefix + view + suffix).exists();
}
return false;
}
}
......@@ -43,7 +43,6 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
......@@ -75,6 +74,7 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver;
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
*/
@Configuration
@ConditionalOnWebApplication
......@@ -118,15 +118,6 @@ public class WebMvcAutoConfiguration {
return new HiddenHttpMethodFilter();
}
public static boolean templateExists(Environment environment,
ResourceLoader resourceLoader, String view) {
String prefix = environment.getProperty("spring.view.prefix",
WebMvcAutoConfiguration.DEFAULT_PREFIX);
String suffix = environment.getProperty("spring.view.suffix",
WebMvcAutoConfiguration.DEFAULT_SUFFIX);
return resourceLoader.getResource(prefix + view + suffix).exists();
}
// Defined as a nested config to ensure WebMvcConfigurerAdapter it not read when not
// on the classpath
@Configuration
......
......@@ -11,9 +11,9 @@ org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsTemplateAutoConfiguration,\
......@@ -23,6 +23,7 @@ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
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.thymeleaf.ThymeleafAutoConfiguration,\
......@@ -34,3 +35,10 @@ org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguratio
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration
# Template availability providers
org.springframework.boot.web.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.JspTemplateAvailabilityProvider
/*
* 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.freemarker;
import java.io.File;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
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.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.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link FreeMarkerAutoConfiguration}.
*
* @author Andy Wilkinson
*/
public class FreeMarkerAutoConfigurationTests {
private AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
@Before
public void registerServletContext() {
MockServletContext servletContext = new MockServletContext();
this.context.setServletContext(servletContext);
}
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void defaultConfiguration() {
this.context.register(FreeMarkerAutoConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(FreeMarkerViewResolver.class));
assertNotNull(this.context.getBean(FreeMarkerConfigurer.class));
}
@Test(expected = BeanCreationException.class)
public void nonExistentTemplateLocation() {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.freemarker.templateLoaderPath:classpath:/does-not-exist/");
this.context.register(FreeMarkerAutoConfiguration.class);
this.context.refresh();
}
@Test
public void emptyTemplateLocation() {
new File("target/test-classes/templates/empty-directory").mkdir();
EnvironmentTestUtils
.addEnvironment(this.context,
"spring.freemarker.templateLoaderPath:classpath:/templates/empty-directory/");
this.context.register(FreeMarkerAutoConfiguration.class);
this.context.refresh();
}
@Test
public void defaultViewResolution() throws Exception {
this.context.register(FreeMarkerAutoConfiguration.class);
this.context.refresh();
MockHttpServletResponse response = render("home");
String result = response.getContentAsString();
assertTrue("Wrong output: " + result, result.contains("home"));
assertEquals("text/html", response.getContentType());
}
@Test
public void customContentType() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.freemarker.contentType:application/json");
this.context.register(FreeMarkerAutoConfiguration.class);
this.context.refresh();
MockHttpServletResponse response = render("home");
String result = response.getContentAsString();
assertTrue("Wrong output: " + result, result.contains("home"));
assertEquals("application/json", response.getContentType());
}
@Test
public void customPrefix() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.freemarker.prefix:prefix/");
this.context.register(FreeMarkerAutoConfiguration.class);
this.context.refresh();
MockHttpServletResponse response = render("prefixed");
String result = response.getContentAsString();
assertTrue("Wrong output: " + result, result.contains("prefixed"));
}
@Test
public void customSuffix() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.freemarker.suffix:.freemarker");
this.context.register(FreeMarkerAutoConfiguration.class);
this.context.refresh();
MockHttpServletResponse response = render("suffixed");
String result = response.getContentAsString();
assertTrue("Wrong output: " + result, result.contains("suffixed"));
}
@Test
public void customTemplateLoaderPath() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.freemarker.templateLoaderPath:classpath:/custom-templates/");
this.context.register(FreeMarkerAutoConfiguration.class);
this.context.refresh();
MockHttpServletResponse response = render("custom");
String result = response.getContentAsString();
assertTrue("Wrong output: " + result, result.contains("custom"));
}
@Test
public void disableCache() {
EnvironmentTestUtils
.addEnvironment(this.context, "spring.freemarker.cache:false");
this.context.register(FreeMarkerAutoConfiguration.class);
this.context.refresh();
assertEquals(0, this.context.getBean(FreeMarkerViewResolver.class)
.getCacheLimit());
}
@SuppressWarnings("deprecation")
@Test
public void customFreeMarkerSettings() {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.freemarker.settings.boolean_format:yup,nope");
this.context.register(FreeMarkerAutoConfiguration.class);
this.context.refresh();
assertEquals("yup,nope", this.context.getBean(FreeMarkerConfigurer.class)
.getConfiguration().getSetting("boolean_format"));
}
private MockHttpServletResponse render(String viewName) throws Exception {
View view = this.context.getBean(FreeMarkerViewResolver.class).resolveViewName(
viewName, Locale.UK);
assertNotNull(view);
HttpServletRequest request = new MockHttpServletRequest();
request.setAttribute(RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE,
this.context);
MockHttpServletResponse response = new MockHttpServletResponse();
view.render(null, request, response);
return response;
}
}
/*
* 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.freemarker;
import org.junit.Test;
import org.springframework.boot.web.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 FreeMarkerTemplateAvailabilityProvider}.
*
* @author Andy Wilkinson
*/
public class FreeMarkerTemplateAvailabilityProviderTests {
private final TemplateAvailabilityProvider provider = new FreeMarkerTemplateAvailabilityProvider();
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.freemarker.templateLoaderPath",
"classpath:/custom-templates/");
assertTrue(this.provider.isTemplateAvailable("custom", this.environment,
getClass().getClassLoader(), this.resourceLoader));
}
@Test
public void availabilityOfTemplateWithCustomPrefix() {
this.environment.setProperty("spring.freemarker.prefix", "prefix/");
assertTrue(this.provider.isTemplateAvailable("prefixed", this.environment,
getClass().getClassLoader(), this.resourceLoader));
}
@Test
public void availabilityOfTemplateWithCustomSuffix() {
this.environment.setProperty("spring.freemarker.suffix", ".freemarker");
assertTrue(this.provider.isTemplateAvailable("suffixed", this.environment,
getClass().getClassLoader(), this.resourceLoader));
}
}
/*
* 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.thymeleaf;
import org.junit.Test;
import org.springframework.boot.web.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 ThymeleafTemplateAvailabilityProvider}.
*
* @author Andy Wilkinson
*/
public class ThymeleafTemplateAvailabilityProviderTests {
private final TemplateAvailabilityProvider provider = new ThymeleafTemplateAvailabilityProvider();
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 availabilityOfTemplateWithCustomPrefix() {
this.environment.setProperty("spring.thymeleaf.prefix",
"classpath:/custom-templates/");
assertTrue(this.provider.isTemplateAvailable("custom", this.environment,
getClass().getClassLoader(), this.resourceLoader));
}
@Test
public void availabilityOfTemplateWithCustomSuffix() {
this.environment.setProperty("spring.thymeleaf.suffix", ".thymeleaf");
assertTrue(this.provider.isTemplateAvailable("suffixed", this.environment,
getClass().getClassLoader(), this.resourceLoader));
}
}
......@@ -51,6 +51,7 @@
<commons-dbcp.version>1.4</commons-dbcp.version>
<commons-pool.version>1.6</commons-pool.version>
<crashub.version>1.3.0-beta14</crashub.version>
<freemarker.version>2.3.20</freemarker.version>
<gemfire.version>7.0.1</gemfire.version>
<gradle.version>1.6</gradle.version>
<groovy.version>2.3.0-rc-2</groovy.version>
......@@ -351,6 +352,11 @@
<artifactId>gemfire</artifactId>
<version>${gemfire.version}</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
......
......@@ -80,6 +80,22 @@ content into your application; rather pick only the properties that you need.
spring.thymeleaf.content-type=text/html # ;charset=<encoding> is added
spring.thymeleaf.cache=true # set to false for hot refresh
# FREEMARKER ({sc-spring-boot-autoconfigure}}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}[FreeMarkerAutoConfiguration])
spring.freemarker.allowRequestOverride=false
spring.freemarker.allowSessionOverride=false
spring.freemarker.cache=true
spring.freemarker.checkTemplateLocation=true
spring.freemarker.contentType=text/html
spring.freemarker.exposeRequestAttributes=false
spring.freemarker.exposeSessionAttributes=false
spring.freemarker.exposeSpringMacroHelpers=false
spring.freemarker.prefix=
spring.freemarker.requestContextAttribute=
spring.freemarker.suffix=
spring.freemarker.templateEncoding=UTF-8
spring.freemarker.templateLoaderPath=classpath:/templates/
spring.freemarker.viewNames=
# INTERNATIONALIZATION ({sc-spring-boot-autoconfigure}/MessageSourceAutoConfiguration.{sc-ext}[MessageSourceAutoConfiguration])
spring.messages.basename=messages
spring.messages.encoding=UTF-8
......
......@@ -38,6 +38,9 @@ The following auto-configuration classes are from the `spring-boot-autoconfigure
|{sc-spring-boot-autoconfigure}/web/EmbeddedServletContainerAutoConfiguration.{sc-ext}[EmbeddedServletContainerAutoConfiguration]
|{dc-spring-boot-autoconfigure}/web/EmbeddedServletContainerAutoConfiguration.{dc-ext}[javadoc]
|{sc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}[FreeMarkerAutoConfiguration]
|{dc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{dc-ext}[javadoc]
|{sc-spring-boot-autoconfigure}/orm/jpa/HibernateJpaAutoConfiguration.{sc-ext}[HibernateJpaAutoConfiguration]
|{dc-spring-boot-autoconfigure}/orm/jpa/HibernateJpaAutoConfiguration.{dc-ext}[javadoc]
......
......@@ -809,8 +809,16 @@ added.
`spring.thymeleaf.suffix`, defaults ``classpath:/templates/'' and ``.html''
respectively). It can be overridden by providing a bean of the same name.
Checkout {sc-spring-boot-autoconfigure}/web/WebMvcAutoConfiguration.{sc-ext}[`WebMvcAutoConfiguration`]
and {sc-spring-boot-autoconfigure}/thymeleaf/ThymeleafAutoConfiguration.{sc-ext}[`ThymeleafAutoConfiguration`]
* If you use FreeMarker you will also have a `FreeMarkerViewResolver` with id
``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
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`], and
{sc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}['FreeMarkerAutoConfiguration']
......@@ -1145,11 +1153,11 @@ you encounter a server error (machine clients consuming JSON and other media typ
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 you can do this by adding an `error.html` 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.
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.
Look at {sc-spring-boot-autoconfigure}/web/ErrorMvcAutoConfiguration.{sc-ext}[`ErrorMvcAutoConfiguration`] for more options.
......@@ -1251,7 +1259,15 @@ tools.
=== Reload Thymeleaf templates without restarting the container
If you are using Thymeleaf, then set `spring.thymeleaf.cache` to `false`. See
{sc-spring-boot-autoconfigure}/thymeleaf/ThymeleafAutoConfiguration.{sc-ext}[`ThymeleafAutoConfiguration`]
for other template customization options.
for other Thymeleaf customization options.
[[howto-reload-freemarker-content]]
=== Reload FreeMarker templates without restarting the container
If you are using FreeMarker, then set `spring.freemarker.cache` to `false`. See
{sc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}[`FreeMarkerAutoConfiguration`]
for other FreeMarker customization options.
......
= Spring Boot Reference Guide
Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch;
Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson;
:doctype: book
:toc:
:toclevels: 4
......
......@@ -892,14 +892,16 @@ 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,
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 templating engine.
Thymeleaf is an XML/XHTML/HTML5 template engine that can work both in web and non-web
environments. It allows you to create natural templates that can be correctly displayed
by browsers and therefore work also as static prototypes. Thymeleaf templates will be
picked up automatically from `src/main/resources/templates`.
Spring Boot includes auto-configuration support for the Thymeleaf and FreeMarker
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. Both
FreeMarker and Thymeleaf templates will be picked up automatically from
`src/main/resources/templates`.
TIP: JSPs should be avoided if possible, there are several
<<boot-features-jsp-limitations, known limitations>> when using them with embedded
......
......@@ -229,6 +229,9 @@ and Hibernate.
|`spring-boot-starter-data-rest`
|Support for exposing Spring Data repositories over REST via `spring-data-rest-webmvc`.
|`spring-boot-starter-freemarker`
|Support for the FreeMarker templating engine
|`spring-boot-starter-integration`
|Support for common `spring-integration` modules.
......
......@@ -41,6 +41,7 @@
<module>spring-boot-sample-tomcat</module>
<module>spring-boot-sample-tomcat-multi-connectors</module>
<module>spring-boot-sample-traditional</module>
<module>spring-boot-sample-web-freemarker</module>
<module>spring-boot-sample-web-method-security</module>
<module>spring-boot-sample-web-secure</module>
<module>spring-boot-sample-web-static</module>
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-web-freemarker</artifactId>
<name>Spring Boot Web FreeMarker Sample</name>
<description>Spring Boot Web FreeMarker Sample</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
<m2eclipse.wtp.contextRoot>/</m2eclipse.wtp.contextRoot>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
</plugins>
</build>
</project>
/*
* 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.freemarker;
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 SampleWebFreeMarkerApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleWebFreeMarkerApplication.class, args);
}
}
/*
* 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.freemarker;
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<String, Object> model) {
model.put("time", new Date());
model.put("message", this.message);
return "welcome";
}
}
<!DOCTYPE html>
<html lang="en">
<body>
Something went wrong: ${status} ${error}
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<body>
Date: ${time?date}
<br>
Time: ${time?time}
<br>
Message: ${message}
</body>
</html>
/*
* 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.freemarker;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
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 FreeMarker application.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleWebFreeMarkerApplication.class)
@WebAppConfiguration
@IntegrationTest
@DirtiesContext
public class SampleWebFreeMarkerApplicationTests {
@Test
public void testFreeMarkerTemplate() throws Exception {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:8080", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body:\n" + entity.getBody(),
entity.getBody().contains("Hello, Andy"));
}
@Test
public void testFreeMarkerErrorTemplate() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
HttpEntity<String> requestEntity = new HttpEntity<String>(headers);
ResponseEntity<String> responseEntity = new TestRestTemplate().exchange(
"http://localhost:8080/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"));
}
}
......@@ -29,6 +29,7 @@
<module>spring-boot-starter-data-mongodb</module>
<module>spring-boot-starter-data-neo4j</module>
<module>spring-boot-starter-data-rest</module>
<module>spring-boot-starter-freemarker</module>
<module>spring-boot-starter-integration</module>
<module>spring-boot-starter-jdbc</module>
<module>spring-boot-starter-jetty</module>
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-starter-freemarker</artifactId>
<name>Spring Boot FreeMarker Starter</name>
<description>Spring Boot FreeMarker Starter</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
</dependencies>
</project>
provides: freemarker,spring-context-support
\ No newline at end of file
......@@ -101,6 +101,11 @@
<artifactId>spring-boot-starter-data-rest</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
......
/*
* 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.web;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
/**
* A {@code TemplateAvailabilityProvider} indicates the availability of view templates for
* a particular templating engine such as FreeMarker or Thymeleaf
*
* @author Andy Wilkinson
*
*/
public interface TemplateAvailabilityProvider {
/**
* Returns {@code true} if a template is available for the given {@code view},
* otherwise {@code false} is returned.
*/
boolean isTemplateAvailable(String view, Environment environment,
ClassLoader classLoader, ResourceLoader resourceLoader);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment