Reactive Jersey Client, Part 2 – Usage and Supported Libraries

Reactive Jersey Client API is a generic API allowing end users to utilize the popular reactive programming model when using Jersey Client. This part of the series describes API, usage and lists all supported reactive libraries.

Usage

Reactive Jersey Client API tries to bring a similar experience you have with the existing JAX-RS Client API. It builds on it with extending these JAX-RS APIs with a few new methods.

When you compare synchronous invocation of HTTP calls:

Response response = ClientBuilder.newClient()
        .target("http://example.com/resource")
        .request()
        .get();

with asynchronous invocation:

 response = ClientBuilder.newClient()
        .target("http://example.com/resource")
        .request()
        .async()
        .get();

it is apparent how to pretty conveniently modify the way how a request is invoked (from sync to async) only by calling async method on an Invocation.Builder.

Naturally, it’d be nice to copy the same pattern to allow invoking requests in a reactive way. Just instead of async you’d call rx on an extension of Invocation.Builder, like

 response = Rx.newClient(RxObservableInvoker.class)
        .target("http://example.com/resource")
        .request()
        .rx()
        .get();

To achieve this a few new interfaces had to be introduced in the Reactive Jersey Client API. The first new interface is RxInvoker which is very similar to SyncInvoker and AsyncInvoker. It contains all methods present in the two latter JAX-RS interfaces but the RxInvoker interface is more generic, so that it can be extended and used in particular implementations taking advantage of various reactive libraries. Extending this new interface in a particular implementation also preserves type safety which means that you’re not loosing type information when a HTTP method call returns an object that you want to process further.

As a user of the Reactive Jersey Client API you only need to keep in mind that you won’t be working with RxInvoker directly. You’d rather be working with an extension of this interface created for a particular implementation and you don’t need to be bothered much with why are things designed the way they are.

The important thing to notice here is that an extension of RxInvoker holds the type information and the Reactive Jersey Client needs to know about this type to properly propagate it among the method calls you’ll be making. This is the reason why other interfaces (described bellow) are parametrized with this type.

In addition to having a concrete RxInvoker implementation ready there is also a need to have an implementation of new reactive methods, rx() and rx(ExecutorService). They’re defined in RxInvocationBuilder which extends the Invocation.Builder from JAX-RS. Using the first method you can simply access the reactive request invocation interface to invoke the built request and the second allows you to specify the executor service to execute the current reactive request (and only this one).

To access the RxInvocationBuilder we needed to also extend JAX-RS Client (RxClient) and WebTarget (RxWebTarget) to preserve the fluent Client API introduced in JAX-RS.

With all these interfaces ready the only question left behind is the way how to create an instance of Reactive Jersey Client. This functionality is beyond the actual JAX-RS API. It is not possible to create such a client via the standard ClientBuilder entry point. To resolve this, we introduced a new helper class, Rx, which does the job. This class contains factory methods to create a new (reactive) client from scratch

and it also contains methods to enhance an existing JAX-RS Client and WebTarget

It’s possible to provide an ExecutorService instance to tell the reactive client that all requests should be invoked using this particular executor. This behaviour can be suppressed by providing another ExecutorService instance for a particular request.

Similarly to the RxInvoker interface the Rx class is general and does not stick to any conrete implementation. When Reactive Clients are created using Rx factory methods, the actual invoker type parameter has to be provided (this is not the case with similar helper classes created for particular reactive libraries).

Supported Reactive Libraries

RxJava – Observable

RxJava, contributed by Netflix, is probably the most advanced reactive library for Java at the moment. It’s used for composing asynchronous and event-based programs by using observable sequences. It uses the observer pattern to support these sequences of data/events via it’s Observable entry point class which implements the Reactive Pattern. Observable is actually the parameter type in the RxJava’s extension of RxInvoker, called RxObservableInvoker.

Requests are by default invoked at the moment when a subscriber is subscribed to an observable (it’s a cold Observable). If not said otherwise a separate thread (JAX-RS Async Client requests) is used to obtain data. This behavior can be overridden by providing an ExecutorService when a reactive Client or WebTarget is created or when a particular requests is about to be invoked.

To create a Client or WebTarget aware of reactive HTTP calls we can either use the more generic method

// New Client
RxClient<RxObservableInvoker> newRxClient = Rx.newClient(RxObservableInvoker.class);
 
// From existing Client
RxClient<RxObservableInvoker> rxClient = Rx.from(client, RxObservableInvoker.class);
 
// From existing WebTarget
RxTarget<RxObservableInvoker> rxWebTarget = Rx.from(target, RxObservableInvoker.class);

or  RxObservable helper class

// New Client
RxClient<RxObservableInvoker> newRxClient = RxObservable.newClient();
 
// From existing Client
RxClient<RxObservableInvoker> rxClient = RxObservable.from(client);
 
// From existing WebTarget
RxTarget<RxObservableInvoker> rxWebTarget = RxObservable.from(target);

In addition to specifying the invoker type and client/web-target instances, when using the factory methods in the entry points mentioned above, an ExecutorService can be specified that will be used to execute requests on separate threads. In the case of RxJava the executor service is utilized to create a Scheduler that is later leveraged in both Observable#observeOn(rx.Scheduler) and Observable#subscribeOn(rx.Scheduler).

To put it in context an example of obtaining Observable with JAX-RS Response from a remote service may look like

 observable = RxObservable.newClient()
        .target("http://example.com/resource")
        .request()
        .rx()
        .get();

More complex example, implementation of the problem described in the first part, can be found in Jersey workspace, e.g. on GitHub –  ObservableAgentResource class in rx-client-java8-webapp.

