Server Side Events (SSE) in Java

I saw an excellent question on StackOverflow about implementing server side events using Servlet 3.0

Here is a full working example (Servlet 3.0 / Java EE 6) based on my answer

Some notes:

  1. it handles “browser tab / window closed” via out.checkError() that also calls flush()
  2. I wrote it quickly, so I’m sure it can be improved, just a POC, don’t use in production before testing

Servlet: (omitted imports for brevity)

@WebServlet(urlPatterns = {"/mySSE"}, asyncSupported = true)
public class MyServletSSE extends HttpServlet {

  private final Queue<AsyncContext> longReqs = 
      new ConcurrentLinkedQueue<>();
  private ScheduledExecutorService service;

  @Override
  public void init(ServletConfig config) throws ServletException {
    final Runnable notifier = new Runnable() {
      @Override
      public void run() {
        final Iterator<AsyncContext> iterator = longReqs.iterator();
        //not using for : in to allow removing items while iterating
        while (iterator.hasNext()) {
          AsyncContext ac = iterator.next();
          Random random = new Random();
          final ServletResponse res = ac.getResponse();
          PrintWriter out;
          try {
            out = res.getWriter();
            String next = "data: " + 
              String.valueOf(random.nextInt(100) + 1) +
              "num of clients = " + longReqs.size() + "\n\n";
            out.write(next);
            //checkError calls flush, 
            //and flush() does not throw IOException
            if (out.checkError()) { 
              iterator.remove();
            }
          } catch (IOException ignored) {
            iterator.remove();
          }
        }
      }
    };
    service = Executors.newScheduledThreadPool(10);
    service.scheduleAtFixedRate(notifier, 1, 1, TimeUnit.SECONDS);
  }

  @Override
  public void doGet(HttpServletRequest req, 
       HttpServletResponse res) {
    res.setContentType("text/event-stream");
    res.setCharacterEncoding("UTF-8");

    final AsyncContext ac = req.startAsync();
    ac.setTimeout(60 * 1000);
    ac.addListener(new AsyncListener() {
      @Override public void onComplete(AsyncEvent event) throws 
        IOException {longReqs.remove(ac);}
      @Override public void onTimeout(AsyncEvent event) throws 
        IOException {longReqs.remove(ac);}
      @Override public void onError(AsyncEvent event) throws 
        IOException {longReqs.remove(ac);}
      @Override public void onStartAsync(AsyncEvent event) throws 
         IOException {}
    });
    longReqs.add(ac);
  }
}

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>
        <script>
            function test() {
                var source = new EventSource('mySSE');
                source.onopen = function(event) {
                    console.log("eventsource opened!");
                };

                source.onmessage = function(event) {
                    var data = event.data;
                    console.log(data);
                    document.getElementById('sse').innerHTML += 
                        event.data + "<br />";
                };
            }
            window.addEventListener("load", test);
        </script>
    </head>
    <body>
        <h1>Hello SSE!</h1>
        <div id="sse"></div>
    </body>
</html>
 
118
Kudos
 
118
Kudos

Now read this

Cake Pattern in Scala / Self type annotations

(migrated from https://coderwall.com/p/t_rapw) I’ve been trying to understand the cake pattern in Scala for some time, and until I didn’t sit and write some code, it didn’t click. Here is my attempt to explain it (to myself mostly) hope... Continue →