I have registered a typical SSE when page loads:
Client:
sseTest: function(){
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 />";
};
}
My Javascript-Debugger says, that "eventsource opened!" was successfully.
My Server Code is a Servlet 3.0:
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns={"/mySSE"}, name = "hello-sse", asyncSupported=true)
public class MyServletSSE extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/event-stream");
resp.setCharacterEncoding("UTF-8");
Random random = new Random();
PrintWriter out = resp.getWriter();
//AsyncContext aCtx = req.startAsync(req, resp);
//ServletRequest sReq = aCtx.getRequest();
String next = "data: " + String.valueOf(random.nextInt(100) + 1) + "\n\n";
//out.print("retry: 600000\n"); //set the timeout to 10 mins in milliseconds
out.write(next);
out.flush();
// do not close the stream as EventSource is listening
//out.close();
//super.doGet(req, resp);
}
}
The code works! The Client-Code triggers the doGet()-Method every 3 seconds and retrieves the new data.
Questions: However, I wonder how I can make this code better by using new Servlet 3.0 Futures such as Async-Support or asyncContext.addListener(asyncListener) or something else which I do not know. As I never closes the stream, I wonder how my server will scale?
Theoretically, the best approach would be to trigger the doGet()-Method via server-side-code explicitly when new data is there, so the client does not need to trigger the client-side "onmessage()"-Method and therefore the server side "doGet()"-Method every 3 seconds for new data.
Server-side includes are instructions and directives included in a web page that the web server may analyze when the page is provided. SSI refers to the servlet code that is embedded into the HTML code.
It is not deprecated. They are still releasing new features on the latest Servlet (like async Servlet). And many of the Java web frameworks are building on top the the Servlet technology.
The current version of Servlet is 5.0.
The Model 2 architecture, as shown in Figure 3, integrates the use of both servlets and JSP pages. In this mode, JSP pages are used for the presentation layer, and servlets for processing tasks. The servlet acts as a controller responsible for processing requests and creating any beans needed by the JSP page.
This is an excellent question, here is a full working example (Servlet 3.0 / Java EE 6)
Some notes:
out.checkError()
that also calls flush()
Servlet: (omitted imports for brevity, will update a full gist soon)
@WebServlet(urlPatterns = {"/mySSE"}, asyncSupported = true)
public class MyServletSSE extends HttpServlet {
private final Queue<AsyncContext> ongoingRequests = 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 = 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);
}
}
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With