From de7eeb50142fc72b9f276c0e86d221941c6ad6f3 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 22 Oct 2018 13:55:32 -0700 Subject: [PATCH 1/2] Fix ResourceUrlEncodingFilter conditions Fix `ResourceUrlEncodingFilter` conditions which were inadvertently changed in commits 64f04fce and 6cc272ec and would back off if any `FilterRegistrationBean` was found. The updated conditions restores the behavior of Spring Boot 2.0.5 and allows users to directly register their own `ResourceUrlEncodingFilter` beans (as long as they don't use a `FilterRegistrationBean`). Fixes gh-14897 --- .../FreeMarkerServletWebConfiguration.java | 2 +- .../thymeleaf/ThymeleafAutoConfiguration.java | 2 +- ...oConfigurationServletIntegrationTests.java | 58 ++++++++++++++++--- ...hymeleafServletAutoConfigurationTests.java | 30 ++++++++++ 4 files changed, 82 insertions(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerServletWebConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerServletWebConfiguration.java index 142fd4cee6b..3cf800e1690 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerServletWebConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerServletWebConfiguration.java @@ -74,7 +74,7 @@ class FreeMarkerServletWebConfiguration extends AbstractFreeMarkerConfiguration } @Bean - @ConditionalOnMissingBean + @ConditionalOnMissingBean(ResourceUrlEncodingFilter.class) @ConditionalOnEnabledResourceChain public FilterRegistrationBean resourceUrlEncodingFilter() { FilterRegistrationBean registration = new FilterRegistrationBean<>( diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java index a0635401db3..80197b106ad 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java @@ -167,7 +167,7 @@ public class ThymeleafAutoConfiguration { static class ThymeleafWebMvcConfiguration { @Bean - @ConditionalOnMissingBean + @ConditionalOnMissingBean(ResourceUrlEncodingFilter.class) @ConditionalOnEnabledResourceChain public FilterRegistrationBean resourceUrlEncodingFilter() { FilterRegistrationBean registration = new FilterRegistrationBean<>( diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfigurationServletIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfigurationServletIntegrationTests.java index f6dad0c0d24..9670c087f0c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfigurationServletIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfigurationServletIntegrationTests.java @@ -19,16 +19,22 @@ package org.springframework.boot.autoconfigure.freemarker; import java.io.StringWriter; import java.util.EnumSet; import java.util.Locale; +import java.util.Map; import javax.servlet.DispatcherType; import javax.servlet.http.HttpServletRequest; import org.junit.After; -import org.junit.Before; import org.junit.Test; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; @@ -52,12 +58,7 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class FreeMarkerAutoConfigurationServletIntegrationTests { - private AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); - - @Before - public void setupContext() { - this.context.setServletContext(new MockServletContext()); - } + private AnnotationConfigWebApplicationContext context; @After public void close() { @@ -170,9 +171,31 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR)); } + @Test + @SuppressWarnings("rawtypes") + public void registerResourceHandlingFilterWithOtherRegistrationBean() { + // gh-14897 + registerAndRefreshContext(FilterRegistrationConfiguration.class, + "spring.resources.chain.enabled:true"); + Map beans = this.context + .getBeansOfType(FilterRegistrationBean.class); + assertThat(beans).hasSize(2); + FilterRegistrationBean registration = beans.values().stream() + .filter((r) -> r.getFilter() instanceof ResourceUrlEncodingFilter) + .findFirst().get(); + assertThat(registration).hasFieldOrPropertyWithValue("dispatcherTypes", + EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR)); + } + private void registerAndRefreshContext(String... env) { + registerAndRefreshContext(BaseConfiguration.class, env); + } + + private void registerAndRefreshContext(Class config, String... env) { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setServletContext(new MockServletContext()); TestPropertyValues.of(env).applyTo(this.context); - this.context.register(FreeMarkerAutoConfiguration.class); + this.context.register(config); this.context.refresh(); } @@ -193,4 +216,23 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { return response; } + @Configuration + @ImportAutoConfiguration({ FreeMarkerAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class }) + static class BaseConfiguration { + + } + + @Configuration + @Import(BaseConfiguration.class) + static class FilterRegistrationConfiguration { + + @Bean + public FilterRegistrationBean filterRegisration() { + return new FilterRegistrationBean( + new OrderedCharacterEncodingFilter()); + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafServletAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafServletAutoConfigurationTests.java index 07720e8823d..5ae47da6160 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafServletAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafServletAutoConfigurationTests.java @@ -20,6 +20,7 @@ import java.io.File; import java.util.Collections; import java.util.EnumSet; import java.util.Locale; +import java.util.Map; import javax.servlet.DispatcherType; @@ -41,6 +42,7 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon import org.springframework.boot.test.rule.OutputCapture; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -224,6 +226,22 @@ public class ThymeleafServletAutoConfigurationTests { EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR)); } + @Test + @SuppressWarnings("rawtypes") + public void registerResourceHandlingFilterWithOtherRegistrationBean() { + // gh-14897 + load(FilterRegistrationConfiguration.class, + "spring.resources.chain.enabled:true"); + Map beans = this.context + .getBeansOfType(FilterRegistrationBean.class); + assertThat(beans).hasSize(2); + FilterRegistrationBean registration = beans.values().stream() + .filter((r) -> r.getFilter() instanceof ResourceUrlEncodingFilter) + .findFirst().get(); + assertThat(registration).hasFieldOrPropertyWithValue("dispatcherTypes", + EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR)); + } + @Test public void layoutDialectCanBeCustomized() { load(LayoutDialectConfiguration.class); @@ -269,4 +287,16 @@ public class ThymeleafServletAutoConfigurationTests { } + @Configuration + @Import(BaseConfiguration.class) + static class FilterRegistrationConfiguration { + + @Bean + public FilterRegistrationBean filterRegisration() { + return new FilterRegistrationBean( + new OrderedCharacterEncodingFilter()); + } + + } + } From 35221c1142196c628ae4d7209b18475e039f935c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 22 Oct 2018 13:25:52 -0700 Subject: [PATCH 2/2] Polish --- .../condition/BeanTypeRegistry.java | 1 - ...oConfigurationServletIntegrationTests.java | 32 +++++++++---------- ...hymeleafServletAutoConfigurationTests.java | 3 -- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java index 694e926d25a..5f1cd37e31c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java @@ -96,7 +96,6 @@ final class BeanTypeRegistry implements SmartInitializingSingleton { BeanDefinition bd = new RootBeanDefinition(BeanTypeRegistry.class); bd.getConstructorArgumentValues().addIndexedArgumentValue(0, beanFactory); listableBeanFactory.registerBeanDefinition(BEAN_NAME, bd); - } return listableBeanFactory.getBean(BEAN_NAME, BeanTypeRegistry.class); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfigurationServletIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfigurationServletIntegrationTests.java index 9670c087f0c..b70dd2e9949 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfigurationServletIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfigurationServletIntegrationTests.java @@ -69,7 +69,7 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { @Test public void defaultConfiguration() { - registerAndRefreshContext(); + load(); assertThat(this.context.getBean(FreeMarkerViewResolver.class)).isNotNull(); assertThat(this.context.getBean(FreeMarkerConfigurer.class)).isNotNull(); assertThat(this.context.getBean(FreeMarkerConfig.class)).isNotNull(); @@ -79,7 +79,7 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { @Test public void defaultViewResolution() throws Exception { - registerAndRefreshContext(); + load(); MockHttpServletResponse response = render("home"); String result = response.getContentAsString(); assertThat(result).contains("home"); @@ -88,7 +88,7 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { @Test public void customContentType() throws Exception { - registerAndRefreshContext("spring.freemarker.contentType:application/json"); + load("spring.freemarker.contentType:application/json"); MockHttpServletResponse response = render("home"); String result = response.getContentAsString(); assertThat(result).contains("home"); @@ -97,7 +97,7 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { @Test public void customPrefix() throws Exception { - registerAndRefreshContext("spring.freemarker.prefix:prefix/"); + load("spring.freemarker.prefix:prefix/"); MockHttpServletResponse response = render("prefixed"); String result = response.getContentAsString(); assertThat(result).contains("prefixed"); @@ -105,7 +105,7 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { @Test public void customSuffix() throws Exception { - registerAndRefreshContext("spring.freemarker.suffix:.freemarker"); + load("spring.freemarker.suffix:.freemarker"); MockHttpServletResponse response = render("suffixed"); String result = response.getContentAsString(); assertThat(result).contains("suffixed"); @@ -113,7 +113,7 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { @Test public void customTemplateLoaderPath() throws Exception { - registerAndRefreshContext( + load( "spring.freemarker.templateLoaderPath:classpath:/custom-templates/"); MockHttpServletResponse response = render("custom"); String result = response.getContentAsString(); @@ -122,14 +122,14 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { @Test public void disableCache() { - registerAndRefreshContext("spring.freemarker.cache:false"); + load("spring.freemarker.cache:false"); assertThat(this.context.getBean(FreeMarkerViewResolver.class).getCacheLimit()) .isEqualTo(0); } @Test public void allowSessionOverride() { - registerAndRefreshContext("spring.freemarker.allow-session-override:true"); + load("spring.freemarker.allow-session-override:true"); AbstractTemplateViewResolver viewResolver = this.context .getBean(FreeMarkerViewResolver.class); assertThat(ReflectionTestUtils.getField(viewResolver, "allowSessionOverride")) @@ -139,14 +139,14 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { @SuppressWarnings("deprecation") @Test public void customFreeMarkerSettings() { - registerAndRefreshContext("spring.freemarker.settings.boolean_format:yup,nope"); + load("spring.freemarker.settings.boolean_format:yup,nope"); assertThat(this.context.getBean(FreeMarkerConfigurer.class).getConfiguration() .getSetting("boolean_format")).isEqualTo("yup,nope"); } @Test public void renderTemplate() throws Exception { - registerAndRefreshContext(); + load(); FreeMarkerConfigurer freemarker = this.context .getBean(FreeMarkerConfigurer.class); StringWriter writer = new StringWriter(); @@ -156,13 +156,13 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { @Test public void registerResourceHandlingFilterDisabledByDefault() { - registerAndRefreshContext(); + load(); assertThat(this.context.getBeansOfType(FilterRegistrationBean.class)).isEmpty(); } @Test public void registerResourceHandlingFilterOnlyIfResourceChainIsEnabled() { - registerAndRefreshContext("spring.resources.chain.enabled:true"); + load("spring.resources.chain.enabled:true"); FilterRegistrationBean registration = this.context .getBean(FilterRegistrationBean.class); assertThat(registration.getFilter()) @@ -175,7 +175,7 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { @SuppressWarnings("rawtypes") public void registerResourceHandlingFilterWithOtherRegistrationBean() { // gh-14897 - registerAndRefreshContext(FilterRegistrationConfiguration.class, + load(FilterRegistrationConfiguration.class, "spring.resources.chain.enabled:true"); Map beans = this.context .getBeansOfType(FilterRegistrationBean.class); @@ -187,11 +187,11 @@ public class FreeMarkerAutoConfigurationServletIntegrationTests { EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR)); } - private void registerAndRefreshContext(String... env) { - registerAndRefreshContext(BaseConfiguration.class, env); + private void load(String... env) { + load(BaseConfiguration.class, env); } - private void registerAndRefreshContext(Class config, String... env) { + private void load(Class config, String... env) { this.context = new AnnotationConfigWebApplicationContext(); this.context.setServletContext(new MockServletContext()); TestPropertyValues.of(env).applyTo(this.context); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafServletAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafServletAutoConfigurationTests.java index 5ae47da6160..1cb51ed174f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafServletAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafServletAutoConfigurationTests.java @@ -262,9 +262,6 @@ public class ThymeleafServletAutoConfigurationTests { private void load(Class config, String... envVariables) { this.context = new AnnotationConfigWebApplicationContext(); TestPropertyValues.of(envVariables).applyTo(this.context); - if (config != null) { - this.context.register(config); - } this.context.register(config); this.context.refresh(); }