Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Servlet 3.0 server push: Sending data multiple times using same AsyncContext

Lead by several examples and questions answered here ( mainly http://www.javaworld.com/javaworld/jw-02-2009/jw-02-servlet3.html?page=3 ), I want to have server sending the response multiple times to a client without completing the request. When request times out, I create another one and so on.

I want to avoid long polling, since I have to recreate request every time I get the response. (and that quite isn't what async capabilities of servlet 3.0 are aiming at).

I have this on server side:

@WebServlet(urlPatterns = {"/home"}, name = "async", asyncSupported = true) 

public class CometServlet extends HttpServlet {

    public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException {

        AsyncContext ac = request.startAsync(request, response);

        HashMap<String, AsyncContext> store = AppContext.getInstance().getStore();

        store.put(request.getParameter("id"), ac);

    }
}

And a thread to write to async context.

class MyThread extends Thread {
    String id, message;

    public MyThread(String id, String message) {
        this.id = id;
        this.message = message;
    }

    public void run() {
        HashMap<String, AsyncContext> store = AppContext.getInstance().getStore();
        AsyncContext ac = store.get(id);
        try {
            ac.getResponse().getWriter().print(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

But when I make the request, data is sent only if I call ac.complete(). Without it request will always timeout. So basically I want to have data "streamed" before request is completed.

Just to make a note, I have tried this with Jetty 8 Continuation API, I also tried with printing to OutputStream instead of PrintWriter. I also tried flushBuffer() on response. Same thing.

What am I doing wrong?

Client side is done like this:

    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'http://localhost:8080/home', true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 3 || xhr.readyState == 4) {
            document.getElementById("dynamicContent").innerHTML = xhr.responseText;
        }
    }
    xhr.send(null);

Can someone at least confirm that server side is okay? :)

like image 287
Nob Venoda Avatar asked Feb 16 '23 10:02

Nob Venoda


1 Answers

Your server-side and client-side code is indeed ok. The problem is actually with your browser buffering text/plain responses from your web-server. This is the reason you dont see this issue when you use curl.

I took your client-side code and I was able to see incremental responses, with only just one little change:

response.setContentType("text/html");

The incremental responses showed up immediately regardless of their size.

Without that setting, when my output was a small message, it was considered as text/plain and wasnt showing up at the client immediately. When I kept adding more and more to the client responses, it got accumulated until the buffer size reached about 1024 bytes and then the whole thing showed up on the client side. After that point, however, the small increments showed up immediately (no more accumulation).

like image 75
2020 Avatar answered Apr 06 '23 22:04

2020