jax-rsreactive-programming

How to stream text data with JAX-RS continuously


I read multiple questions that are similar to mine and found this: https://stackoverflow.com/a/34358215/12550134

But I am not able to do this. I use plain JAX-RS API and Open Liberty as my server. Unfortunately the ResourceConfig cannot be found, so I cannot disable the buffer, as described in the answer above.

This is my code:

@GET
@Produces(MediaType.TEXT_PLAIN)
public Response sayHelloStream() {
    LOGGER.debug("calling sayHelloStream");
    StreamingOutput out = outputStream -> {
        Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream));
        for (int i = 0; i < 999999999; i++) {
            writer.write("Hello\n");
            writer.flush();

            try {
                LOGGER.debug("before sleep");
                TimeUnit.SECONDS.sleep(3);
                LOGGER.debug("after sleep");
            } catch (InterruptedException e) {
                LOGGER.error("error with the timer", e);
            }
        }
    };
    return Response.ok(out).build();
}

When calling it in the browser nothing happens. To my understanding due to the buffer. How am I able to stream text data like this using plain JAX-RS?


Solution

  • I would use the SSE extension. AFAIK it's part of the JAX-RS API, allthough you might need an extra module to enable it server-side:

    https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/sse.html

    ...
    import javax.ws.rs.sse.Sse;
    import javax.ws.rs.sse.SseEventSink;
    import javax.ws.rs.sse.OutboundSseEvent;
    ...
     
    @Path("events")
    public static class SseResource {
     
        @GET
        @Produces(MediaType.SERVER_SENT_EVENTS)
        public void getServerSentEvents(@Context SseEventSink eventSink, @Context Sse sse) {
            new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    // ... code that waits 1 second
                    final OutboundSseEvent event = sse.newEventBuilder()
                        .name("message-to-client")
                        .data(String.class, "Hello world " + i + "!")
                        .build();
                    eventSink.send(event);
                }
            }).start();
        }
    }
    

    It streams text data to the client in chunks in the SSE format, so it can easily be handled in the browser using the EventSource JavaScript API.

    var source = new EventSource('.../events');
    source.addEventListener('message-to-client', function(e) {
      console.log(e.data);
    }, false);