1 | package fr.sii.ogham.spring.template; | |
2 | ||
3 | import static fr.sii.ogham.core.util.ConfigurationValueUtils.firstValue; | |
4 | import static java.util.Optional.ofNullable; | |
5 | ||
6 | import org.slf4j.Logger; | |
7 | import org.slf4j.LoggerFactory; | |
8 | import org.springframework.boot.autoconfigure.freemarker.FreeMarkerProperties; | |
9 | import org.springframework.context.ApplicationContext; | |
10 | ||
11 | import fr.sii.ogham.core.builder.configurer.MessagingConfigurerAdapter; | |
12 | import fr.sii.ogham.email.builder.EmailBuilder; | |
13 | import fr.sii.ogham.sms.builder.SmsBuilder; | |
14 | import fr.sii.ogham.spring.common.OghamTemplateProperties; | |
15 | import fr.sii.ogham.spring.common.SpringMessagingConfigurer; | |
16 | import fr.sii.ogham.spring.email.OghamEmailProperties; | |
17 | import fr.sii.ogham.spring.sms.OghamSmsProperties; | |
18 | import fr.sii.ogham.spring.template.freemarker.SpringBeansTemplateHashModelEx; | |
19 | import fr.sii.ogham.template.freemarker.FreeMarkerParser; | |
20 | import fr.sii.ogham.template.freemarker.FreemarkerConstants; | |
21 | import fr.sii.ogham.template.freemarker.builder.AbstractFreemarkerBuilder; | |
22 | import fr.sii.ogham.template.freemarker.builder.FreemarkerEmailBuilder; | |
23 | import fr.sii.ogham.template.freemarker.builder.FreemarkerSmsBuilder; | |
24 | import freemarker.ext.beans.BeansWrapper; | |
25 | import freemarker.ext.beans.BeansWrapperBuilder; | |
26 | import freemarker.template.Configuration; | |
27 | import freemarker.template.ObjectWrapper; | |
28 | ||
29 | /** | |
30 | * Integrates with Spring templating system by using Freemarker | |
31 | * {@link Configuration} object provided by Spring and by using Spring | |
32 | * properties defined with prefix {@code spring.freemarker} (see | |
33 | * {@link FreeMarkerProperties}). | |
34 | * | |
35 | * If both Spring property and Ogham property is defined, Ogham property is | |
36 | * used. | |
37 | * | |
38 | * For example, if the file application.properties contains the following | |
39 | * configuration: | |
40 | * | |
41 | * <pre> | |
42 | * spring.freemarker.prefix=/email/ | |
43 | * ogham.email.freemarker.path-prefix=/foo/ | |
44 | * </pre> | |
45 | * | |
46 | * The {@link FreeMarkerParser} will use the templates in "/foo/". | |
47 | * | |
48 | * <p> | |
49 | * This configurer is also useful to support property naming variants (see | |
50 | * <a href= | |
51 | * "https://github.com/spring-projects/spring-boot/wiki/relaxed-binding-2.0">Relaxed | |
52 | * Binding</a>). | |
53 | * | |
54 | * <p> | |
55 | * If ogham.freemarker.enable-spring-beans is true (default value), then Spring | |
56 | * Beans are available from the template using syntax | |
57 | * {@code @beanName.method(args)}. | |
58 | * | |
59 | * <p> | |
60 | * If {@code ogham.freemarker.static-method-access.enable} is true (default | |
61 | * value), then static methods can be called from templates using | |
62 | * | |
63 | * <pre> | |
64 | * {@code statics['full.package.name.ClassName'].method(args)} | |
65 | * </pre> | |
66 | * | |
67 | * If {@code ogham.freemarker.static-method-access.variable-name} value is | |
68 | * changed (default value is 'statics'), then static methods can be called from | |
69 | * templates using another variable name. For example, configuring | |
70 | * {@code ogham.freemarker.static-method-access.variable-name=global} gives | |
71 | * access to static methods using name global: | |
72 | * | |
73 | * <pre> | |
74 | * {@code global['full.package.name.ClassName'].method(args)} | |
75 | * </pre> | |
76 | * | |
77 | * | |
78 | * @author Aurélien Baudet | |
79 | * | |
80 | */ | |
81 | public class FreemarkerConfigurer extends MessagingConfigurerAdapter implements SpringMessagingConfigurer { | |
82 | private static final int SPRING_CONFIGURER_PRIORITY_OFFSET = 1000; | |
83 | ||
84 | private static final Logger LOG = LoggerFactory.getLogger(FreemarkerConfigurer.class); | |
85 | ||
86 | private final Configuration emailConfiguration; | |
87 | private final Configuration smsConfiguration; | |
88 | private final OghamCommonTemplateProperties templateProperties; | |
89 | private final OghamEmailProperties emailProperties; | |
90 | private final OghamSmsProperties smsProperties; | |
91 | private final FreeMarkerProperties springProperties; | |
92 | private final OghamFreemarkerProperties oghamFreemarkerProperties; | |
93 | private final ApplicationContext applicationContext; | |
94 | ||
95 | public FreemarkerConfigurer(Configuration emailConfiguration, Configuration smsConfiguration, OghamCommonTemplateProperties templateProperties, OghamEmailProperties emailProperties, | |
96 | OghamSmsProperties smsProperties, FreeMarkerProperties springProperties, OghamFreemarkerProperties oghamFreemarkerProperties, ApplicationContext applicationContext) { | |
97 | super(); | |
98 | this.emailConfiguration = emailConfiguration; | |
99 | this.smsConfiguration = smsConfiguration; | |
100 | this.templateProperties = templateProperties; | |
101 | this.emailProperties = emailProperties; | |
102 | this.smsProperties = smsProperties; | |
103 | this.springProperties = springProperties; | |
104 | this.oghamFreemarkerProperties = oghamFreemarkerProperties; | |
105 | this.applicationContext = applicationContext; | |
106 | } | |
107 | ||
108 | @Override | |
109 | public void configure(EmailBuilder emailBuilder) { | |
110 | AbstractFreemarkerBuilder<?, ?> builder = emailBuilder.template(FreemarkerEmailBuilder.class); | |
111 | builder.mergeConfiguration(emailConfiguration); | |
112 | // specific Ogham properties explicitly take precedence over Spring | |
113 | // properties | |
114 |
2
1. configure : negated conditional → KILLED 2. configure : negated conditional → KILLED |
if (springProperties != null) { |
115 |
2
1. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::applySpringConfiguration → KILLED 2. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::applySpringConfiguration → KILLED |
applySpringConfiguration(builder); |
116 | } | |
117 |
2
1. configure : negated conditional → KILLED 2. configure : negated conditional → KILLED |
if (emailProperties != null) { |
118 |
2
1. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::applyOghamConfiguration → KILLED 2. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::applyOghamConfiguration → KILLED |
applyOghamConfiguration(builder, emailProperties); |
119 | } | |
120 |
2
1. configure : negated conditional → KILLED 2. configure : negated conditional → KILLED |
if (oghamFreemarkerProperties.getSpringBeans().isEnable()) { |
121 |
2
1. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::registerSpringBeans → TIMED_OUT 2. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::registerSpringBeans → KILLED |
registerSpringBeans(builder, emailConfiguration); |
122 | } | |
123 |
2
1. configure : negated conditional → SURVIVED 2. configure : negated conditional → KILLED |
if (oghamFreemarkerProperties.getStaticMethodAccess().isEnable()) { |
124 |
1
1. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::registerStatics → SURVIVED |
registerStatics(builder); |
125 | } | |
126 | } | |
127 | ||
128 | @Override | |
129 | public void configure(SmsBuilder smsBuilder) { | |
130 | AbstractFreemarkerBuilder<?, ?> builder = smsBuilder.template(FreemarkerSmsBuilder.class); | |
131 | builder.mergeConfiguration(smsConfiguration); | |
132 | // specific Ogham properties explicitly take precedence over Spring | |
133 | // properties | |
134 |
2
1. configure : negated conditional → KILLED 2. configure : negated conditional → KILLED |
if (springProperties != null) { |
135 |
2
1. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::applySpringConfiguration → KILLED 2. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::applySpringConfiguration → KILLED |
applySpringConfiguration(builder); |
136 | } | |
137 |
2
1. configure : negated conditional → KILLED 2. configure : negated conditional → KILLED |
if (smsProperties != null) { |
138 |
2
1. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::applyOghamConfiguration → KILLED 2. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::applyOghamConfiguration → KILLED |
applyOghamConfiguration(builder, smsProperties); |
139 | } | |
140 |
2
1. configure : negated conditional → KILLED 2. configure : negated conditional → KILLED |
if (oghamFreemarkerProperties.getSpringBeans().isEnable()) { |
141 |
2
1. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::registerSpringBeans → KILLED 2. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::registerSpringBeans → KILLED |
registerSpringBeans(builder, smsConfiguration); |
142 | } | |
143 |
2
1. configure : negated conditional → SURVIVED 2. configure : negated conditional → KILLED |
if (oghamFreemarkerProperties.getStaticMethodAccess().isEnable()) { |
144 |
1
1. configure : removed call to fr/sii/ogham/spring/template/FreemarkerConfigurer::registerStatics → SURVIVED |
registerStatics(builder); |
145 | } | |
146 | } | |
147 | ||
148 | @Override | |
149 | public int getOrder() { | |
150 |
1
1. getOrder : replaced int return with 0 for fr/sii/ogham/spring/template/FreemarkerConfigurer::getOrder → SURVIVED |
return FreemarkerConstants.DEFAULT_FREEMARKER_EMAIL_CONFIGURER_PRIORITY + SPRING_CONFIGURER_PRIORITY_OFFSET; |
151 | } | |
152 | ||
153 | private void applyOghamConfiguration(AbstractFreemarkerBuilder<?, ?> builder, OghamTemplateProperties props) { | |
154 | LOG.debug("[{}] apply ogham configuration properties to {}", this, builder); | |
155 | // @formatter:off | |
156 | builder | |
157 | .classpath() | |
158 | .pathPrefix() | |
159 | .value(ofNullable(firstValue(props.getFreemarker().getClasspath().getPathPrefix(), | |
160 | props.getTemplate().getClasspath().getPathPrefix(), | |
161 | props.getFreemarker().getPathPrefix(), | |
162 | props.getTemplate().getPathPrefix(), | |
163 | templateProperties.getPathPrefix()))) | |
164 | .and() | |
165 | .pathSuffix() | |
166 | .value(ofNullable(firstValue(props.getFreemarker().getClasspath().getPathSuffix(), | |
167 | props.getTemplate().getClasspath().getPathSuffix(), | |
168 | props.getFreemarker().getPathSuffix(), | |
169 | props.getTemplate().getPathSuffix(), | |
170 | templateProperties.getPathSuffix()))) | |
171 | .and() | |
172 | .and() | |
173 | .file() | |
174 | .pathPrefix() | |
175 | .value(ofNullable(firstValue(props.getFreemarker().getFile().getPathPrefix(), | |
176 | props.getTemplate().getFile().getPathPrefix(), | |
177 | props.getFreemarker().getPathPrefix(), | |
178 | props.getTemplate().getPathPrefix(), | |
179 | templateProperties.getPathPrefix()))) | |
180 | .and() | |
181 | .pathSuffix() | |
182 | .value(ofNullable(firstValue(props.getFreemarker().getFile().getPathSuffix(), | |
183 | props.getTemplate().getFile().getPathSuffix(), | |
184 | props.getFreemarker().getPathSuffix(), | |
185 | props.getTemplate().getPathSuffix(), | |
186 | templateProperties.getPathSuffix()))); | |
187 | builder | |
188 | .configuration() | |
189 | .defaultEncoding().value(ofNullable(oghamFreemarkerProperties.getDefaultEncoding())); | |
190 | // @formatter:on | |
191 | } | |
192 | ||
193 | private void applySpringConfiguration(AbstractFreemarkerBuilder<?, ?> builder) { | |
194 | LOG.debug("[{}] apply spring configuration properties to {}", this, builder); | |
195 | // @formatter:off | |
196 | builder | |
197 | .classpath() | |
198 | .pathPrefix().value(ofNullable(springProperties.getPrefix())).and() | |
199 | .pathSuffix().value(ofNullable(springProperties.getSuffix())).and() | |
200 | .and() | |
201 | .file() | |
202 | .pathPrefix().value(ofNullable(springProperties.getPrefix())).and() | |
203 | .pathSuffix().value(ofNullable(springProperties.getSuffix())); | |
204 | builder | |
205 | .configuration() | |
206 | .defaultEncoding().value(ofNullable(springProperties.getCharsetName())); | |
207 | // @formatter:on | |
208 | } | |
209 | ||
210 | private void registerSpringBeans(AbstractFreemarkerBuilder<?, ?> builder, Configuration configuration) { | |
211 | builder.configuration().addSharedVariables(new SpringBeansTemplateHashModelEx(applicationContext, getBeansWrapper(configuration))); | |
212 | } | |
213 | ||
214 | private static BeansWrapper getBeansWrapper(Configuration configuration) { | |
215 | ObjectWrapper objectWrapper = configuration.getObjectWrapper(); | |
216 |
1
1. getBeansWrapper : negated conditional → SURVIVED |
if (objectWrapper instanceof BeansWrapper) { |
217 |
2
1. getBeansWrapper : replaced return value with null for fr/sii/ogham/spring/template/FreemarkerConfigurer::getBeansWrapper → KILLED 2. getBeansWrapper : replaced return value with null for fr/sii/ogham/spring/template/FreemarkerConfigurer::getBeansWrapper → KILLED |
return (BeansWrapper) objectWrapper; |
218 | } | |
219 |
1
1. getBeansWrapper : replaced return value with null for fr/sii/ogham/spring/template/FreemarkerConfigurer::getBeansWrapper → NO_COVERAGE |
return new BeansWrapperBuilder(configuration.getIncompatibleImprovements()).build(); |
220 | } | |
221 | ||
222 | private void registerStatics(AbstractFreemarkerBuilder<?, ?> builder) { | |
223 | builder.configuration() | |
224 | .enableStaticMethodAccess(true) | |
225 | .staticMethodAccessVariableName(oghamFreemarkerProperties.getStaticMethodAccess().getVariableName()); | |
226 | } | |
227 | } | |
Mutations | ||
114 |
1.1 2.2 |
|
115 |
1.1 2.2 |
|
117 |
1.1 2.2 |
|
118 |
1.1 2.2 |
|
120 |
1.1 2.2 |
|
121 |
1.1 2.2 |
|
123 |
1.1 2.2 |
|
124 |
1.1 |
|
134 |
1.1 2.2 |
|
135 |
1.1 2.2 |
|
137 |
1.1 2.2 |
|
138 |
1.1 2.2 |
|
140 |
1.1 2.2 |
|
141 |
1.1 2.2 |
|
143 |
1.1 2.2 |
|
144 |
1.1 |
|
150 |
1.1 |
|
216 |
1.1 |
|
217 |
1.1 2.2 |
|
219 |
1.1 |