Performing a job after closing the http-connection in a servlet - java

I want a user to make a post, the data in the post is handled and the request is forwarded to a jsp-page. The jsp-page is sent back to the user but then the server should do a job. This job takes a lot of time so the user shouldn't wait for it.
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
//... get posted data ...
request.getRequestDispatcher("/page.jsp").forward(request, response);
//do-someting
}
I want the "do-something" to be done after the connection is closed and the user gets the page.jsp. Of course the "do-something" is completely serverside like updating a database etc. So the server keeps working even after the user leaves the page. I tried to flush and close the outputstream in the servlet after the forward to the jsp-page but this results in an exception. Also flushing and closing in the jsp-page results in an exception. How can I do this?

If your job (do-something) is completely independent of what should be displayed in the jsp, then you can use one of the following:
A timer
A thread (in a thread pool)
A more powerful library like Quartz which will give you powerful scheduling capabilities

Related

Close request and start asynchronous computation in Java

i'm trying to create an asynchronous servlet, so that the client makes a request, get an answer like "process started" and stops waiting.
The servlet will start some computation and write the result on a DB and then the user will be able to check if the process is terminated and to see its result.
Well, every time i try to do this a get errors. In the request i have a Connection object, and if i pass it to a Thread and then start it, when the main servlet ends the connection gets closed and the Thread throws an Exception ('connection has already been closed').
Is there a way to just give to the client a response and tell it not to wait anymore while the servlet does all its job?
Thank you in advance!
If you were mentioning a DB Connection, why not create the Connection inner your Thread?
There exists Ajax (JavaScript) in the browser to place a call, and have success/failed callbacks received later. That would be the most regular way.
If more is required, register the request for scheduling in a queue = store something in the database. Then have a timer task (for instance from Spring) in your app that does the work. This prevents overloading, DoS.
In fact if looks like the response is fully sent to the client when you close the response output stream (or writer), and you can continue the processing inside the servlet without any threading issues. Unfortunately I could never found a confirmation that if was explicitely allowed in servlet specs, but I could do it successfully in Tomcat (was using version 7)
So provided you note it in a bold comment, your servlet code could look like:
void service(ServletRequest req, ServletResponse resp) {
OutputStream out = res.getOutputStream();
... // generate the output
out.close(); // the response is sent to client
// asynchonous processing
...
}

Is it legal to write and close the response in JSP, then do some extra job?

I'm working on a web app, which is communicating with the server with AJAX requests. A special type of "close" request takes 5 secs, which the web app should just fire-and-forget, the result is irrelevant. Due to browser behaviors (only limited number of simultaneous AJAX requests are performed), a 5-sec request may stuck other AJAX requests, which is unacceptable.
The smart folks here in StackOverflow has adviced me to write a small server-side proxy, which the web app should call instead of the original 5-sec one. The proxy should response immediatelly, close response channel, then perform a HTTP request and wait for it, spending the 5 secs server-side, instead of client-side. (The original question is here: See Is there a way to perform fire-and-forget AJAX request? )
The server is a Tomcat with JSP, and I can write small JPS pages. (I'm not an experienced JSP ninja, but I don't afraid of Java.) My question is: is it legal to write such a JSP, or what's the best practice:
send the response,
close reply channel (is out.close() enough?), in order to end the AJAX request at client-side,
fire and process (actually: just drop response) a HTTP request "in background", which may take as long as 5 secs?
It's not (only) your browser you should worry about. Blocking a tomcat thread for 5s severly limits your max-users as well (how many requests per second do you need to handle ultimately?)
So making it "more" asynchronous in the server might make sense.
Doing it in JSP (with Sriplets?!) alone will noway be a robust implementation - but if you need to do it that way, you should think about starting the "work to do" in a separate Thread.
So instead of
<%
do_something_heavy();
%>
You'll do like
<%
new Thread(new Runnable() {
public void run() {
do_something_heavy();
}
}).start();
%>
There's other options as well (JMS, ExecutorService, Spring #Async...) but this should get you started quick.
First the best is to separate business logic from view: it means write java code on a servlet and delegate only the view aspect to the jsp.
To execute your task asynchronously in the servlet code you can:
Invoke a submit method of an ExecutorService
Make a call to a JMS
Manually create a thread and start it
Then you can forward to the jsp.
TIP: It is possible to assign an id to the long task and return it in the jsp with a link to monitor the status of the task.
Basically you do something like that:
Accept the request
Start asynchronously a thread to execute the long task
Return immediately without waiting for the long task termination
Or using an id:
Accept the request
Calculate the id of the task
Start asynchronously a thread to execute the long task with the desired id
Return immediately a link with the id of the long task without waiting for the termination

