Since the dawn of modern web applications, push notifications have gained significant traction in industry. Instead of polling data from server it has now become common that server should notify client. Ajax is so pervasive these days it is almost seen on every web page we visit. This is all good if you want to refresh a part of page every few seconds. But things get ugly if you want tens or hundreds of requests a second. Polling webserver is too expensive in such cases. With that in mind HTML5 has introduced a nifty cool feature “Server-Sent Events”.
1. Introduction to Server-Sent Events
The Server-Sent Events are the new APIs for opening an HTTP connection for receiving push notifications from a server in the form of DOM events. Consider below javascript code:
//...
var eventSource = new EventSource("/some/data.html");
eventSource.onmessage = function(event) {
alert(event.data);
};
//...
Code language: JavaScript (javascript)
The above code can be executed in any modern browser (ofcourse the server-side script /some/data.html needs to be implemented first). We create an object of class EventSource. We pass a server URL which implements Server-sent events protocol. And finally we add a handler function on .onmessage. Every time server sends a message, the handler is called and the event object will have the data. No need for polling. The javascript handler will be asynchronously called. The server needs to specific. First the response content type must be set to text/event-stream
. And the data needs to be sent in following format.
... data: This is some data data: a quick brown fox data: jumps over a lazy dog. ...
You got the idea. Lets quickly create a Java Servlet based application with Client code for Server-sent event.
2. Server-Sent Events, Hello World Servlet
For our hello world example, we create an html page that has a button to start server-sent event. The server will send us a timestamp every second which we just display on page.
index.jsp
<!DOCTYPE html>
<html>
<body>
Time: <span id="foo"></span>
<br />
<br />
<button onclick="start()">Start</button>
<script type="text/javascript">
function start() {
var eventSource = new EventSource("HelloServlet");
eventSource.onmessage = function (event) {
document.getElementById("foo").innerHTML = event.data;
};
}
</script>
</body>
</html>
Code language: HTML, XML (xml)
In above javascript code we created an event source for “/HelloServlet” path. On each message we just prints the data in span “foo”. Now lets check Servlet code which sents the Server-Sent Events.
HelloServlet.java
package net.viralpatel.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//content type must be set to text/event-stream
response.setContentType("text/event-stream");
//encoding must be set to UTF-8
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
for (int i = 0; i < 10; i++) {
writer.write("data: " + System.currentTimeMillis() + "\n\n");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
writer.close();
}
}
Code language: Java (java)
The servlet is quite simple. Content type must be set to “text/event-stream” and the character encoding must be UTF-8. Also each message that we send must ends with.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>ServerSentEvent_HttpServlet_example</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<display-name>HelloServlet</display-name>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>net.viralpatel.servlets.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>
</web-app>
Code language: HTML, XML (xml)
Output
Source code:https://gist.github.com/viralpatel/7007662
3. Multiple Events in Server-Sent Events
In above example we tracked a single event. The servlet sent only single data entity. In real world, you might want to send a number of events and want to track same on client side. For example, consider below Javascript snippet.
var eventSource = new EventSource("HelloServlet");
eventSource.addEventListener(
"up_vote",
function (event) {
document.getElementById("up").innerHTML = event.data;
},
false
);
eventSource.addEventListener(
"down_vote",
function (event) {
document.getElementById("down").innerHTML = event.data;
},
false
);
Code language: JavaScript (javascript)
In above code we used addEventListener()
method of EventSource to add a handler function. We added handler to event “up_vote” and “down_vote”. Whenever any of this event changes, we need to update the count on html page. The server must send the data in format:
... event: up_vote data: 10 event: down_vote data: 5 event: up_vote data: 12 event: down_vote data: 9 ...
The Java Servlet code for this example should be: HelloServlet.java
package net.viralpatel.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
int upVote = 0;
int downVote = 0;
for (int i = 0; i < 20; i++) {
upVote = upVote + (int)(Math.random() * 10);
downVote = downVote + (int)(Math.random() * 10);
writer.write("event:up_vote\n");
writer.write("data: " + upVote + "\n\n");
writer.write("event:down_vote\n");
writer.write("data: " + downVote + "\n\n");
writer.flush();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
writer.close();
}
}
Code language: Java (java)
In above servlet code, we randomly incremented two ints upVote and downVote. Also we sent these ints to client with:
writer.write("event:up_vote\n");
writer.write("data: " + upVote + "\n\n");
Code language: Java (java)
Notice single \n in event and double \n\n in data. You should get following output when the servlet is executed.
3.1. The EventSource API
In the examples above we used the onmessage event to get messages. But other events are also available:
Events | Description |
---|---|
onopen | When a connection to the server is opened |
onmessage | When a message is received |
onerror | When an error occurs |
4. Browser compatibility
As of Oct 2013 – Internet explorer is your enemy :) Server-sent events are supported in all major browsers (Firefox, Opera, Chrome, Safari.) except for Internet Explorer.
References
- W3C Specification: Server-Sent Events
- Mozilla Development Center: Using server-sent events
- Server-Sent Events – W3Schools
Hi,
thank you for this tutorial.
The example “2. Server-Sent Events, Hello World Servlet” works for me only if I flush the servlet data at every update, so the writer becomes:
—
Marcello
hi, nice post and nice blog !!
just one question…
this kind of event are broadcasted to every client?
how could i target different event to different client?
Hi,
Thank you for this tutorial, I run :
example “2. Server-Sent Events
I don’t know why when the loop reach 20 it start over again, any explaination will be appreciated.
Thanks
The magical part is that whenever the connection is closed, the browser will automatically reconnect to the source after ~3 seconds. Your server implementation can even have control over this reconnection timeout. See Controlling the reconnection-timeout in the next section.
Hi,
Can we sent image and video also from the server side ? Or we can sent only text data.
Thanks,
Hi,
thanks for this nice tutorial. Hoping we see more in future..
How could we add support for IE 9 and IE10 using polyfills?
https://github.com/Yaffle/EventSource/blob/master/eventsource.js
I am creating one finanace erp. I want one help. I have one table in database LAON its attributes are (loan_id,Cust_ID,PDate,Down_pay,Installment,FIDate,Xtra_pay,SPDate,dues).Now m query is FIDate is 12th jan 2015 den admin should get notification on jsp page that particulat customer’s installment date has arrived on 12th jan. Plz tel me hoe to do this?
Hi Nice Post I tried your example and it worked perfectly, but when i tried to hit doPost method from EventSource it didn’t worked Please help me
I tried using gzip compression by enabling in server.xml for event stream
but apparently all the results come all together
Is this proper behaviour?
Thanks, you solved my problem
I implemented it and when I checked the network tab, I see the request is made every time to the said servlet. It just as normal as I would make ajax request in a loop. If the writer.write() executes, the client receives the response. And then again a new request is made. Is there anything I am missing? I am searching for something in which the server sends the data without any request is made from the client side. Or may be something like I redirect to this helloservlet from any other servlet, on which the hello servlet pushes data to the client. Its not that the client explicitly requests everytime. Any help is much much appreciated, thanks a lot for helping since long time. Cheers.
I think it a bit (a lot?) difference.
With AJAX, the client manually make request to the server. Say, you have 1000 client, every 100ms client send a request. Then, in 1 second you have 1000*(1000/100) = 10K call.
But your data is not change that frequently. It change, say, every 10 seconds.
So, does it make any sense? The number of requests to server change pretty much, right?
P/s: If you have an application that change data frequently. You should take a look at web socket.
Server push event is good for notification, not for frequently changing data. This just a demo how it work and how you iterate it with Java.
hi tank you for this.
but it’s not work on struts2 frame work.
is it pool or poll………….”Instead of pooling data from server”