Java EE Request scope and Asynchronous - java

I am attempting to time an log performance using an interceptor.
#LogPerformance
#Interceptor
public class PerformanceInterceptor {
The times are stored in a request scoped bean. When the the request has finished the times are written to a log file.
When I changed several of the logged methods to be asynchronous, the logging for those operations stopped being output.
#Asynchronous
#LogPerformance
public Future<String> getString() {
I believe the new EJB thread is defining a new request scope. I can log output from the interceptor and see the request scoped object, but there are two different addresses attached to the objects. One address for the http thread and a different address for the EJB thread.
Is there a way to allow the interceptors from the async methods to write to the object in the http request scope? Is there another way to get the data back into the parent scope?

The container will propagate the javax.ejb.EJBContext to the thread that executes the asynchronous method.
You can inject this EJBContext into your interceptor and stash any state (such as your request scoped bean) that you want into it via javax.ejb.EJBContext.getContextData().

Related

How to deal with thread in background processing java spring boot?

I want to download file after query but the time is too long, so my company said I should let the process in background.
For that I create thread when user call method, it will generate file and send to email of customer.
But the problem is about my thread, I also test function for sendemaildownload (it also includes the function which I use to query), I'm quite sure this problem from the way I create the thread.
This is what it says to me when I'm logging the error on creating the thread:
Exception in thread "Nathan-Transportation1" 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: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Is there any way to fix it? I want to understand why it happend when I create my thread pool.
Try using #Async annotation.
For more information see here
This error message indicates that you are attempting to access request-scoped data (for example, request attributes) outside of the context of an HTTP request.
You can try the following solutions to this problem:
To expose the current request and make it accessible to your code,
use a RequestContextListener or RequestContextFilter.
To execute tasks in a background thread, use Spring's TaskExecutor interface, which will handle the necessary context management for you.
Here are some links that may be of interest to you:
RequestContextListener
RequestContextFilter
TaskExecutor

Spring Boot: Unable to access the request scope bean in Spring Scheduler

In my Spring Boot application, i've a Scheduler task which executes for every one hour. In the scheduler method trying to access the request-scope bean. Always getting the exception org.springframework.beans.factory.BeanCreationException.
Here is the code sample.
#Data
public class TestVo {
private String message = "Hello";
}
#Bean
#Scope(value="request", proxyMode=ScopedProxyMode.TARGET_CLASS)
public TestVo testVo() {
return new TestVo();
}
Accessing the above created bean in scheduler method as below,
#Autowired
private TestVo testVo;
#Scheduled(cron="0 0 * * * *")
public void greetings() {
System.out.println(testVo.getMessage()); // accessing request scope bean
}
getting below exception with above code,
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'scopedTarget.scheduledJobTaskExecutor': 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: In this case, use
RequestContextListener or RequestContextFilter to expose the current
request.
The request scoped beans are bounded to specific requests. Every time a request comes, a new instance will be created and after the request finished it will be destroyed. The request is bounded to a thread and use that thread to process the request (in non reactive environment). Even if it was possible, the scheduler wouldnt know, which request object it should use in this situation. Consider you have 100 active request when the scheduled job starts to run, how it should choose one? Or if there arent any active request (so no instance hold by the context?). You can inject request scope into singleton via proxy because the singleton method call will be handled on the same request thread, but the scheduled job use its own thread pool, which not bounded to any requests.
Maybe now you can see the problem using request scoped bean in the scheduler.
If you want to use the same logic in the scheduler and in request scoped beans, you can for example extract it into a superclass.

Using CDI with AsyncResponse and ExecutorService

Before adding CDI into our application I had created a resource that used the #Suspended AsyncResponse object to implement long polling for a chat client. What I did was create a new newSingleThreadExecutor() and submit a Runnable to it that used .wait(30000) on a message list until notification that a new message was sent. Inside that task I used the HttpServletRequest which was obtained using #Context and everything worked perfectly.
However once we added CDI to our application and even without making the resource class a bean (scanning only annotated beans and we didn't give it any scope annotation) I got a runtime exception that the request object INSIDE the Runnable task couldn't be accessed because of an illegal state exception:
Method threw 'java.lang.IllegalStateException' exception. Cannot evaluate com.sun.proxy.$Proxy74.toString()
I'm not really sure why this happens but I know it is CDI related since it refers to a proxy object. One guess is that the resource class itself has become CDI scoped and that scope can't be accessed from a different thread? I read somewhere that manually started threads are not managed and thus can't have access to any scope related objects. However how did this use to work until CDI was implemented?
Right now I THINK I've solved the issue (that is releasing the thread servicing request I/O and having a worker take over the waiting until notified) using jersey's #ManagedAsync annotation which supposedly has the whole method be run in an internal jersey executor service. Is this correct? Also in that case, is there any need of the AsyncResponse object?
EDIT: I have NOT solved the issue. #ManagedAsync worked when the resource class was not defined as a CDI bean. After making it #RequestScoped, whenever I try to call the method I get the following exception
org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
I think this is because the request can end before the async thread has finished which means all scope objects (like HttpServletRequest) will be destroyed and thus we won't have access to them. Is there a way to used #ManagedAsync in a #RequestScoped bean and make use of #Context HttpServletRequest??
TL;DR:
How can I have access to a context resource inside a manually started thread?
Why did I have access to the request object before CDI was implemented?
Is it possible to use #ManagedAsync in a #RequestScoped cdi bean?
Old method:
#GET
#Path("method")
public void method(#Context HttpServletRequest request, #Suspended AsyncResponse ar) {
//request object was accessible here
Executors.newSingleTHreadExecutor().submit(() -> {
//request object was also accessible here but lost access after implementing CDI.
Object o = foo.bar(request);
ar.resume(Response.ok(o).build());
});
}
Current non-working method:
#GET
#Path("method")
#ManagedAsync
public void method(#Context HttpServletRequest request, #Suspended AsyncResponse ar) {
Object o = foo.bar(request);
ar.resume(Response.ok(o).build()); //Is there any point to this?
}
To answer your question - no. You cannot use async and request scoped objects. Async support is lacking in CDI - see also https://issues.jboss.org/browse/CDI-452

How to handle bean lifecycle in an asynchronous request which spawns multiple threads

Question:
Is there a Scope for a Thread and it's spawned Threads which supports it's destruction as soon as all Threads accessing it are terminated? If not do I have to implement it myself or am I getting the concept of Scopes in Spring DI wrong?
Context:
I have a platform which has a REST-API on which processes can be started which are then running on the server. Some of these processes start multiple Threads of which some only terminate on system shutdown (e.g. listening on a stream and doing stuff with the data received).
I want to use Spring for dependency injection and now need to manage beans in a suitable scope.
Problem:
I want to take parameters from the request and provide these at multiple other locations. My approach is to take a container bean which is populated in the request handler and then used at all other occasions. The #Scope("request") is destroyed as soon as the response is send which happens instantly (since the handler only spawns a Thread) thus it's not applicable here.
I read about the ThreadScope implementation from springbyexample (http://www.springbyexample.org/examples/custom-thread-scope-module.html) and a way to modify the spring SimpleThreadScope to support inheritance within the hierarchy of spawned threads (https://stackoverflow.com/a/14987371/4502203). Both are only solving parts of my Problem.
What I need is a Scope which supports destruction callbacks (since I'm not keen on memory leak) and is inherited to Child-Threads.
Code Example:
#RequestMapping(value = "/myApi/{parameterA}/{parameterB}", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
public #ResponseBody void doFancyStuff(#PathVariable String parameterA, #PathVariable String parameterB) {
new Thread(() -> {
ParameterContainer parameterContainer = applicationContext.getBean(ParameterContainer.class);
parameterContainer.setParA(parameterA);
parameterContainer.setParB(parameterB);
/*
* spawn a couple of additional threads here which
* need to get access to the ParameterContainer.
*/
}
}

Call controller form another bean. No thread-bound request found

I'm developing a spring mvc and i want my controller to listen application events
via #controller I send a jms message from a web page
and I'm trying to notify the controller when receiving a message jms, in order to push some data to a web page
First I've tried with the observer pattern, implementing the controller as an ApplicationListener
# Controller ("MyController")
# Scope (value = "session" = proxyMode ScopedProxyMode.TARGET_CLASS)
public class MyController implements Serializable, ApplicationListener <CustomEvent>
Then I've tried to call a controller method from another bean when receiving a jms message
#Resource(name="MyController")
private MyController c;
c.update(String data);
But I'm getting always the same error
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.
Is there a way to call a controller from another bean?
Thanks
The problem is that your controller is session-scoped, and you try to call its method from a context where no HTTP session is available.
So, I assume that your controller combines functionality that depends on HTTP session (thus session scope), and functionality that need to be called outside of a session (such as your update() method).
If so, you can solve this problem by moving functionality that doesn't require HTTP session into separate bean (without session scope).

Categories