I'm trying to understand under what circumstances the async-supported tag is needed in web.xml for async servlet processing with Jersey. If I'm doing any async work, I typically put it in there. But to illustrate, I have setup a simple sevice
#GET
#Produces(MediaType.APPLICATION_JSON)
public void myService(#Suspended AsyncResponse response) {
new Thread(new Runnable() {
#Override
public void run() {
response.resume(someResponseObject);
}
}).start();
}
In this case, if I don't put async-supported in my web.xml, this works fine.
Is this not working asynchronously as I think or are there some specific operations I need to do to before enabling async-supported is required?
The async-supported option in the web.xml or the equivalent servlet annotation #WebServlet(... asyncSupported=true) is used to indicate on the servlet level that potential long running background work is carried out which should be handled in a separate thread from the one that is handling the request. The thread handling the request is not blocked for this background work, the doGet() or doPost() method returns and the thread can be used to handle another request. The task will be put in a queue and will be handled by a thread from a threadpool initiated on application startup.
This has actually nothing to do with the Jersey AsyncResponse which is handled by the Jersey implementation. Your Jersey REST service is not a servlet.
Related
I am working with Spring Boot 2 and I would like my requests to be handled asynchronously.
In Java EE, one can use Asynchronous Processing for Asynchronous Servlets as in this link. The following is a related example:
#WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet { ... }
and the above allows to use AsyncContext.
But in Spring Boot, I have the following #RequestMapping. How do I make it handle requests in Asynchronous mode and also supporting AsyncContext? How do I leverage the use of an Asynchronous Web Servlet?
#RestController
public class myRestController {
#RequestMapping("{resource}/**")
public void resourceRequest (#PathVariable("resource") String resource) {
// example:
// ... some long running calls such as database communication
// ... convert request through AsyncContext ctx = req.startAsync();
// etc
}
}
Note that returning void is intentional.
I found the following SO answer How to register a servlet with enabled "async-supported" in Spring-Boot? saying that "Spring Boot will automatically register any Servlet beans in your application context with the servlet container. By default async supported is set to true so there's nothing for you to do beyond creating a bean for your Servlet." but I am not using any #WebServlet annotations anywhere in my program, just the #RestController annotation. So how do I ensure that I am benefitting from asyncSupported option?
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
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.
I have a requirement where, as part of a Web Service [ Java ], I enter details of a job into Database, which is asynchronously processed by a Windows Service [ C# ] and makes a HTTP Restful call to the Java web service notifying the status of the job.
The scenario:
Client makes a synchronous Restful call to the Java Web Service.
The Java Web Service enters the job details into database (similar to making an asynchronous call) and waits for a response back from the Windows Service (which is a new HTTP request to the Java Web Service).
Based on the response received, the Java Web Service needs to respond back to the client who is waiting on the synchronous call.
How can we achieve this in the Java Web Service?
EDIT: I've implemented restful web-service using Jersey framework and is running on a Jetty Server.
Depending on which versions of Jersey and Jetty you are using, you may be able to use the support for asynchronously handling requests added in Servlet 3.0. (Based on the Jetty Wikipedia page, I believe you need at least Jetty 8.x. Based on this article, I believe you need at least Jersey/JAX-RS 2.0.)
The Jersey documentation and the JAX-RS API docs have examples of how to asynchronously (i.e. in another thread) complete an outstanding request:
#Path("/resource")
public class AsyncResource {
#GET
public void asyncGet(#Suspended final AsyncResponse asyncResponse) {
new Thread(new Runnable() {
#Override
public void run() {
String result = veryExpensiveOperation();
asyncResponse.resume(result);
}
private String veryExpensiveOperation() {
// ... very expensive operation
}
}).start();
}
}
In your case you would store the AsyncResponse object in such a way that, once you have received the response from the other web service, you complete the request by calling resume with whatever response you want to send to the client.
See also:
What is the purpose of asynchronous JAX-RS
JAX-RS and Long Polling
I have a situation that seems to fit the Async Servlet 3.0 / Comet situation but all I need to do is return a 200 response code (or other) after accepting the incoming parameters.
Is there a way for a HttpServlet to complete the http request/response handshake and yet continue processing?
Something like...
doPost( req, response ) {
// verify input params...
response.setStatus( SC_OK );
response.close();
// execute long query
}
EDIT: Looking at the javax.servlet package - the proper phrasing to my question is
How do I commit a response?
as in Servlet.isCommitted()
Here's how I've handled this situation:
When the app starts up, create an ExecutorService with Executors.newFixedThreadPool(numThreads) (there are other types of executors, but I suggest starting with this one)
In doPost(), create an instance of Runnable which will perform the desired processing - your task - and submit it to the ExecutorService like so: executor.execute(task)
Finally, you should return the HTTP Status 202 Accepted, and, if possible, a Location header indicating where a client will be able to check up on the status of the processing.
I highly recommend you read Java Concurrency in Practice, it's a fantastic and very practical book.
On possibility for your servlet to accept a request for processing in the background, is for the servlet to hand off processing to a separate thread which then executes in the background.
Using Spring, you can invoke a separate Thread using the a TaskExecutor. The advantage of using spring over standard JDK 5 java.util.concurrent.Executor is that if you're on application servers that need to use managed threads (IBM websphere or Oracle weblogic), you can use the WorkManagerTaskExecutor to hook into the CommonJ work managers.
Another alternative would be to move the long query logic into a Message Driven Bean or Message Driven POJO (Spring JMS can help here) and let the servlet simply post a message on a JMS queue. That would have the advantage that should the load on your web container become too great because of your long running query, you could easily move the MDB onto a different (dedicated) system.
You can continue processing in a separate Thread.
The response is commited once you return from doPost() method.
This example can help
void doPost(){
// do something
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
#Override
public void run() {
// processing after response
}
});}