Skip to content

ClassCastException in TestDispatcherServlet [SPR-16695] #21236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue Apr 6, 2018 · 9 comments
Closed
Assignees
Labels
in: test Issues in the test module status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Apr 6, 2018

Lukas Krecan opened SPR-16695 and commented

ClassCastException when testing async endpoint using Spring Boot 1.5.11 @SpringBootTest(webEnvironment = RANDOM_PORT) with Spring Security.

Apparently, Spring Security wraps MockAsyncContext which causes TestDispatcherServlet to fail.

java.lang.ClassCastException: org.springframework.security.web.servletapi.HttpServlet3RequestFactory$SecurityContextAsyncContext cannot be cast to org.springframework.mock.web.MockAsyncContext

	at org.springframework.test.web.servlet.TestDispatcherServlet.initAsyncDispatchLatch(TestDispatcherServlet.java:90)
	at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:68)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
	at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:160)
	at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:127)
	at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:127)
	at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:111)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:127)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)

Affects: 4.3.15

Issue Links:

Referenced from: commits 7a896d7, 6deee3e, 3133082

Backported to: 4.3.16

1 votes, 7 watchers

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Apr 6, 2018

Jan Philipp commented

I have also this issue right after updating a working SB/1.5.10 (Spring/4.3.14)

Looks like being related to #21189.

Here is demo code (with TestNG, SB/1.5.11 & Spring/4.3.15, but that might work with minor changes in Junit5 as well):

@SpringBootTest(classes = SPR16695Test.Config.class)
public class SPR16695Test extends AbstractTestNGSpringContextTests {

	@Configuration
	@EnableWebMvc
	@EnableAsync
	@EnableWebSecurity
	@ComponentScan(basePackageClasses = SPR16695Test.class)
	public static class Config extends WebSecurityConfigurerAdapter {
		@Override
		protected void configure(final HttpSecurity http) throws Exception {
			http.authorizeRequests()
			    .anyRequest()
			    .permitAll();
		}
	}

	@Controller
	@RequestMapping("/demo")
	public static class DemoController {
		@GetMapping
		public CompletableFuture<String> get() {
			return CompletableFuture.completedFuture("");
		}
	}

	@Autowired
	protected WebApplicationContext context;

	protected MockMvc mockMvc;

	@BeforeMethod
	public void prepare() {
		mockMvc = MockMvcBuilders.webAppContextSetup(context)
		                         .apply(springSecurity())
		                         .build();
	}

	@Test
	public void demo() throws Exception {
		mockMvc.perform(asyncDispatch(mockMvc.perform(get("/demo")).andReturn()));
	}

}

While developing the demo, I have noticed that the exception comes when security is enabled (it was my last piece of integration).

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

Jan Philipp just confirming, you mean you have the same exception?

@spring-projects-issues
Copy link
Collaborator Author

Jan Philipp commented

Yes.

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

This should be fixed now in the latest snapshots.

As a workaround, until the fixes are released, you can use this Filter in tests:

private static class Spr16695Filter extends GenericFilterBean {

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws IOException, ServletException {

		HttpServletRequest requestToUse = new HttpServletRequestWrapper((HttpServletRequest) request) {

			@Override
			public AsyncContext getAsyncContext() {
				MockHttpServletRequest mockRequest = WebUtils.getNativeRequest(request, MockHttpServletRequest.class);
				Assert.notNull(mockRequest, "Expected mock request");
				return mockRequest.getAsyncContext();
			}
		};

		filterChain.doFilter(requestToUse, response);
	}
}

You can use a MockMvcConfigurer to apply it after the Spring Security one:

@BeforeMethod
public void prepare() {
	mockMvc = MockMvcBuilders.webAppContextSetup(context)
			.apply(springSecurity())
			.apply(new Spr16695Configurer())  // <-- 
			.build();
}


private static class Spr16695Configurer implements MockMvcConfigurer {
	@Override
	public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder, WebApplicationContext cxt) {
		builder.addFilters(new Spr16695Filter());
		return null;
	}
}

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

FYI the fix for this is available earlier than expected in 4.3.x, with 4.3.16 released today.

@spring-projects-issues
Copy link
Collaborator Author

Brian Dilley commented

When will the the fix for this make it into spring boot?

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

It's in Boot 1.5.12 already and will be part of the upcoming 2.0.2 as well.

@spring-projects-issues
Copy link
Collaborator Author

JJ Zabkar commented

I think the fix (313308208e857c5f4e56be8d50c76065ed59cdc2) was inadvertently a breaking change. Non-Spring-Boot test consumers that use < 4.3.16 and EnableAsync must upgrade their tests, effectively making with the workaround you described above mandatory. Basic tests that use a MockMvc dispatcher will now fail.

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

JJ Zabkar I don't follow. How can a change in 4.3.16 break anything < 4.3.16?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants