Using CDI with AsyncResponse and ExecutorService - java

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

Related

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.

Java EE Request scope and Asynchronous

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().

Jersey #ManagedAsync and copying data between HTTP thread and Worker thread

I am working on a project that works in two flavors with and without multi tenancy.
The project exposes a REST service which I would like to be asynchronous.
So my basic service looks like
#Component
#Path("/resouce")
#Consumes(MediaType.APPLICATION_JSON)
public class ResouceEndpoint {
#POST
#ManagedAsync
public void add(final Event event, #Suspended final AsyncResponse asyncResponse) {
resouce.insert (event);
asyncResponse.resume( Response.status(Response.Status.NO_CONTENT).build());
}
}
That works fine without multi tenancy and I get the benefits of the internal Jersey executor service for free. See #ManagedAsync
When I switch to multi tenancy I add a filter on the request that resolve the tenant id and place it on the thread local (in our case the HTTP thread).
When the processing chain hits the "add()" method above the current thread is the one provided by the Jersey executor service, so it does not include my tenant id.
I could think only on the following options to work around this issue.
Extend the ResouceEndpoint to MutliTenantResouceEndpoint and drop the #ManagedAsync
Using my own thread executor
public class MutliTenantResouceEndpoint extends ResouceEndpoint {
#POST
public void add(final Event event, #Suspended final AsyncResponse asyncResponse) {
final String tenantId = getTeantIdFromThreadLocal();
taskExecutor.submit(new Callable<Void>() {
#Override
public Void call() throws Exception {
setTeantIdToThreadLocal(tenantId);
browserEventsAnalyzer.insertEvent(event);
Response response = Response.status(Response.Status.NO_CONTENT).build();
asyncResponse.resume(response);
return null;
}
});
}
}
But this way I need to manage my own thread executor and it feel's like I am missing something here.
Any suggestion on a different approach?
Here are a handful of recommendations, in order.
For context, I've been using Jersey for 2 years, and faced this exact problem 18 months ago.
1. Stop using #ManagedAsync
If you have control over the http server that Jersey is running on, I would recommend you stop using #ManagedAsync.
Instead of setting up Jersey to return it's http handling thread immediately and offload real request work to a managed executor service thread, use something like Grizzly for your http server, and configure it to have a larger worker thread pool. This accomplishes the same thing, but pushes the async responsibility down a layer, below Jersey.
You'll run into many pain points over the course of a year if you use #ManagedAsync for any medium-to-large project. Here are some of them off the top of my head:
If any ContainerRequestFilter's hits an external service (e.g. an auth filter hits your security module, which hits the database) you will lose the benefits you thought you were gaining
If your DB chokes and that auth filter call takes 5 seconds, Jersey hasn't offloaded work to the async thread yet, so your main thread needed to receive a new conn is blocked
If you set up logback's MDC in a filter, and you want that context throughout your request, you'll need to set up the MDC again on the managed async thread
Resource methods are cryptic to new comers and ugly to read because:
they need an extra parameter
they return void, hiding their real response type
they can "return" anywhere, without any actual return statements
Swagger or other API doc tools cannot automatically document async resource endpoints
Guice or other DI frameworks may have trouble dealing with certain scope bindings and/or providers in async resource endpoints
2. Use #Context and ContainerRequest properties
This would involve involved calling requestContext.setProperty("tenant_id", tenantId) in your filter, then calling calling requestContext.getProperty("tenant_id") in your resource with a #Context injected request.
3. Use HK2 AOP instead of Jersey filters
This would involve setting up an HK2 binding of InterceptionService which has a MethodInterceptor that checks for managed async resource methods and manually executes all RequestScoped bound ContainerRequestFilters. Instead of your filters being registered with Jersey, you'd register them with HK2, to be run by the method interceptor.
I can add more detail and code samples to options 2/3 if you'd like, or give additional suggestions, but it would first be helpful to see more of your filter code, and I again suggest option 1 if possible.

JavaEE Web JAX-RS: can i use instance variables inside it's class?

I'm looking for thread-safe Servlet alternative and I've found JAX-RS technology.
So can i use instance variables inside it's class like this (is it thread safe):
#Path("helloworld")
public class HelloWorldResource {
private String msg;
#GET
public void doSmth() {
this.msg = "test";
}
}
?
Resource scope will default to #RequestScope so a new instance of your resource will be created per request.
From Chapter 3. JAX-RS Application, Resources and Sub-Resources
#RequestScoped
Default lifecycle (applied when no annotation is present). In this scope the resource instance is created for each new request and used for processing of this request. If the resource is used more than one time in the request processing, always the same instance will be used. This can happen when a resource is a sub resource is returned more times during the matching. In this situation only on instance will server the requests.
So as long as msg is not static it should be created per request.
This also means that after the request is handled you are going to lose any state contained in the resource, what use case are you trying to solve here?

