0

I am trying to implement a solution for a long running process on the server where it is taking about 10 min to process a pdf generation request. The browser bored/timesout at the 5 mins. I was thinking to deal with this using a Ajax and threads. I am using regular javascript for ajax. But I am stuck with it.

I have reached till the point where it sends the request to the servlet and the servlet starts the thread.Please see the below code

public class HelloServlet extends javax.servlet.http.HttpServlet 
     implements javax.servlet.Servlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {      
    }   

    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        System.out.println("POST request!!");
        LongProcess longProcess = new LongProcess();
        longProcess.setDaemon(true);
        longProcess.start();
        request.getSession().setAttribute("longProcess", longProcess);
        request.getRequestDispatcher("index.jsp").forward(request, response);
    }   
 }

    class LongProcess extends Thread {

        public void run() {
            System.out.println("Thread Started!!");
            while (progress < 10) {             
                try { sleep(2000); } catch (InterruptedException ignore) {}
                progress++;
            }
        }           
    }

Here is my AJax call

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>My Title</title>
<script language="JavaScript" >
function getXMLObject()  //XML OBJECT
    {
       var xmlHttp = false;
         xmlHttp = new XMLHttpRequest();        //For Mozilla, Opera Browsers
       return xmlHttp;  // Mandatory Statement returning the ajax object created
    }

    var xmlhttp = new getXMLObject();   //xmlhttp holds the ajax object

    function ajaxFunction() {
        xmlhttp.open("GET","HelloServlet" ,true);
        xmlhttp.onreadystatechange  = handleServerResponse;
        xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xmlhttp.send(null);
      }

    function handleServerResponse() {
       if (xmlhttp.readyState == 4) {
         if(xmlhttp.status == 200) {           
           document.forms[0].myDiv.value = xmlhttp.responseText;
           setTimeout(ajaxFunction(), 2000);
         }
         else {
            alert("Error during AJAX call. Please try again");
         }
       }
    }
    function openPDF() {    
           document.forms[0].method = "POST";
        document.forms[0].action = "HelloServlet";
        document.forms[0].submit();     
    }
    function stopAjax(){
         clearInterval(intervalID);
     }
</script>
</head> 
<body><form name="myForm">
<table><tr><td>
<INPUT TYPE="BUTTON" NAME="Download" VALUE="Download Queue ( PDF )" onclick="openPDF();">
</td></tr>
<tr><td>
Current status: <div id="myDiv"></div>%
</td></tr></table>
</form></body></html>

But I dont know how to proceed further like how will the thread communicate the browser that the process has complete and how should the ajax call me made and check the status of the request.

Please let me know if I am missing some pieces. Any suggestion if helpful.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
John Blue
  • 1
  • 1
  • 1

2 Answers2

1

Jetty 9.0 Distribution includes a long polling chat example, which includes an async servlet and javascript client that work in tandem. A poll request is initiated by the client which causes the servlet to start an async loop that deliberately times out at a chosen interval. Some browsers last a long time, however some only last 30 seconds. So, setting the timeout to less than 30 seconds is recommended. When the servlet times out, it sends a signal to the client causing the client to intiate another poll. Data can be sent at any time through the response, and the client can simply connect again thereafter if necessary. This has the effect of establishing an open channel from a server to a client.

// This starts an async request
AsyncContext asyncCtx = request.startAsync();
asyncCtx.setTimeout(10000);   // 10 seconds
asyncCtx.addListener(member);

// This is the timeout handler which tells the client to continue to poll
@Override
public void onTimeout(AsyncEvent event) throws IOException {
    System.out.println("Client onTimeout\r\n");
    AsyncContext asyncCtx = asyncCtxAtomRef.get();
    if ((asyncCtx != null) && asyncCtxAtomRef.compareAndSet(asyncCtx, null))
    {
        HttpServletResponse response = (HttpServletResponse)asyncCtx.getResponse();
        response.setContentType("text/json;charset=utf-8");
        PrintWriter out=response.getWriter();
        out.print("{action:\"poll\"}");
        asyncCtx.complete();
    }
}

Essentially, any response sent to the client with an action of "poll", has the effect of causing the client to automatically reconnect. It seems to work really well, so you might want to check it out.

djabraham
  • 766
  • 11
  • 20
0

You can either use Servlet 3.0 asynchronous processing or existing libraries like (using servlet 3.0 underneath).

The idea is to call servlet and start AsyncContext. You then pass that context to your thread and use it to periodically send some progress. On the client side reading this stream is a bit tricky, see: jquery ajax, read the stream incrementally? Do not access original HttpServletResponse inside a thread, it won't work.

Here is a sample code:

@WebServlet("/hello" asyncSupported=true)
public class HelloServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse res) {
        AsyncContext async = request.startAsync(req, res);
        LongProcess longProcess = new LongProcess();
        longProcess.setDaemon(true);
        longProcess.start();
    }
}

class LongProcess extends Thread {

    private final AsyncContext asyncContext;

    public LongProcess(AsyncContext asyncContext) {
        this.asyncContext = asyncContext;
    }

    public void run() {
        System.out.println("Thread Started!!");
        while (progress < 10) {             
            try { sleep(2000); } catch (InterruptedException ignore) {}
            progress++;
            //use asyncContext here to send progress to the client incrementally
        }
    }           
}

See also Asynchronous Support in Servlet 3.0.

You can also use library which will do it for you and works quite well.

Community
  • 1
  • 1
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • Thank you very much for response. The problem is i dont have the liberty to use servlet 3.0 as this is a legacy application and whole system is implemented way back. I will have to stick to servlet 2.x :( – John Blue Aug 02 '12 at 17:28
  • @JohnBlue: in that case you can periodically check the background process status - a bit cumbersome, but proven to work. – Tomasz Nurkiewicz Aug 02 '12 at 17:32
  • Sure. I was thinking the same. But I am unable to figure out few things. the thread commincation between the servlet and the longprocess thread. and The polling from AJAX. DO i have to introduce an intermediate screen? – John Blue Aug 02 '12 at 18:49