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.
- Reactive Jersey Client – Motivation
- Reactive Jersey Client – Usage and Supported Reactive Libraries (this article)
- Reactive Jersey Client – Customization (SPI)
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
- Rx.from(Client,Class)
- Rx.from(Client,Class,ExecutorService)
- Rx.from(WebTarget,Class)
- Rx.from(WebTarget,Class,ExecutorService)
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 to1.8
) and run in a Java 8 environment. If you want to use Reactive Jersey Client withCompletableFuture
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.