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