Using EJBContext getContextData - is this safe?

I am planning to use EJBContext to pass some properties around from the application tier (specifically, a message-driven bean) to a persistence lifecycle callback that cannot directly be injected or passed parameters (session listener in EclipseLink, entity lifecycle callback, etc.), and that callback is getting the EJBContext via JNDI.
This appears to work but are there any hidden gotchas, like thread safety or object lifespan that I'm missing? (Assume the property value being passed is immutable like String or Long.)
Sample bean code
#MessageDriven
public class MDB implements MessageListener {
private #Resource MessageDrivenContext context;
public void onMessage(Message m) {
context.getContextData().put("property", "value");
}
}
Then the callback that consumes the EJBContext
public void callback() {
InitialContext ic = new InitialContext();
EJBContext context = (EJBContext) ic.lookup("java:comp/EJBContext");
String value = (String) context.getContextData().get("property");
}
What I'm wondering is, can I be sure that the contextData map contents are only visible to the current invocation/thread? In other words, if two threads are running the callback method concurrently, and both look up an EJBContext from JNDI, they're actually getting different contextData map contents?
And, how does that actually work - is the EJBContext returned from the JNDI lookup really a wrapper object around a ThreadLocal-like structure ultimately?
I think in general the contract of the method is to enable the communication between interceptors + webservice contexts and beans. So the context should be available to all code, as long as no new invocation context is created. As such it should be absolutely thread-safe.
Section 12.6 of the EJB 3.1 spec says the following:
The InvocationContext object provides metadata that enables
interceptor methods to control the behavior of the invocation chain.
The contextual data is not sharable across separate business method
invocations or lifecycle callback events. If interceptors are invoked
as a result of the invocation on a web service endpoint, the map
returned by getContextData will be the JAX-WS MessageContext
Furthermore, the getContextData method is described in 4.3.3:
The getContextData method enables a business method, lifecycle callback method, or timeout method to retrieve any interceptor/webservices context associated with its invocation.
In terms of actual implementation, JBoss AS does the following:
public Map<String, Object> getContextData() {
return CurrentInvocationContext.get().getContextData();
}
Where the CurrentInvocationContext uses a stack based on a thread-local linked list to pop and push the current invocation context.
See org.jboss.ejb3.context.CurrentInvocationContext. The invocation context just lazily creates a simple HashMap, as is done in org.jboss.ejb3.interceptor.InvocationContextImpl
Glassfish does something similar. It also gets an invocation, and does this from an invocation manager, which also uses a stack based on a thread-local array list to pop and push these invocation contexts again.
The JavaDoc for the GlassFish implementation is especially interesting here:
This TLS variable stores an ArrayList. The ArrayList contains
ComponentInvocation objects which represent the stack of invocations
on this thread. Accesses to the ArrayList dont need to be synchronized
because each thread has its own ArrayList.
Just as in JBoss AS, GlassFish too lazily creates a simple HashMap, in this case in com.sun.ejb.EjbInvocation. Interesting in the GlassFish case is that the webservice connection is easier to spot in the source.
I can't help you directly with your questions regarding EJBContext, since the getContextData method was added in JEE6 there is still not much documentation about it.
There is however another way to pass contextual data between EJBs, interceptors and lifecycle callbacks using the TransactionSynchronizationRegistry. The concept and sample code can be found in this blog post by Adam Bien.
javax.transaction.TransactionSynchronizationRegistry holds a Map-like structure and can be used to pass state inside a transaction. It works perfectly since the old J2EE 1.4 days and is thread-independent.
Because an Interceptor is executed in the same transaction as the ServiceFacade, the state can be even set in a #AroundInvoke method. The TransactionSynchronizationRegistry (TSR) can be directly injected into an Interceptor.
The example there uses #Resource injection to obtain the TransactionSynchronizationRegistry, but it can also be looked up from the InitialContext like this:
public static TransactionSynchronizationRegistry lookupTransactionSynchronizationRegistry() throws NamingException {
InitialContext ic = new InitialContext();
return (TransactionSynchronizationRegistry)ic.lookup("java:comp/TransactionSynchronizationRegistry");
}

Categories