We a have very strange reaction in our application.
The application has a (singleton) service - let us call it singletonService
.
And a other service which runs in a session-scope. sessionService
.
The singletonService
has a method like this CompletableFuture<String> longTask(String param)
and after the long task, the singletonService
has to call the sessionService
(for the method String transform(String param)
).
When we write longTask()
like Example 1 then all works fine.
The transform
method runs as expected.
Example 1.1
public CompletableFuture<String> longTask(String param) {
CompletableFuture<String> future = startLongTask(param);
future.thenApply(sessionService::transform);
return future;
}
Example 1.2
public CompletableFuture<String> longTask(String param) {
CompletableFuture<String> future = startLongTask(param);
future.thenApplyAsync(sessionService::transform, asyncExecutor);
return future;
}
But in this way we dont wait for transform
method.
Its better to write it like Example 2.
Example 2.1
public CompletableFuture<String> longTask(String param) {
return startTaskLongTask(param).thenApply(sessionService::transform);
}
Example 2.2
public CompletableFuture<String> longTask(String param) {
return startTaskLongTask(param).thenApplyAsync(sessionService::transform, asyncExecutor);
}
But Example 2 will always finish exceptionally. It throws a org.springframework.beans.factory.BeanCreationException
.
The whole exception:
Scope 'session' 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: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Has anyone hints, where we have to search for the problem?
Additonal info
The signature of startLongTask()
is this: private CompletableFuture<String> startLongTask(String param)
.
To keep it simple, I wrote the method in this way.
In real, its retrofit that calls a RESTful-api.
The asyncExecutor
is a bean for spring.
/**
* Creates a context aware {@link Runnable} that wraps the original one.
*
* @param task The original {@link Runnable}
* @return The wrapper
*/
private Runnable getRunnable(final Runnable task) {
try {
final RequestAttributes attr = RequestContextHolder.currentRequestAttributes();
return () -> {
try {
RequestContextHolder.setRequestAttributes(attr);
} catch (final Exception ignored) {
}
task.run();
try {
RequestContextHolder.resetRequestAttributes();
} catch (final Exception ignored) {
}
};
} catch (final Exception ignored) {
return task;
}
}
The signature for the transform()
is String transform(String param)
. The function uses session-relevant data (the locale of current user).