Jetty make doPost asyncronously

We have a long-working doPost method, which blocks Jetty thread
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
// long prepare answer (which depends on external sources wait)
// here must be something like yield
PrintWriter out = response.getWriter();
out.println(answer);
}
Is there a way to do the long prepare asynchronously and yield to another Jetty tasks there while it will not be completed?
You want to use Jetty's Continuation API. Basically a continuation is a mechanism where you start a potentially long running operation in an asynchronous manner and then you free up the Jetty thread that is handling the current request. When your asynchronous operation completes you use a continuation to resume the processing of the request and at that point you can finish the response. See Continuation
You'll want to use 2 features of the Servlet spec.
AsyncContext - The Asynchronous Processing feature of the Servlet spec - allowing you to suspend the active processing until such time you actually have some content to generate back on the response. (this is an over-simplification, but you get the gist)
HttpServletResponse.getOutputStream().setWriteListener(WriteListener) - The Asynchronous I/O feature of the Servlet spec - for asynchronously generating / writing a response back to the client. (this is also a simplification, but be aware of the gotchas when working with this)

Does Servlet return response after doGet method has finished?

Obviously, the doGet method has a return type of void, so, it doesn't return anything. In this sense, I'm using the word "return" to mean send the response back to the client that requested it.
I'm trying to implement a long-polling Servlet. It would be beneficial for it not to send a response until I have something that I would like to send back. So, in the doGet method I add the connected user's ID and AsyncContext to a map:
private ConcurrentMap<String, AsyncContext> contexts = new ConcurrentHashMap<>();
//...in the doGet method when I need to add the context...
contexts.put(userId, context);
Then, when I have something to send back, I can retrieve the appropriate context and write to it's responses output stream:
AsyncContext context = contexts.get(userId);
PrintWriter writer = context.getResponse().getWriter();
writer.write("something to send to the client");
But, the client never seems to receive the response. Looking at the Network tab in the developer console of the browser, I can see the GET request is sent and then returns (with a status of 200). This occurs before I actually send something back. Which is leading me to believe that after the doGet method is finished the response is returned. And perhaps because of this, after this point, nothing can be sent to the client because the connection is not opened.
Does the doGet method send the response to the client once the method is finished executing? If this is the case, how can I keep the connection open for a long-polling effect?
To answer my own questions: Does the doGet method send the response to the client once the method is finished executing?
Yes, when the doGet (or any HttpServlet method, ex: doGet, doPost, etc.) method finishes executing it sends the response back to the client.
If this is the case, how can I keep the connection open for a long-polling effect?
Using asynchronous Servlets (which I was using, however, I found my particular problem must be elsewhere, yet these answers are still relevant to the questions asked). On the ServletRequest object call the startAsync method, like so:
AsyncContext context = request.startAsync(request, response);
"This will notify the Web Container that at the end of the request call it should free the handling thread and leave the connection open so that other thread writes the response and end the connection."Reference Link.
Also, I will add the solution to my particular problem (the client wasn't receiving the response) was because in my Servlet, I wasn't calling the complete method on the AsyncContext object:
asyncContext.complete();
Yes, the response stream is flushed and closed when doGet() finishes executing.
Keeping UI threads occupied for extended periods of time violates Java Enterprise best practice.
Recommend you rather return immediately if nothing to respond, and implement a timer on the client (browser) side to poll the server for results every so often.

Call a Web Service from Servlet at AppEngine

Question: What is best way to call a web service (0.5-1.5 seconds/call) from a servlet at AppEngine? Are blocking calls are scalable at AppEngine environment?
Context: I am developing a web application using AppEngine and J2EE. The applications calls Amazon web service to grab some information for the user. From my asp.net experience, best way to do the calls - is to use async http handler to prevent starvation at IIS thread pool. This feature is not available for J2EE with Servlet 2.5 spec (3.0 is planned).
Right now I am thinking of making my controllers (and servlets) thread safe and request scoped. Is there anything also that I can do? Is it even an issue in J2EE + AppEngine environment?
EDIT: I am aware of AppEngine and JAX-WS async invocation support, but I am not sure how it play with servlet environment. As far as I understand, to complete servlet request, the code still should wait for async WS call completion (callback or whatever).
I assume that doing it using synchronization primitives will block current working thread.
So, as far as thread is blocked, to serve another user request servlet container need to allocate new thread in thread pool, allocate new memory for stack and waste time for context switching. Moreover, requests can block entire server, when we run out of threads in thread pool. This assumptions are based on ASP.Net and IIS thread model. Are they applicable to J2EE environment?
ANSWER: After studying Apache and GAE documentation, it seems that starvation of threads in the thread pool is not a real issue. Apache, by default has 200 threads for thread pool (compared to 25 in asp.NET and IIS). Based on this I can infer that threads are rather cheap in JVM.
In case if async processing is really required or servlet container will run out of threads, it's possible to redesign the application to send response via google channel api.
The workflow will look like:
Make sync request to servlet
Servlet makes creates channel for async reply and queues task for background worker
Servlet returns response to client
[Serving other requests]
Background worker does processing and pushes data to client via channel api
As you observe, servlets don't support using a single thread to service multiple concurrent requests - one thread is required per request. The best way to do your HTTP call is to use asynchronous urlfetch, and wait on that call to complete when you need the result. This will block the request's thread, but there's no avoiding that - the thread is dedicated to the current request until it terminates no matter what you do.
If you don't need the response from the API call to serve the user's request, you could use the task queue to do the work offline, instead.
Isn't it OK to use fetchAsync?
looks at this, this might help
http://today.java.net/pub/a/today/2006/09/19/asynchronous-jax-ws-web-services.html
I am not sure, If you can exactly replicate what you do in dot net, Here is what you could do to may be to simulate it page on load
Submit an ajax request to controller using a java script body onload
In the controller start the async task and send the response back the user and use a session token to keep track of the task
You can poll the controller (add another method to ask for update of the task, since you have session token to track the task) until u get the response
You can do this either waiting for response page or hidden frame that keeps polling the controller
Once you have the response that you are looking for remove the session token
If you want to do that would be the best option instead of polling would be ideal in this case Reverse Ajax / server push
Edit: Now I understand what you mean, I think you can have your code execute async task not wait for response from async itself, just send response back to the user. I have simple thread that I will start but will wait for it to finish as I send the response back to the user and the same time use a session token to track the request
#Controller
#RequestMapping("/asyncTest")
public class AsyncCotroller {
#RequestMapping(value = "/async.html", method = RequestMethod.GET)
public ModelAndView dialogController(Model model, HttpServletRequest request)
{
System.err.println("(System.currentTimeMillis()/1000) " + (System.currentTimeMillis()/1000));
//start a thread (async simulator)
new Thread(new MyRunnbelImpl()).start();
//use this attribute to track response
request.getSession().setAttribute("asyncTaskSessionAttribute", "asyncTaskSessionAttribute");
//if you look at the print of system out, you will see that it is not waiting on //async task
System.err.println("(System.currentTimeMillis()/1000) " + (System.currentTimeMillis()/1000));
return new ModelAndView("test");
}
class MyRunnbelImpl implements Runnable
{
#Override
public void run()
{
try
{
Thread.sleep(5000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}

Categories