Skip to content

Commit

Permalink
Reinstate auto-config of RequestContextFilter with Jersey
Browse files Browse the repository at this point in the history
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
  • Loading branch information
wilkinsona committed Feb 12, 2019
1 parent cc64398 commit 799ac24
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
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;
Expand All @@ -64,6 +65,7 @@
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.
Expand Down Expand Up @@ -109,6 +111,16 @@ private void customize() {
.forEach((customizer) -> customizer.customize(this.config));
}

@Bean
@ConditionalOnMissingFilterBean(RequestContextFilter.class)
public FilterRegistrationBean<RequestContextFilter> requestContextFilter() {
FilterRegistrationBean<RequestContextFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new RequestContextFilter());
registration.setOrder(this.jersey.getFilter().getOrder() - 1);
registration.setName("requestContextFilter");
return registration;
}

@Bean
@ConditionalOnMissingBean
public JerseyApplicationPath jerseyApplicationPath() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -401,6 +401,7 @@ private void customizeResourceHandlerRegistration(
@Bean
@ConditionalOnMissingBean({ RequestContextListener.class,
RequestContextFilter.class })
@ConditionalOnMissingFilterBean(RequestContextFilter.class)
public static RequestContextFilter requestContextFilter() {
return new OrderedRequestContextFilter();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<RequestContextFilter> customRequestContextFilterRegistration() {
return new FilterRegistrationBean<RequestContextFilter>(
new RequestContextFilter());
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
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;
Expand Down Expand Up @@ -73,6 +74,7 @@
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;
Expand Down Expand Up @@ -913,6 +915,33 @@ public void contentNegotiationStrategySkipsPathExtension() throws Exception {
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<String, Object> handlerMap = getHandlerMap(
context.getBean("resourceHandlerMapping", HandlerMapping.class));
Expand Down Expand Up @@ -1232,4 +1261,25 @@ public void configureAsyncSupport(AsyncSupportConfigurer configurer) {

}

@Configuration
static class RequestContextFilterConfiguration {

@Bean
public RequestContextFilter customRequestContextFilter() {
return new RequestContextFilter();
}

}

@Configuration
static class RequestContextFilterRegistrationConfiguration {

@Bean
public FilterRegistrationBean<RequestContextFilter> customRequestContextFilterRegistration() {
return new FilterRegistrationBean<RequestContextFilter>(
new RequestContextFilter());
}

}

}

0 comments on commit 799ac24

Please sign in to comment.