10

I have the Problem, that I want to outsource some processes of my Spring WebMVC application into separate Threads. That was easy enough and works, until I want to use a class, userRightService, which uses the global request. That's not available in the threads, and we get a problem, that's pretty much understandable.

This is my Error:

java.lang.RuntimeException:
org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'scopedTarget.userRightsService': 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!

Okay, clear enough. I am trying to keep the request context by implementing this solution:

How to enable request scope in async task executor

This is my runnable class:

@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class myThread implements Runnable {

  private RequestAttributes context;

  public DataExportThread(RequestAttributes context) {
    this.context = context;
  }

  public void run() {
    RequestContextHolder.setRequestAttributes(context);

And this where it gets spawned:

final DataExportThread dataExportThread = 
   new myThread(RequestContextHolder.currentRequestAttributes());

final Thread thread = new Thread(myThread);
thread.setUncaughtExceptionHandler((t, e) -> {...});
thread.start();

As far as I understood, we store the currentRequestAttributes in the thread and then, when running, we restore them currentRequestAttributes... sounded solid to me, but the error is still there. I think I made some mistake adapting the solution for my case. maybe someone can help me finding the error.

Before I went through a lot of stackoverflow-threads with different solutions (see below), so I could try something else next, but this one seemed the clearest and simplest to me, so I hope someone could help me finding the mistake in the implementation or explain why it's the wrong approach.

I already tried this one without success:

If it's matters:

<org.springframework-version>4.3.4.RELEASE</org.springframework-version>

BTW: I know that it would be better to restructure the application in a way, that the request is not needed in the thread but that's very complicated in that case and I really hope I could avoid this.

--

Edit1:

The Bean which can not be created in the thread starts like this:

@Service("userRightsService")
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserRightsService {

--

Edit2:

I also tried this one:

But context is always empty...

Paflow
  • 2,030
  • 3
  • 30
  • 50
  • Could you please clarify few things? What is your main goal, is it to have an instance of `UserRightsService` in the custom `Runnable` implementation you've introduced? Is it correct that `@Scope` annotations you already have are must haves? And generally is it correct, that you want to call some method in `UserRightsService` using request attributes you've set somewhere on the back-end (let's say in the interceptor) as arguments/parameters (it is question about `RequestAttributes` you are trying to pass to your `Runnable` instance)? – marme1ad Sep 28 '18 at 17:40
  • And btw could I please ask to fix class/field names in your sources related to your custom `Runnable` instance, there are copy/paste typos there? Will be also good to have few lines of code related to `UserRightsService` in the `Runnable` instance if any. Otherwise it is hard to understand what is your mistake. – marme1ad Sep 28 '18 at 17:44
  • You can use ThreadLocale. – Prasad Oct 01 '18 at 08:07
  • The Runnable creates a DataExport which is too slow for a live response. By creating it there are a couple of classes involved, some need the information what the requester is allowed to do and what not. There are employing the UserRightsService via @Autowire for that. – Paflow Oct 04 '18 at 09:48

2 Answers2

2

I couldn't reproduce the problem as I am not sure how are you creating/injecting the UserRightsService but I have a couple of suggestions that you may try.

I guess that the problem is that the RequestAttributes is invalidated as the request is over (that's why the exception says Cannot ask for request attribute - request is not active anymore), which happens as your task is running.

Instead, you could try injecting the UserRightsService where your thread is spawned and pass this instance as an argument to the thread. That way the UserRightsService should be created without problem as the request should be still available.

Even so, trying to access the RequestAttributes after the request is over will probably fail. In that case I propose to make a copy of all the values that you need before the request is over, i.e. before your run the thread.

If that doesn't work for you please provide some more info regarding how you initialize the UserRightsService inside the task.

Good luck!

P.S.: I think that the scope annotation in your thread class is useless as the task object is created manually and not managed by spring.

Master_ex
  • 789
  • 6
  • 12
2

For those, who are searching. With the help of Master_Ex's hints I found a solution:

In the runnable:

private HttpServletRequest request;

public void run() {

    final RequestContextListener rcl = new RequestContextListener();
    final ServletContext sc = request.getServletContext();
    rcl.requestInitialized(new ServletRequestEvent(sc, request));

And in the UserRightService I make a call to a function which does the following:

    SecurityContext context = SecurityContextHolder.getContext();
    Authentication auth = context.getAuthentication();

    context.setAuthentication(getDataExportAuthentication(exportingUser));

@Master_Ex's Thank You, your post was very helpful. So sorry that I am too late to give you the bounty, otherwise I would have marked it as the correct one.

Paflow
  • 2,030
  • 3
  • 30
  • 50