Scope 'session' is not active for the current thread; IllegalStateException: No thread-bound request found
The problem is not in your Spring annotations but your design pattern. You mix together different scopes and threads:
- singleton
- session (or request)
- thread pool of jobs
The singleton is available anywhere, it is ok. However session/request scope is not available outside a thread that is attached to a request.
Asynchronous job can run even the request or session doesn't exist anymore, so it is not possible to use a request/session dependent bean. Also there is no way to know, if your are running a job in a separate thread, which thread is the originator request (it means aop:proxy is not helpful in this case).
I think your code looks like that you want to make a contract between ReportController, ReportBuilder, UselessTask and ReportPage. Is there a way to use just a simple class (POJO) to store data from UselessTask and read it in ReportController or ReportPage and do not use ReportBuilder anymore?
I'm answering my own question because it provides a better overview of the cause and possible solutions. I've awarded the bonus to @Martin because he pin pointed the cause.
Cause
As suggested by @Martin the cause is the use of multiple threads. The request object is not available in these threads, as mentioned in the Spring Guide:
DispatcherServlet
,RequestContextListener
andRequestContextFilter
all do exactly the same thing, namely bind the HTTP request object to the Thread that is servicing that request. This makes beans that are request- and session-scoped available further down the call chain.
Solution 1
It is possible to make the request object available to other threads, but it places a couple of limitations on the system, which may not be workable in all projects. I got this solution from Accessing request scoped beans in a multi-threaded web application:
I managed to get around this issue. I started using
SimpleAsyncTaskExecutor
instead ofWorkManagerTaskExecutor
/ThreadPoolExecutorFactoryBean
. The benefit is thatSimpleAsyncTaskExecutor
will never re-use threads. That's only half the solution. The other half of the solution is to use aRequestContextFilter
instead ofRequestContextListener
.RequestContextFilter
(as well asDispatcherServlet
) has athreadContextInheritable
property which basically allows child threads to inherit the parent context.
Solution 2
The only other option is to use the session scoped bean inside the request thread. In my case this wasn't possible because:
- The controller method is annotated with
@Async
; - The controller method starts a batch job which uses threads for parallel job steps.
If anyone else stuck on same point, following solved my problem.
In web.xml
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
In Session component
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
In pom.xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>