001/*
002 * Copyright 2012-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.autoconfigure.thymeleaf;
018
019import java.lang.reflect.Method;
020import java.util.Collection;
021
022import javax.servlet.Servlet;
023
024import com.github.mxab.thymeleaf.extras.dataattribute.dialect.DataAttributeDialect;
025import nz.net.ultraq.thymeleaf.LayoutDialect;
026import org.thymeleaf.dialect.IDialect;
027import org.thymeleaf.extras.conditionalcomments.dialect.ConditionalCommentsDialect;
028import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
029import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
030import org.thymeleaf.spring4.SpringTemplateEngine;
031import org.thymeleaf.spring4.resourceresolver.SpringResourceResourceResolver;
032import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
033import org.thymeleaf.spring4.view.ThymeleafViewResolver;
034import org.thymeleaf.templateresolver.ITemplateResolver;
035
036import org.springframework.beans.factory.ObjectProvider;
037import org.springframework.boot.autoconfigure.AutoConfigureAfter;
038import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
039import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
040import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;
041import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
042import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
043import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
044import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
045import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
046import org.springframework.boot.context.properties.EnableConfigurationProperties;
047import org.springframework.context.ApplicationContext;
048import org.springframework.context.annotation.Bean;
049import org.springframework.context.annotation.Configuration;
050import org.springframework.util.CollectionUtils;
051import org.springframework.util.ReflectionUtils;
052import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
053
054/**
055 * {@link EnableAutoConfiguration Auto-configuration} for Thymeleaf.
056 *
057 * @author Dave Syer
058 * @author Andy Wilkinson
059 * @author Stephane Nicoll
060 * @author Brian Clozel
061 * @author Eddú Meléndez
062 */
063@Configuration
064@EnableConfigurationProperties(ThymeleafProperties.class)
065@ConditionalOnClass(SpringTemplateEngine.class)
066@AutoConfigureAfter(WebMvcAutoConfiguration.class)
067public class ThymeleafAutoConfiguration {
068
069        @Configuration
070        @ConditionalOnMissingClass("org.thymeleaf.templatemode.TemplateMode")
071        static class Thymeleaf2Configuration {
072
073                @Configuration
074                @ConditionalOnMissingBean(name = "defaultTemplateResolver")
075                static class DefaultTemplateResolverConfiguration
076                                extends AbstractTemplateResolverConfiguration {
077
078                        DefaultTemplateResolverConfiguration(ThymeleafProperties properties,
079                                        ApplicationContext applicationContext) {
080                                super(properties, applicationContext);
081                        }
082
083                        @Bean
084                        public SpringResourceResourceResolver thymeleafResourceResolver() {
085                                return new SpringResourceResourceResolver();
086                        }
087
088                }
089
090                @Configuration
091                @ConditionalOnClass({ Servlet.class })
092                @ConditionalOnWebApplication
093                static class Thymeleaf2ViewResolverConfiguration
094                                extends AbstractThymeleafViewResolverConfiguration {
095
096                        Thymeleaf2ViewResolverConfiguration(ThymeleafProperties properties,
097                                        SpringTemplateEngine templateEngine) {
098                                super(properties, templateEngine);
099                        }
100
101                        @Override
102                        protected void configureTemplateEngine(ThymeleafViewResolver resolver,
103                                        SpringTemplateEngine templateEngine) {
104                                resolver.setTemplateEngine(templateEngine);
105                        }
106
107                }
108
109                @Configuration
110                @ConditionalOnClass(ConditionalCommentsDialect.class)
111                static class ThymeleafConditionalCommentsDialectConfiguration {
112
113                        @Bean
114                        @ConditionalOnMissingBean
115                        public ConditionalCommentsDialect conditionalCommentsDialect() {
116                                return new ConditionalCommentsDialect();
117                        }
118
119                }
120
121        }
122
123        @Configuration
124        @ConditionalOnClass(name = "org.thymeleaf.templatemode.TemplateMode")
125        static class Thymeleaf3Configuration {
126
127                @Configuration
128                @ConditionalOnMissingBean(name = "defaultTemplateResolver")
129                static class DefaultTemplateResolverConfiguration
130                                extends AbstractTemplateResolverConfiguration {
131
132                        DefaultTemplateResolverConfiguration(ThymeleafProperties properties,
133                                        ApplicationContext applicationContext) {
134                                super(properties, applicationContext);
135                        }
136
137                        @Bean
138                        @Override
139                        public SpringResourceTemplateResolver defaultTemplateResolver() {
140                                SpringResourceTemplateResolver resolver = super.defaultTemplateResolver();
141                                Method setCheckExistence = ReflectionUtils.findMethod(resolver.getClass(),
142                                                "setCheckExistence", boolean.class);
143                                ReflectionUtils.invokeMethod(setCheckExistence, resolver,
144                                                getProperties().isCheckTemplate());
145                                return resolver;
146                        }
147
148                }
149
150                @Configuration
151                @ConditionalOnClass({ Servlet.class })
152                @ConditionalOnWebApplication
153                static class Thymeleaf3ViewResolverConfiguration
154                                extends AbstractThymeleafViewResolverConfiguration {
155
156                        Thymeleaf3ViewResolverConfiguration(ThymeleafProperties properties,
157                                        SpringTemplateEngine templateEngine) {
158                                super(properties, templateEngine);
159                        }
160
161                        @Override
162                        protected void configureTemplateEngine(ThymeleafViewResolver resolver,
163                                        SpringTemplateEngine templateEngine) {
164                                Method setTemplateEngine;
165                                try {
166                                        setTemplateEngine = ReflectionUtils.findMethod(resolver.getClass(),
167                                                        "setTemplateEngine",
168                                                        Class.forName("org.thymeleaf.ITemplateEngine", true,
169                                                                        resolver.getClass().getClassLoader()));
170                                }
171                                catch (ClassNotFoundException ex) {
172                                        throw new IllegalStateException(ex);
173                                }
174                                ReflectionUtils.invokeMethod(setTemplateEngine, resolver, templateEngine);
175                        }
176
177                }
178
179        }
180
181        @Configuration
182        @ConditionalOnMissingBean(SpringTemplateEngine.class)
183        protected static class ThymeleafDefaultConfiguration {
184
185                private final Collection<ITemplateResolver> templateResolvers;
186
187                private final Collection<IDialect> dialects;
188
189                public ThymeleafDefaultConfiguration(
190                                Collection<ITemplateResolver> templateResolvers,
191                                ObjectProvider<Collection<IDialect>> dialectsProvider) {
192                        this.templateResolvers = templateResolvers;
193                        this.dialects = dialectsProvider.getIfAvailable();
194                }
195
196                @Bean
197                public SpringTemplateEngine templateEngine() {
198                        SpringTemplateEngine engine = new SpringTemplateEngine();
199                        for (ITemplateResolver templateResolver : this.templateResolvers) {
200                                engine.addTemplateResolver(templateResolver);
201                        }
202                        if (!CollectionUtils.isEmpty(this.dialects)) {
203                                for (IDialect dialect : this.dialects) {
204                                        engine.addDialect(dialect);
205                                }
206                        }
207                        return engine;
208                }
209
210        }
211
212        @Configuration
213        @ConditionalOnClass(name = "nz.net.ultraq.thymeleaf.LayoutDialect")
214        protected static class ThymeleafWebLayoutConfiguration {
215
216                @Bean
217                @ConditionalOnMissingBean
218                public LayoutDialect layoutDialect() {
219                        return new LayoutDialect();
220                }
221
222        }
223
224        @Configuration
225        @ConditionalOnClass(DataAttributeDialect.class)
226        protected static class DataAttributeDialectConfiguration {
227
228                @Bean
229                @ConditionalOnMissingBean
230                public DataAttributeDialect dialect() {
231                        return new DataAttributeDialect();
232                }
233
234        }
235
236        @Configuration
237        @ConditionalOnClass({ SpringSecurityDialect.class })
238        protected static class ThymeleafSecurityDialectConfiguration {
239
240                @Bean
241                @ConditionalOnMissingBean
242                public SpringSecurityDialect securityDialect() {
243                        return new SpringSecurityDialect();
244                }
245
246        }
247
248        @Configuration
249        @ConditionalOnJava(ConditionalOnJava.JavaVersion.EIGHT)
250        @ConditionalOnClass(Java8TimeDialect.class)
251        protected static class ThymeleafJava8TimeDialect {
252
253                @Bean
254                @ConditionalOnMissingBean
255                public Java8TimeDialect java8TimeDialect() {
256                        return new Java8TimeDialect();
257                }
258
259        }
260
261        @Configuration
262        @ConditionalOnWebApplication
263        protected static class ThymeleafResourceHandlingConfig {
264
265                @Bean
266                @ConditionalOnMissingBean
267                @ConditionalOnEnabledResourceChain
268                public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
269                        return new ResourceUrlEncodingFilter();
270                }
271
272        }
273
274}