• Categories
  • Search
Monday, March 6, 2017

JAX-RS 2.1 Server Sent Events

If you find this post better-worded (and grammatically correct) compared to the usual standard on this blog, its because the text below is part of the proposed JAX-RS 2.1 specification, which was authored by Santiago :)

If you want to provide feedback to this chapter, please send us a note to users@jax-rs-spec.java.net.

Server Sent Events

Server-sent events (SSE) is a specification originally introduced as part of HTML5 by the W3C, but is currently maintained by the WHATWG. It provides a way to establish a one-way channel from a server to a client. The connection is long running: it is re-used for multiple events sent from the server, yet it is still based on the HTTP protocol. Clients request the opening of an SSE connection by using the special media type text/event-stream in the Accept header.

Events are structured and contain several fields, namely, event, data, id, retry and comment. SSE is a messaging protocol where the event field corresponds to a topic, and where the id field can be used to validate event order and guarantee continuity. If a connection is interrupted for any reason, the id can be sent in a request header for a server to re-play past events —although this is an optional behavior that may not be supported by all implementations. Event payloads are conveyed in the data field and must be in text format; retry is used to control re-connects and finally comment is a general purpose field that can also be used to keep connections alive.

Client API

The JAX-RS client API for SSE was inspired by the corresponding JavaScript API in HTML5, but with changes that originate from the use of a different language as well as the desire to support the Flow API from Java 9 – but without introducing a dependency with that version of Java.

The entry point to the client API is the type SseEventSource, which provides a fluent builder similarly to other classes in the JAX-RS API. An SseEventSource is constructed from a WebTarget that is already configured with a resource location; SseEventSource does not duplicate any functionality in WebTarget and only adds the necessary logic for SSE.

The following example shows how to open an SSE connection and read some messages for a little while:

1
2
3
4
5
6
7
8
9
try (final SseEventSource eventSource =
SseEventSource.target(target)
.build()) {
eventSource.subscribe(System.out::println);
eventSource.open();
Thread.sleep(500); // consume events for 500 ms.
} catch (InterruptedException e) {
// ...
}

As seen in this example, an SseEventSource implements AutoCloseable. Before opening the source, the client subscribes to the event stream and registers an event consumer that simply prints each event. Additional handlers for other life-cycle events such as onSubscribe, onComplete and onError are also supported, but for simplicity only onEvent is shown in the example above.

Server API

The JAX-RS SSE server API is used to accept connections and send events to one or more clients (also known as subscribers). A resource method that injects an SseEventSink and produces the media type text/event-stream is an SSE resource method.

The following example accepts SSE connections and uses an executor thread to send three events before closing the connection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@GET
@Path("eventStream")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void eventStream(@Context SseEventSink eventSink,
@Context Sse sse) {

   executor.execute(() -> {
try(SseEventSink sink = eventSink) {
eventSink.onNext(sse.newEvent("event1"));
eventSink.onNext(sse.newEvent("event2"));
eventSink.onNext(sse.newEvent("event3"));
eventSink.close();
} catch (IOException e) {
// handle exception
}
});
}

SSE resource methods follow a similar pattern to those for asynchronous processing in that the object representing the incoming connection, in this case SseEventSink, is injected into the resource method.

The example above also injects the Sse type which provides factory methods for events and broadcasters. Note that, just like SseEventSource, the interface SseEventSink is also auto-closeable, hence the use of the try-with-resources statement above.

Broadcasting

Applications may need to send events to multiple clients simultaneously. This action is called broadcasting in JAX-RS. Multiple SseEventSink’s can be registered (or subscribed) on a single SseBroadcaster. Thus, all SseBroadcaster’s are also publishers in the sense defined by the Java 9 Flow API, supporting the same methods as Flow.Publisher<T> but not directly extending that interface to avoid dependencies on Java 9. More precisely, an SseBroadcaster implements Flow.Publisher<OutboundSseEvent>.

A broadcaster can only be created by calling method newBroadcaster on the injected Sse instance. The life-cycle and scope of an SseBroadcaster is fully controlled by applications and not the JAX-RS runtime. The following example shows the use of broadcasters, note the @Singleton annotation on the resource class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Path("/")
@Singleton
public class SseResource {
private final Sse sse;
private final SseBroadcaster sseBroadcaster;
public SseResource(@Context Sse sse) {
this.sse = sse;
this.sseBroadcaster = sse.newBroadcaster();
}
@GET
@Path("subscribe")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void subscribe(@Context SseEventSink eventSink) {
eventSink.onNext(sse.newEvent("welcome!"));
sseBroadcaster.subscribe(eventSink);
}
@POST
@Path("broadcast")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public void broadcast(@FormParam("event") String event) {
sseBroadcaster.broadcast(sse.newEvent(event));
}
}

Environment

The SseEventSource class uses the existing JAX-RS mechanism based on RuntimeDelegate to find an implementation using the service name javax.ws.rs.sse.SseEventSource.Builder. The majority of types in the javax.ws.rs.sse are thread safe; the reader is referred to the Javadoc for more information on thread safety.

Join the discussion

Comments ( 2 )
  • guest Wednesday, March 8, 2017

    Hi,

    You request comments on the mailing list, but my mail was rejected from there. I can't seem to find where to sign up?

    Groeten,

    Friso


  • Pavel Wednesday, March 8, 2017

    Hi Friso,

    sorry for the inconvenience. To be able to send messages to that list, you need to subscribe first - that should be available here: https://java.net/projects/jax-rs-spec/lists. you might need to have java.net account, I'm not sure whether subscriptions with only email address are allowed.

    Regards,

    Pavel


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha
 

Visit the Oracle Blog

 

Contact Us

Oracle

Integrated Cloud Applications & Platform Services