Starting Server-Sent Events

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

  1. 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();
      }
  2. 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();
    }
  3. Optional: If you cannot convert the JSON interceptors back into Java objects, add a MessageBodyReader and a MessageBodyWriter provider.
  4. 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>
  5. Package the HTML page with your web application.
  6. 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.
  7. 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.
    1. 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.
    2. 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.