To obtain the extension module containing Reactive Jersey Client with RxJava support look at the coordinates: org.glassfish.jersey.ext.rx:jersey-rx-client-rxjava.

Java 8 – CompletionStage and CompletableFuture

Java 8 natively contains asynchronous/event-based completion aware types, CompletionStage and CompletableFuture. These types can be then combined with Streams to achieve similar functionality as provided by RxJava. CompletionStage is the parameter type in the Java 8 extension of RxInvoker, called RxCompletionStageInvoker.

Requests are by default invoked immediately. If not said otherwise the ForkJoinPool#commonPool() pool is used to obtain a thread which processed the request. This behavior can be overridden by providing an ExecutorService when a reactive Client or WebTarget is created or when a particular request is about to be invoked.

To use this module the application has to be compiled (with javac -target option set to 1.8) and run in a Java 8 environment. If you want to use Reactive Jersey Client with CompletableFuture in pre-Java 8 environment, see JSR-166e – CompletableFuture.

To create a Client or WebTarget aware of reactive HTTP calls we can use RxCompletionStage helper class.

// New Client
RxClient<RxCompletionStageInvoker> newRxClient = RxCompletionStage.newClient();
 
// From existing Client
RxClient<RxCompletionStageInvoker> rxClient = RxCompletionStage.from(client);
 
// From existing WebTarget
RxTarget<RxCompletionStageInvoker> rxWebTarget = RxCompletionStage.from(target);

In addition to specifying the invoker type and client/web-target instances, when using the factory methods in the entry points mentioned above, an ExecutorService instance could be specifies that should be used to execute requests on a separate thread.

An example of obtaining CompletionStage with JAX-RS Response from a remote service may look like

 stage = RxCompletionStage.newClient()
        .target("http://example.com/resource")
        .request()
        .rx()
        .get();

More complex example, implementation of the problem described in the first part, can be found in Jersey workspace, e.g. on GitHub –  CompletionStageAgentResource class in rx-client-java8-webapp.

To obtain the extension module containing Reactive Jersey Client with Java 8 support look at the coordinates: org.glassfish.jersey.ext.rx:jersey-rx-client-java8.

Guava – ListenableFuture and Futures {.title}

Guava, contributed by Google, also contains a type, ListenableFuture, which can be decorated with listeners that are notified when the future completes. The ListenableFuture can be combined with Futures to achieve asynchronous/event-based completion aware processing. ListenableFuture is the parameter type in the Guava’s extension of RxInvoker, called RxListenableFutureInvoker.

Requests are by default invoked immediately. If not said otherwise the Executors#newCachedThreadPool() pool is used to obtain a thread which processed the request. This behavior can be overridden by providing a ExecutorService when a reactive Client or WebTarget is created or when a particular requests is about to be invoked.

To create a Client or WebTarget aware of reactive HTTP calls we can use RxListenableFuture helper class.

// New Client
RxClient<RxListenableFutureInvoker> newRxClient = RxListenableFuture.newClient();
 
// From existing Client
RxClient<RxListenableFutureInvoker> rxClient = RxListenableFuture.from(client);
 
// From existing WebTarget
RxTarget<RxListenableFutureInvoker> rxWebTarget = RxListenableFuture.from(target);

In addition to specifying the invoker type and client/web-target instances, when using the factory methods in the entry points mentioned above, an ExecutorService can be specified that will be used to execute requests on a separate thread.

An example of obtaining ListenableFuture with JAX-RS Response from a remote service may look like

 stage = RxListenableFuture.newClient()
        .target("http://example.com/resource")
        .request()
        .rx()
        .get();

More complex example, implementation of the problem described in the first part, can be found in Jersey workspace, e.g. on GitHub –  ListenableFutureAgentResource class in rx-client-java8-webapp.

To obtain the extension module containing Reactive Jersey Client with Guava support look at the coordinates: org.glassfish.jersey.ext.rx:jersey-rx-client-guava.

JSR-166e – CompletableFuture

When Java 8 is not an option but the functionality of CompletionStage and CompletableFuture is required a JSR 166 library can be used. It’s a back-port of classes fromjava.util.concurrent package added to Java 8. Contributed and maintained by Doug Lea. CompletableFuture is the parameter type in the JSR-166e’s extension ofRxInvoker, called RxCompletableFutureInvoker.

Requests are by default invoked immediately. If not said otherwise the ForkJoinPool.html#commonPool() pool is used to obtain a thread which processed the request. This behavior can be overridden by providing an ExecutorService when a reactive Client or WebTarget is created or when a particular requests is about to be invoked.

If you’re compiling and running your application in Java 8 environment consider use Reactive Jersey Client with Java 8 – CompletionStage and CompletableFuture support instead.

To create a Client or WebTarget aware of reactive HTTP calls we can use RxCompletableFuture helper class.

// New Client
RxClient<RxCompletableFutureInvoker> newRxClient = RxCompletableFuture.newClient();
 
// From existing Client
RxClient<RxCompletableFutureInvoker> rxClient = RxCompletableFuture.from(client);
 
// From existing WebTarget
RxTarget<RxCompletableFutureInvoker> rxWebTarget = RxCompletableFuture.from(target);

In addition to specifying the invoker type and client/web-target instances, when using the factory methods in the entry points mentioned above, an ExecutorService can be specified that is further used to execute requests on a separate thread.

To put it in context an example of obtaining CompletableFuture with JAX-RS Response from a remote service may look like

 stage = RxCompletableFuture.newClient()
        .target("http://example.com/resource")
        .request()
        .rx()
        .get();

To obtain the extension module containing Reactive Jersey Client with Java 8 support look at the coordinates: org.glassfish.jersey.ext.rx:jersey-rx-client-jsr166e.

Further reading

Resources