From 1b00d38fda76c12445625d94cfbe9105385bcf59 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 10 Sep 2025 10:53:10 +0100 Subject: [PATCH] Only apply non-null Groovy Template properties Previously, properties were always applied even when they had a null value. When using the property defaults, this would leave the GroovyMarkupConfigurer bean with null values for autoIndentString, locale, and newLineString. The null values could cause problems at runtime, for example an NPE when Groovy Templates tries to call toString on the null newLineString. Closes gh-47139 --- .../GroovyTemplateAutoConfiguration.java | 2 +- .../GroovyTemplateAutoConfigurationTests.java | 43 ++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfiguration.java index 23ff2793c35..b375ffa93d0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfiguration.java @@ -115,7 +115,7 @@ public class GroovyTemplateAutoConfiguration { GroovyMarkupConfigurer groovyMarkupConfigurer(ObjectProvider templateEngine, Environment environment) { GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer(); - PropertyMapper map = PropertyMapper.get(); + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); map.from(this.properties::isAutoEscape).to(configurer::setAutoEscape); map.from(this.properties::isAutoIndent).to(configurer::setAutoIndent); map.from(this.properties::getAutoIndentString).to(configurer::setAutoIndentString); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfigurationTests.java index 159161a008a..51983770f5b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfigurationTests.java @@ -25,6 +25,7 @@ import java.util.Locale; import java.util.Map; import groovy.text.markup.BaseTemplate; +import groovy.text.markup.DelegatingIndentWriter; import groovy.text.markup.MarkupTemplateEngine; import groovy.text.markup.TemplateConfiguration; import jakarta.servlet.http.HttpServletRequest; @@ -159,9 +160,16 @@ class GroovyTemplateAutoConfigurationTests { assertThat(result).contains("suffixed"); } + @Test + void defaultResourceLoaderPath() throws Exception { + registerAndRefreshContext(); + assertThat(this.context.getBean(GroovyMarkupConfigurer.class).getResourceLoaderPath()) + .isEqualTo(GroovyTemplateProperties.DEFAULT_RESOURCE_LOADER_PATH); + } + @Test @WithResource(name = "custom-templates/custom.tpl", content = "yield \"custom\"") - void customTemplateLoaderPath() throws Exception { + void customResourceLoaderPath() throws Exception { registerAndRefreshContext("spring.groovy.template.resource-loader-path:classpath:/custom-templates/"); MockHttpServletResponse response = render("custom"); String result = response.getContentAsString(); @@ -206,6 +214,13 @@ class GroovyTemplateAutoConfigurationTests { assertThat(this.context.getBean(GroovyMarkupConfigurer.class).isAutoIndent()).isTrue(); } + @Test + void defaultAutoIndentString() { + registerAndRefreshContext(); + assertThat(this.context.getBean(GroovyMarkupConfigurer.class).getAutoIndentString()) + .isEqualTo(DelegatingIndentWriter.SPACES); + } + @Test void customAutoIndentString() { registerAndRefreshContext("spring.groovy.template.auto-indent-string:\\t"); @@ -218,6 +233,13 @@ class GroovyTemplateAutoConfigurationTests { assertThat(this.context.getBean(GroovyMarkupConfigurer.class).isAutoNewLine()).isTrue(); } + @Test + void defaultBaseTemplateClass() { + registerAndRefreshContext(); + assertThat(this.context.getBean(GroovyMarkupConfigurer.class).getBaseTemplateClass()) + .isEqualTo(BaseTemplate.class); + } + @Test void customBaseTemplateClass() { registerAndRefreshContext("spring.groovy.template.base-template-class:" + CustomBaseTemplate.class.getName()); @@ -225,6 +247,12 @@ class GroovyTemplateAutoConfigurationTests { .isEqualTo(CustomBaseTemplate.class); } + @Test + void defaultDeclarationEncoding() { + registerAndRefreshContext(); + assertThat(this.context.getBean(GroovyMarkupConfigurer.class).getDeclarationEncoding()).isNull(); + } + @Test void customDeclarationEncoding() { registerAndRefreshContext("spring.groovy.template.declaration-encoding:UTF-8"); @@ -237,12 +265,25 @@ class GroovyTemplateAutoConfigurationTests { assertThat(this.context.getBean(GroovyMarkupConfigurer.class).isExpandEmptyElements()).isTrue(); } + @Test + void defaultLocale() { + registerAndRefreshContext(); + assertThat(this.context.getBean(GroovyMarkupConfigurer.class).getLocale()).isEqualTo(Locale.getDefault()); + } + @Test void customLocale() { registerAndRefreshContext("spring.groovy.template.locale:en_US"); assertThat(this.context.getBean(GroovyMarkupConfigurer.class).getLocale()).isEqualTo(Locale.US); } + @Test + void defaultNewLineString() { + registerAndRefreshContext(); + assertThat(this.context.getBean(GroovyMarkupConfigurer.class).getNewLineString()) + .isEqualTo(System.lineSeparator()); + } + @Test void customNewLineString() { registerAndRefreshContext("spring.groovy.template.new-line-string:\\r\\n");