From 799ac24da16e1400691ce6f02e55b54375f997d7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 12 Feb 2019 12:50:17 +0000 Subject: [PATCH] Reinstate auto-config of RequestContextFilter with Jersey Previously, the auto-configuration for both Jersey and WebMvc would auto-configure a RequestContextFilter bean. In 2.1.0, this led to a startup failure due to the latter attempting to override the bean defined by the former. In addition to the override there were also problems with the order of the filter as Jersey uses -1 and MVC uses -105. To avoid the above-described problems, the auto-configuration of the RequestContextFilter was removed from JerseyAutoConfiguration in 2.1.1. Unfortunately, the broke request-scoped beans for those using only Jersey. This commit attempts to strike a better balance by reintroducing the auto-configuration of RequestContextFilter in JerseyAutoConfiguration. It will back off if the user defines their own filter or filter registration. WebMvcAutoConfiguration has been updated to back off in the same manner. This leaves the potential for ordering problems, but they are no worse than they were before. Furthermore, the user has the means to correct any problems by using the various filter ordering properties that are provided for Jersey, Spring Session, Spring Security, etc. Closes gh-15376 --- .../jersey/JerseyAutoConfiguration.java | 12 ++ .../web/servlet/WebMvcAutoConfiguration.java | 3 +- .../jersey/JerseyAutoConfigurationTests.java | 106 ++++++++++++++++++ .../servlet/WebMvcAutoConfigurationTests.java | 50 +++++++++ 4 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationTests.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java index 0e3fb473390..a1b324ebbef 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java @@ -50,6 +50,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandi import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.autoconfigure.web.servlet.ConditionalOnMissingFilterBean; import org.springframework.boot.autoconfigure.web.servlet.DefaultJerseyApplicationPath; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath; @@ -64,6 +65,7 @@ import org.springframework.core.annotation.Order; import org.springframework.util.ClassUtils; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ServletContextAware; +import org.springframework.web.filter.RequestContextFilter; /** * {@link EnableAutoConfiguration Auto-configuration} for Jersey. @@ -109,6 +111,16 @@ public class JerseyAutoConfiguration implements ServletContextAware { .forEach((customizer) -> customizer.customize(this.config)); } + @Bean + @ConditionalOnMissingFilterBean(RequestContextFilter.class) + public FilterRegistrationBean requestContextFilter() { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setFilter(new RequestContextFilter()); + registration.setOrder(this.jersey.getFilter().getOrder() - 1); + registration.setName("requestContextFilter"); + return registration; + } + @Bean @ConditionalOnMissingBean public JerseyApplicationPath jerseyApplicationPath() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java index 19e8ecbc98e..d3ed83cc5f9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -401,6 +401,7 @@ public class WebMvcAutoConfiguration { @Bean @ConditionalOnMissingBean({ RequestContextListener.class, RequestContextFilter.class }) + @ConditionalOnMissingFilterBean(RequestContextFilter.class) public static RequestContextFilter requestContextFilter() { return new OrderedRequestContextFilter(); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationTests.java new file mode 100644 index 00000000000..1600e073903 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationTests.java @@ -0,0 +1,106 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.jersey; + +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener; +import org.springframework.boot.logging.LogLevel; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.RequestContextFilter; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JerseyAutoConfiguration}. + * + * @author Andy Wilkinson + */ +public class JerseyAutoConfigurationTests { + + private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(JerseyAutoConfiguration.class)) + .withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO)) + .withUserConfiguration(ResourceConfig.class); + + @Test + public void requestContextFilterRegistrationIsAutoConfigured() { + this.contextRunner.run((context) -> { + assertThat(context).hasSingleBean(FilterRegistrationBean.class); + FilterRegistrationBean registration = context + .getBean(FilterRegistrationBean.class); + assertThat(registration.getFilter()).isInstanceOf(RequestContextFilter.class); + }); + } + + @Test + public void whenUserDefinesARequestContextFilterTheAutoConfiguredRegistrationBacksOff() { + this.contextRunner.withUserConfiguration(RequestContextFilterConfiguration.class) + .run((context) -> { + assertThat(context).doesNotHaveBean(FilterRegistrationBean.class); + assertThat(context).hasSingleBean(RequestContextFilter.class); + }); + } + + @Test + public void whenUserDefinesARequestContextFilterRegistrationTheAutoConfiguredRegistrationBacksOff() { + this.contextRunner + .withUserConfiguration( + RequestContextFilterRegistrationConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(FilterRegistrationBean.class); + assertThat(context).hasBean("customRequestContextFilterRegistration"); + }); + } + + @Configuration + static class ResourceConfigConfiguration { + + @Bean + public ResourceConfig resourceConfig() { + return new ResourceConfig(); + } + + } + + @Configuration + static class RequestContextFilterConfiguration { + + @Bean + public RequestContextFilter requestContextFilter() { + return new RequestContextFilter(); + } + + } + + @Configuration + static class RequestContextFilterRegistrationConfiguration { + + @Bean + public FilterRegistrationBean customRequestContextFilterRegistration() { + return new FilterRegistrationBean( + new RequestContextFilter()); + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java index a3696292204..f425e234ace 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java @@ -45,6 +45,7 @@ import org.springframework.boot.test.context.assertj.AssertableWebApplicationCon import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.ApplicationContext; @@ -73,6 +74,7 @@ import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.filter.FormContentFilter; import org.springframework.web.filter.HiddenHttpMethodFilter; +import org.springframework.web.filter.RequestContextFilter; import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerMapping; @@ -913,6 +915,33 @@ public class WebMvcAutoConfigurationTests { assertThat(mediaTypes).containsOnly(MediaType.ALL); } + @Test + public void requestContextFilterIsAutoConfigured() { + this.contextRunner.run((context) -> assertThat(context) + .hasSingleBean(RequestContextFilter.class)); + } + + @Test + public void whenUserDefinesARequestContextFilterTheAutoConfiguredRegistrationBacksOff() { + this.contextRunner.withUserConfiguration(RequestContextFilterConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(RequestContextFilter.class); + assertThat(context).hasBean("customRequestContextFilter"); + }); + } + + @Test + public void whenUserDefinesARequestContextFilterRegistrationTheAutoConfiguredFilterBacksOff() { + this.contextRunner + .withUserConfiguration( + RequestContextFilterRegistrationConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(FilterRegistrationBean.class); + assertThat(context).hasBean("customRequestContextFilterRegistration"); + assertThat(context).doesNotHaveBean(RequestContextFilter.class); + }); + } + private void assertCacheControl(AssertableWebApplicationContext context) { Map handlerMap = getHandlerMap( context.getBean("resourceHandlerMapping", HandlerMapping.class)); @@ -1232,4 +1261,25 @@ public class WebMvcAutoConfigurationTests { } + @Configuration + static class RequestContextFilterConfiguration { + + @Bean + public RequestContextFilter customRequestContextFilter() { + return new RequestContextFilter(); + } + + } + + @Configuration + static class RequestContextFilterRegistrationConfiguration { + + @Bean + public FilterRegistrationBean customRequestContextFilterRegistration() { + return new FilterRegistrationBean( + new RequestContextFilter()); + } + + } + }