35

I have a web application that has a Spring Integration logic running with it in a separated thread. The problem is that at some point my Spring Integration logic tries to use a request scoped bean and then i get the following errors:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.tenantContext': 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: 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.


Caused by: 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.

I have the ContextLoaderListener set:

<listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

My Scoped Bean is annotated like that(since I heard that proxing my bean would help):

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)  
public class TenantContext  implements Serializable {

Is what I'm doing possible? If yes, what am I missing here? If no, any other suggestions on how I can achieve that?

Diego Urenia
  • 1,620
  • 2
  • 21
  • 28
  • Are you trying to run code after the request has been served, or have the request wait for some asynchronous processing? – OrangeDog Jul 21 '16 at 14:06
  • @OrangeDog the question was answered and the answer accepted already. Back in 2013 I didn't know a lot of things and now I understand it was a rookie mistake, but thanks anyway. – Diego Urenia Jul 21 '16 at 19:23
  • I should have read your comments there to get the answer. You don't want my answer for how you can actually do it then? – OrangeDog Jul 21 '16 at 19:26

5 Answers5

15

For Spring 4 Frameworks add servletContext.addListener(new RequestContextListener());

public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { RootConfiguration.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebMvcConfiguration.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] { new HiddenHttpMethodFilter() };
    }

    **@Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        super.onStartup(servletContext);
        servletContext.addListener(new RequestContextListener());
    }**
}
Community
  • 1
  • 1
Deepak
  • 1,670
  • 1
  • 20
  • 20
  • For Spring 3 add This in your web.xml org.springframework.web.context.request.RequestContextListener – Deepak Jun 04 '15 at 09:31
  • 3
    The answer was quoted several times... I haven't tried it, but I see no reason why it should work for spawned thread as I totally agree with argumentation provided [here](http://stackoverflow.com/a/21400914/267197). – dma_k Mar 31 '16 at 14:33
14

For spring-boot 2.4 and spring framework 5, both of the RequestContextFilter and RequestContextListener did not work for me.

After digging into the code, I found the DispatcherServlet will overwrite the inheritable of RequestContextHolder set by RequestContextFilter or any one else, see DispatcherServlet.processRequest and DispatcherServlet.initContextHolders.

So the solution is quite simple, without any other components:

@Configuration
class whateverNameYouLike {
   @Bean
   DispatcherServlet dispatcherServlet() {
       DispatcherServlet srvl = new DispatcherServlet();
       srvl.setThreadContextInheritable(true);
       return srvl;
   }
}

But notice that the solution alone only applies to new threads created by the current request thread, not regarding to any thread pool.

For the thread pool cases, you can depend on an extra wraper class:

public class InheritableRequestContextTaskWrapper {
    private Map parentMDC = MDC.getCopyOfContextMap();
    private RequestAttributes parentAttrs = RequestContextHolder.currentRequestAttributes();

    public <T, R> Function<T, R> lambda1(Function<T, R> runnable) {
        return t -> {
            Map orinMDC = MDC.getCopyOfContextMap();
            if (parentMDC == null) {
                MDC.clear();
            } else {
                MDC.setContextMap(parentMDC);
            }

            RequestAttributes orinAttrs = null;
            try {
                orinAttrs = RequestContextHolder.currentRequestAttributes();
            } catch (IllegalStateException e) {
            }
            RequestContextHolder.setRequestAttributes(parentAttrs, true);
            try {
                return runnable.apply(t);
            } finally {
                if (orinMDC == null) {
                    MDC.clear();
                } else {
                    MDC.setContextMap(orinMDC);
                }
                if (orinAttrs == null) {
                    RequestContextHolder.resetRequestAttributes();
                } else {
                    RequestContextHolder.setRequestAttributes(orinAttrs, true);
                }
            }
        };
    }
}

And then use it like this:

InheritableRequestContextTaskWrapper wrapper = new InheritableRequestContextTaskWrapper();
List<String> res = pool.submit(() -> ids.parallelStream().map(
    wrapper.lambda1((String id) -> {
        try {
           // do something and return the result string
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Error occurred in async tasks", e);
        }
    })).collect(Collectors.toList())).get();

uqb
  • 300
  • 4
  • 7
  • this worked with me until the main thread is finished, then the background thread start to throw the IllegalState Exception again. Any idea why? I tried the easier solution which is starting a thread from the main rest api thread: Thread t = new Thread(runnable); t.start() "But notice that the solution alone only applies to new threads created by the current request thread, not regarding to any thread pool." by saing this, that should work, right? btw, what is MDC? how to import it? Thank you for your helpful feedback – Khaled Ahmed Sobhy Aug 12 '22 at 08:01
12

You can only use request (and session) -scoped beans on the web container thread on which the request is running.

I presume that thread is waiting for an async reply from your SI flow?

If so, you can bind the request-scoped bean to the message, perhaps in a header, or somewhere in the payload.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Thanks for you answer. Actually I don't think that thread is waiting for the reply since I'm just trying to persist a object and i need info from that TenantContext bean to do so. – Diego Urenia Jul 02 '13 at 13:17
  • Then you can't use a request scoped bean at all because, by definition, the request no longer exists. – Gary Russell Jul 02 '13 at 14:08
  • Thank you very much Gary, I'm gonna find another way then. – Diego Urenia Jul 02 '13 at 15:52
  • 1
    The documentation describes a `RequestContextListener` as `[A] listener that exposes the request to the current thread`. That seems contradictory to what you've described in your answer. Can you please clarify? – Jon Jan 22 '14 at 15:16
  • 2
    In that context, the "current thread" means the servlet container thread that is processing the HTTP request, not some arbitrary thread that you hand off the request to. – Gary Russell Jan 22 '14 at 16:02
9

Use RequestContextFilter with the property threadContextInheritable set to true. This makes the child thread to inherit the parent's context, which contains the request object itself. Also make sure that the executor doesn't reuse the threads in the pool, because the request object is very specific to that request and cannot be shared across various requests. One such executor is SimpleAsyncTaskExecutor.

For more info refer Scope 'session' is not active for the current thread; IllegalStateException: No thread-bound request found.

Kepotx
  • 1,095
  • 12
  • 26
Thilak
  • 656
  • 7
  • 15
1

You could publish the request in the new Thread like this:

import org.springframework.web.context.request.RequestContextListener;
 ...
ServletRequestEvent requestEvent = new ServletRequestEvent(req.getServletContext(), req);
RequestContextListener requestContextListener = new RequestContextListener();
requestContextListener.requestInitialized(requestEvent);
 ...
requestContextListener.requestDestroyed(requestEvent);

If you look inside requestInitialized()-method you will find a ThreadLocal-variable holding the request. Now you can autowire your request successfully.

Grim
  • 1,938
  • 10
  • 56
  • 123