Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>This is an excellent question, here is a full working example (Servlet 3.0 / Java EE 6)</p> <p>Some notes:</p> <ol> <li>it handles "browser tab / window closed" via <code>out.checkError()</code> that also calls <code>flush()</code> </li> <li>I wrote it quickly, so I'm sure it can be improved, just a POC, don't use in production before testing</li> </ol> <p><strong>Servlet:</strong> (omitted imports for brevity, will update a full gist soon)</p> <pre><code>@WebServlet(urlPatterns = {"/mySSE"}, asyncSupported = true) public class MyServletSSE extends HttpServlet { private final Queue&lt;AsyncContext&gt; ongoingRequests = new ConcurrentLinkedQueue&lt;&gt;(); private ScheduledExecutorService service; @Override public void init(ServletConfig config) throws ServletException { final Runnable notifier = new Runnable() { @Override public void run() { final Iterator&lt;AsyncContext&gt; iterator = ongoingRequests.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 = " + ongoingRequests.size() + "\n\n"; out.write(next); if (out.checkError()) { //checkError calls flush, and flush() does not throw IOException 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 {ongoingRequests.remove(ac);} @Override public void onTimeout(AsyncEvent event) throws IOException {ongoingRequests.remove(ac);} @Override public void onError(AsyncEvent event) throws IOException {ongoingRequests.remove(ac);} @Override public void onStartAsync(AsyncEvent event) throws IOException {} }); ongoingRequests.add(ac); } } </code></pre> <p><strong>JSP:</strong></p> <pre><code>&lt;%@page contentType="text/html" pageEncoding="UTF-8"%&gt; &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt; &lt;title&gt;JSP Page&lt;/title&gt; &lt;script&gt; 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 + "&lt;br /&gt;"; }; } window.addEventListener("load", test); &lt;/script&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Hello SSE!&lt;/h1&gt; &lt;div id="sse"&gt;&lt;/div&gt; &lt;/body&gt; &lt;/html&gt; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload