Start Server-Sent Events (SSE) to send and receive data from a server and clients with a
chat service.
Before you begin
SSE is part of HTML5. To start SSE, use a Java™ client,
such as Java API for RESTful Web Services (JAX-RS) 2.1 or JavaScript.
About this task
SSE was first introduced in HTML5. A client, such as a browser, creates an HTTP connection
to a server and sends an initial request. However, instead of closing the connection, the client and
server keep the connection open, and the server sends data to the client as needed. The client then
processes the data as events. Either the server or the client can close the connection, but if two
clients remain connected, they can see events that the server sends.
Procedure
-
Start SSE with JAX-RS 2.1.
The following server code shows a resource that fires SSE and sends data back to a specific
client. The connection to the server stays open so that the server can send information at any time.
This example RESTful service provides music radio information. Create the
RadioInfoService.java file in the web application.
@GET
@Path("/songDataStream")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void songDataStream(@Context SseEventSink eventSink, @Context Sse sse) {
Runnable r = new Runnable() {
@Override
public void run() {
Song song = RadioService.getCurrentSong();
while (song != null) {
OutboundSseEvent event = sse.newEventBuilder()
.mediaType(MediaType.APPLICATION_JSON_TYPE)
.data(Song.class, song)
.build();
eventSink.send(event);
try {
Thread.currentThread().sleep(song.timeRemaining());
} catch (InterruptedException ex) {
// ...
}
}
}
}
new Thread(r).start();
}
-
Create a JAX-RS client in a .java file.
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost/RadioSvc/rest/songDataStream");
try (SseEventSource source = SseEventSource.target(target).build()) {
source.register(new Consumer<InboundSseEvent>(){
@Override
public void accept(InboundSseEvent event) {
// called when we receive a new event
Song song = event.readData(Song.class);
System.out.println("Now playing " + song.title() + " by " + song.artist());
}}, new Consumer<Throwable>(){
@Override
public void accept(Throwable t) {
// called when something went wrong
t.printStackTrace();
}}, new Runnable() {
@Override
public void run() {
// called when our connection is closed
System.out.println("All done for now!");
}});
source.open();
Thread.sleep(3600000); // Consume song events for one hour
} catch (InterruptedException e) {
e.printStackTrace();
}
- Optional:
If you cannot convert the JSON interceptors back into Java objects, add a
MessageBodyReader
and a
MessageBodyWriter
provider.
- Create a web page that invokes your SSE method. The following example HTML file creates a
simple web page that displays the song that plays:
<!DOCTYPE html>
<html>
<head>
<title>SSE Radio - Now Playing</title>
</head>
<body>
<h2>Now Playing:</h2>
<div id="song">Song: </div>
<div id="artist">Artist: </div>
<script>
var source = new EventSource('rest/songDataStream');
source.onmessage = function(e) {
var song = JSON.parse(e.data);
document.getElementById("song").innerHTML = 'Song: ' + song.title;
document.getElementById("artist").innerHTML = 'Artist: ' + song.artist;
};
</script>
</body>
</html>
- Package the HTML page with your web application.
- Browse to your web application. The application receives song events from the server and
automatically updates the web page without requiring you to refresh.
If multiple
clients invoke the songDataStream
resource method or another method, each method
invocation creates a separate thread that sends data to that specific user. The sent message creates
an outbound SSE event, and the message is sent to all the clients.
- To ensure that all clients see the same data and to send a message to all clients at the
same time, use the broadcast method. Both of the previous JAX-RS client and JavaScript examples work with this broadcasting example.
Change only the URL.
- As a broadcaster, send messages to all the registered clients.
In this
example, whenever you invoke the playSong
resource method, all the subscribed
clients receive the event with the selected song data.
- As a client, register for events and stay connected.
In this example,
multiple clients can subscribe to the radio station by invoking the listen
SSE
resource method.
@Context
private Sse sse;
private static SseBroadcaster broadcaster;
private synchronized static SseBroadcaster getBroadcaster(Sse sse) {
if (broadcaster == null) {
broadcaster = sse.newBroadcaster();
}
return broadcaster;
}
@GET
@Path("listen")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void listen(@Context SseEventSink sink, @Context Sse sse) {
SseBroadcaster b = getBroadcaster(this.sse);
b.register(sink);
}
@PUT
@Path("playSong")
public Response playSong(@PathParam("songId") String songId) {
Song song = RadioService.getSong(songId);
SseBroadcaster b = getBroadcaster(this.sse);
OutboundSseEvent event = sse.newEventBuilder()
.mediaType(MediaType.APPLICATION_JSON_TYPE)
.data(Song.class, song)
.build();
b.broadcast(event);
}
Results
The web application receives the events from the server and
automatically updates the web page without refreshing.