0

I am trying to use Asynchronous Servlets for Web Push Notifications using this example here on oracle page

The code mentioned in the example on this page uses post request to add new data and then consistently send a request using get to check for new data and then display it on all browser instances.

In my case, I would be getting data from the database so I thought to create a simple counter to mock the new data on the server itself and as the counter increases, display those in all browser instances

Like : 1 2 3 4 5 6 7 8 9 

but this is not working, what am I mistaking?

Servlet

@WebServlet(urlPatterns = {"/shoutServlet"}, asyncSupported=true)

public class ShoutServlet extends HttpServlet {
    private List<AsyncContext> contexts = new LinkedList<>();

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    final AsyncContext asyncContext = request.startAsync(request, response);
    asyncContext.setTimeout(10 * 60 * 1000);
    contexts.add(asyncContext);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    List<AsyncContext> asyncContexts = new ArrayList<>(this.contexts);

    this.contexts.clear();
    int counter=10;
    int i =0;
    while(i<counter) {

         ServletContext sc = request.getServletContext();
         if (sc.getAttribute("messages") == null) {
             sc.setAttribute("messages", i);
         } else {
             String currentMessages = (String) sc.getAttribute("i");
             sc.setAttribute("messages", i + currentMessages);
         }
         for (AsyncContext asyncContext : asyncContexts) {
             try (PrintWriter writer = asyncContext.getResponse().getWriter()) {
                 writer.println(i);
                 writer.flush();
                 asyncContext.complete();
             } catch (Exception ex) {
             }
         }
         i++;
    }
}
}

JSP

 <%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>SHOUT-OUT!</h1>
        <form method="POST" action="shoutServlet">
            <table>
                <tr>
                    <td>Your name:</td>
                    <td><input type="text" id="name" name="name"/></td>
                </tr>
                <tr>
                    <td>Your shout:</td>
                    <td><input type="text" id="message" name="message" /></td>
                </tr>
                <tr>
                    <td><input type="submit" value="SHOUT" /></td>
                </tr>
            </table>
        </form>
        <h2> Current Shouts </h2>
        <div id="content">
            <% if (application.getAttribute("messages") != null) {%>
            <%= application.getAttribute("messages")%>
            <% }%>
        </div>
         <script>
            var messagesWaiting = false;
            function getMessages(){
                if(!messagesWaiting){
                    messagesWaiting = true;
                    var xmlhttp = new XMLHttpRequest();
                    xmlhttp.onreadystatechange=function(){
                        if (xmlhttp.readyState==4 && xmlhttp.status==200) {
                            messagesWaiting = false;
                            var contentElement = document.getElementById("content");
                            contentElement.innerHTML = xmlhttp.responseText + contentElement.innerHTML;
                        }
                    }
                    xmlhttp.open("GET", "shoutServlet?t="+new Date(), true);
                    xmlhttp.send();
                }
            }
            setInterval(getMessages, 1000);
        </script>
    </body>
</html>

1 Answer 1

1

It seems to me that you have not entirely understood how async servlets work. The idea is that you release the web thread (usually on the http thread pool, the size of which can be configured on the application server) and then finish the work of the request on some other thread. At the end, you have to call AsyncContext.complete() to actually finish the request and return the response to the client (which is still waiting).

Note that the whole flow will not be any faster (actually it will be a tiny bit slower) than the normal, synchronous handling. The benefit of this is that you can release a HTTP thread sooner, so that it can handle other HTTP requests. Normally the HTTP thread is blocked for the entire time that your doGet/Post method is running, and if you do extensive processing and/or I/O there, this may fill up the HTTP thread pool, and when that happends, your application will not be able to handle any more HTTP requests. (Or, more presicely, a client connection will hang until one of the HTTP threads is available again to handle the request.)

In your code, however, I can't see where you're calling AsyncContext.complete() or finishing the doGet() logic, like return any data to the client. There may be other problems as well, but this is the most obvious reason it's not working.

Other than that, I think you have a typo in doPost() where you're doing sc.getAttribute("i");. The attribute i is not set anywhere, so this will always return null, which will cause a NPE in the next line i + currentMessages.

Also, just in case you expect that this will sum two numbers together - it will not. It will append them, since currentMessages is a String, and the + on a String does a concatenation, no mather what type the other operand is.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.