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:
- it handles “browser tab / window closed” via
out.checkError()
that also callsflush()
- 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>