I'm using a Servlet 3 controller in a Spring Boot application. This controller calls services that, in the end, make 3 HTTP requests.
@GetMapping("/nbasync/c/**")
public CompletableFuture<String> nonBlockingAsyncCall() throws Exception {
CompletableFuture<String> result = service.call());
CompletableFuture<Void> result2 = service2.call();
CompletableFuture<Void> result3 = service3.call();
return result
.thenCombine(result2, this::keepFirst)
.thenCombine(result3, this::keepFirst);
}
Each of this outgoing calls are made using a RestTemplate
and are intercepted by a ClientHttpRequestInterceptor
. In this ClientHttpRequestInterceptor
, I need a (proxied) request scoped bean (cf: How to enable request scope in async task executor with a Runnable).
This works just fine if I wait for the the result :
CompletableFuture.allOf(result, result2, result3).join();
return result.get();
In the non blocking method, it crashes with the following exception :
java.util.concurrent.CompletionException:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.requestCookieHelper': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is
java.lang.IllegalStateException: Cannot ask for request attribute - request is not active anymore!
See complete log : https://gist.github.com/Skeebl/d0b19ebb9ab4d0d2a917203e4bd6fad5
It appears that each time a thread lets go of the process, AbstractRequestAttributes.requestCompleted()
is called (twice). This methods sets this.requestActive = false;
. While requestActive
is false, you can't access the request scoped beans.
The interceptor and request scoped bean simplify the methods signatures. Is there a way to keep theses while working with async requests ?