Chapter 18. Server-Sent Event

Table of Contents

18.1. Overview
18.2. Server-Sent Events (SSE) Support in the JAX-RS Resource
18.2.1. Simple SSE Resource Method
18.3. SSE Event Processing in the JAX-RS Client
18.3.1. SSE Event Reading by using EventInput
18.3.2. Asynchronous SSE Processing by using EventSource

This chapter describes how to use HTML5's Server-Sent Event (SSE) in JEUS.

18.1. Overview

Server-Sent Events enables servers to push data to webpages by using the standard HTTP or HTTPS via one-way client server connection.

In the Server-Sent Events communication model, the client (e.g. browser) maintains the initial connection, and the server provides data and sends it to the client.

The Server-Sent Events technology is part of the HTML5 specification, and is supported in JEUS through JAX-RS (Java API for RESTful Web Services) 2.0.

18.2. Server-Sent Events (SSE) Support in the JAX-RS Resource

18.2.1. Simple SSE Resource Method

Add SSeFeature in the resource for SSE support.

[Example 18.1] Simple SSE Resource Method

...
import jeus.webservices.jaxrs.media.sse.EventOutput;
import jeus.webservices.jaxrs.media.sse.OutboundEvent;
import jeus.webservices.jaxrs.media.sse.SseFeature;
...
 
@Path("events")
public class SseResource {
 
    @GET
    @Produces(SseFeature.SERVER_SENT_EVENTS)
    public EventOutput getServerSentEvents() {
        EventOutput eventOutput = new EventOutput();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 5; i++) {
                        // ... code that waits a few seconds
                        OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder();
                        eventBuilder.name("example");
                        eventBuilder.data(String.class, "Hello world " + i + "!");
                        OutboundEvent event = eventBuilder.build();
                        eventOutput.write(event);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                } finally {
                    try {
                        eventOutput.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }).start();
        return eventOutput;
    }
}

The above source code defines the resource deployed to URI "/events". The resource contains a @GET resource method which returns the EventOutput entity for processing ChunkedOutput messages.

After EventOutput is returned from the method, by recognizing EventOutput as ChunkedOutput, the JAX-RS runtime does not immediately disconnect from the client. It writes an HTTP header in the response script and waits for the sent chunks (SSE events). Then, the client reads the header and starts listening to individual events.

In the example, a resource method creates a thread for sending a series of five events. There is a small delay between the series of events. Each event is expressed as an OutboundEvent type. An OutboundEvent is applied in the standard SSE message format, and includes a property that expresses a name, comment, and id. The dat (Class) method is used to serialize event data.

A client connected to an SSE supported resource will receive the following data from the entity stream.

event: example
data: Hello world 0!

event: example
data: Hello world 1!

event: example
data: Hello world 2!

event: example
data: Hello world 3!

event: example
data: Hello world 4!

18.3. SSE Event Processing in the JAX-RS Client

JEUS JAX-RS uses two types of program models to provide a client API for receiving and handling SSE events.

  • Pull model: Pools events from EventInput

  • Push model: Listens EventSource's asynchronous notifications

18.3.1. SSE Event Reading by using EventInput

A client can reading an event from EventInput. Let's take a look at the following code.

[Example 18.2] SSE Event Reading by using EventInput

Client client = ClientBuilder.newBuilder().build();
WebTarget target = client.target("http://localhost:8088/sse_example/events");
EventInput eventInput = target.request().get(EventInput.class);
while (!eventInput.isClosed()) {
    final InboundEvent inboundEvent = eventInput.read();
    if (inboundEvent == null) {
        // connection has been closed
        break;
    }
    System.out.println(inboundEvent.getName() + ": " + inboundEvent.readData(String.class));
}

A client connects to the server where the SseResource in the "Simple SSE Resource Method" example is deployed. First create the JAX_RS client object. Then, get the WebTarget object from the client object, and use it for calling an HTTP request. The returned response entity can be read via EventInput. In the example source code, a loop is started in order to read the inbound SSE event from the eventInput response stream.

An individual chunk read from eventInput is an InboundEvent. The InboundEvent.readData(Class) is a method used for de-serializing event data.

example: Hello world 0!
example: Hello world 1!
example: Hello world 2!
example: Hello world 3!
example: Hello world 4!

18.3.2. Asynchronous SSE Processing by using EventSource

EventSource is a client API used to read SSE events asynchronously. The following example describes how to use EventSource.

[Example 18.3] Reading an SSE Event by using EventSource

Client client = ClientBuilder.newBuilder().build();
WebTarget target = client.target("http://localhost:8088/sse_example/events");
EventSource eventSource = new EventSource(target);
EventListener listener = new EventListener() {
        @Override
        public void onEvent(InboundEvent inboundEvent) {
            System.out.println(inboundEvent.getName() + ": " 
+ inboundEvent.readData(String.class));
        }
    };
eventSource.addEventListener("example", listener);
eventSource.open();
...
eventSource.close();

The client connects to the server where the SseResource in the "Simple SSE Resource Method" example is deployed. First, create the JAX-RS client object. Then, get the WebTarget object from the client object.

HTTP request calls for a web target is not created in the WebTarget object. Create an EventSource object by initializing it as a target object.

A created EventSource object does not automatically connect to the target. To connect, manually connect by using the eventSource.open() method.

The EventListener implementation is used for listening and handling the incoming SSE event. The InboundEvent.readData(Class) method is used for de-serializing event data from an inboundEvent.

The listener shows the following result in an example.

example: Hello world 0!
example: Hello world 1!
example: Hello world 2!
example: Hello world 3!
example: Hello world 4!