Commit 037a27e2 authored by Andy Wilkinson's avatar Andy Wilkinson

Add a workaround for DATACMNS-776

Spring Data’s web support includes a handler method argument resolver,
ProxyingHandlerMethodArgumentResolver, that inaccurately claims that it
can handle all interface handler method arguments. This causes problems
for handler methods that take Spring Mobile’s Device as an argument as
the proxied Device instance does not behave correctly.

This commit works around the problem by assigning an order to the 
WebMvcConfigurerAdapter that registers Spring Mobile’s argument resolver
with Spring MVC. This ordering ensures that Spring Mobile’s resolver
takes precedence over Spring Data’s for Device arguments.

Closes gh-4163
parent c7c685f6
...@@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat ...@@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.mobile.device.DeviceHandlerMethodArgumentResolver; import org.springframework.mobile.device.DeviceHandlerMethodArgumentResolver;
import org.springframework.mobile.device.DeviceResolver; import org.springframework.mobile.device.DeviceResolver;
import org.springframework.mobile.device.DeviceResolverHandlerInterceptor; import org.springframework.mobile.device.DeviceResolverHandlerInterceptor;
...@@ -48,6 +49,7 @@ public class DeviceResolverAutoConfiguration { ...@@ -48,6 +49,7 @@ public class DeviceResolverAutoConfiguration {
@Configuration @Configuration
@ConditionalOnWebApplication @ConditionalOnWebApplication
@Order(0)
protected static class DeviceResolverMvcConfiguration protected static class DeviceResolverMvcConfiguration
extends WebMvcConfigurerAdapter { extends WebMvcConfigurerAdapter {
......
...@@ -20,15 +20,23 @@ import org.junit.After; ...@@ -20,15 +20,23 @@ import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration;
import org.springframework.boot.autoconfigure.test.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mobile.device.Device;
import org.springframework.mobile.device.DeviceHandlerMethodArgumentResolver; import org.springframework.mobile.device.DeviceHandlerMethodArgumentResolver;
import org.springframework.mobile.device.DeviceResolverHandlerInterceptor; import org.springframework.mobile.device.DeviceResolverHandlerInterceptor;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext; import org.springframework.mock.web.MockServletContext;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;
...@@ -38,6 +46,8 @@ import static org.hamcrest.Matchers.hasItemInArray; ...@@ -38,6 +46,8 @@ import static org.hamcrest.Matchers.hasItemInArray;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/** /**
* Tests for {@link DeviceResolverAutoConfiguration}. * Tests for {@link DeviceResolverAutoConfiguration}.
...@@ -76,10 +86,7 @@ public class DeviceResolverAutoConfigurationTests { ...@@ -76,10 +86,7 @@ public class DeviceResolverAutoConfigurationTests {
public void deviceResolverHandlerInterceptorRegistered() throws Exception { public void deviceResolverHandlerInterceptorRegistered() throws Exception {
this.context = new AnnotationConfigWebApplicationContext(); this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext()); this.context.setServletContext(new MockServletContext());
this.context.register(Config.class, WebMvcAutoConfiguration.class, this.context.register(Config.class);
HttpMessageConvertersAutoConfiguration.class,
DeviceResolverAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
RequestMappingHandlerMapping mapping = this.context RequestMappingHandlerMapping mapping = this.context
.getBean(RequestMappingHandlerMapping.class); .getBean(RequestMappingHandlerMapping.class);
...@@ -89,7 +96,23 @@ public class DeviceResolverAutoConfigurationTests { ...@@ -89,7 +96,23 @@ public class DeviceResolverAutoConfigurationTests {
hasItemInArray(instanceOf(DeviceResolverHandlerInterceptor.class))); hasItemInArray(instanceOf(DeviceResolverHandlerInterceptor.class)));
} }
@Test
public void deviceHandlerMethodArgumentWorksWithSpringData() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(Config.class);
this.context.setServletContext(new MockServletContext());
this.context.refresh();
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
mockMvc.perform(get("/")).andExpect(status().isOk());
}
@Configuration @Configuration
@ImportAutoConfiguration({ WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
DeviceResolverAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
SpringDataWebAutoConfiguration.class,
RepositoryRestMvcAutoConfiguration.class })
protected static class Config { protected static class Config {
@Bean @Bean
...@@ -103,8 +126,11 @@ public class DeviceResolverAutoConfigurationTests { ...@@ -103,8 +126,11 @@ public class DeviceResolverAutoConfigurationTests {
protected static class MyController { protected static class MyController {
@RequestMapping("/") @RequestMapping("/")
public void test() { public ResponseEntity<Void> test(Device device) {
if (device.getDevicePlatform() != null) {
return new ResponseEntity<>(HttpStatus.OK);
}
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment