-
Notifications
You must be signed in to change notification settings - Fork 355
/
rx-client.xml
839 lines (713 loc) · 44.4 KB
/
rx-client.xml
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
<?xml version="1.0"?>
<!--
Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0, which is available at
http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary
Licenses when the conditions for such availability set forth in the
Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
version 2 with the GNU Classpath Exception, which is available at
https://www.gnu.org/software/classpath/license.html.
SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-->
<!DOCTYPE chapter [
<!ENTITY % ents SYSTEM "jersey.ent" > %ents;
<!ENTITY jersey.github.rx.example.link "<link xlink:href='&jersey.github.examples.uri;/rx-client-webapp'>Travel Agency (Orchestration Layer) Example using Reactive Jersey Client API</link>">
<!ENTITY jersey.github.rx.java8.example.link "<link xlink:href='&jersey.github.examples.uri;/rx-client-java8-webapp'>Travel Agency (Orchestration Layer) Example using Reactive Jersey Client API (Java 8)</link>">
]>
<chapter xmlns="http://docbook.org/ns/docbook"
version="5.0"
xml:lang="en"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xlink="http://www.w3.org/1999/xlink"
xsi:schemaLocation="http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd
http://www.w3.org/1999/xlink http://www.w3.org/1999/xlink.xsd"
xml:id="rx-client">
<title>Reactive JAX-RS Client API</title>
<warning>
<para>
Jersey 2.26 (JAX-RS 2.1 implementation) dropped Jersey-proprietary API in favor of JAX-RS 2.1 Reactive Client API.
</para>
</warning>
<para>
Reactive client extension is quite a generic API allowing end users to utilize the popular reactive programming model
when using JAX-RS Client. The API is designed to be extensible, so any existing reactive framework can integrate with
it and there is build in support for CompletionStage. Along with describing the API itself, this section also covers
existing extension modules and provides hints to implement a custom extension if needed.
</para>
<para>
If you are not familiar with the JAX-RS Client API, it is recommended that you see <xref linkend="client"/>
where the basics of JAX-RS Client API along with some advanced techniques are described.
</para>
<section>
<title>Motivation for Reactive Client Extension</title>
<simplesect>
<title>The Problem</title>
<para>
Imagine a travel agency whose information system consists of multiple basic services. These services might be built
using different technologies (JMS, EJB, WS, ...). For simplicity we presume that the services can be
consumed using REST interface via HTTP method calls (e.g. using a JAX-RS Client). We also presume that the basic
services we need to work with are:
<itemizedlist>
<listitem>
<para>
<emphasis>Customers service</emphasis> – provides information about customers of the travel agency.
</para>
</listitem>
<listitem>
<para>
<emphasis>Destinations service</emphasis> – provides a list of visited and recommended destinations
for an authenticated customer.
</para>
</listitem>
<listitem>
<para>
<emphasis>Weather service</emphasis> – provides weather forecast for a given destination.
</para>
</listitem>
<listitem>
<para>
<emphasis>Quoting service</emphasis> – provides price calculation for a customer to travel to
a recommended destination.
</para>
</listitem>
</itemizedlist>
</para>
<para>
The task is to create a publicly available feature that would, for an authenticated user, display a list of 10 last visited places and
also display a list of 10 new recommended destinations including weather forecast and price calculations for the
user. Notice that some of the requests (to retrieve data) depend on results of previous requests. E.g. getting
recommended destinations depends on obtaining information about the authenticated user first. Obtaining weather
forecast depends on destination information, etc. This relationship between some of the requests is an important part of the
problem and an area where you can take a real advantage of the reactive programming model.
</para>
<para>
One way how to obtain data is to make multiple HTTP method calls from the client (e.g. mobile device) to all
services involved and combine the retrieved data on the client. However, since the basic services are available
in the internal network only we'd rather create a public orchestration layer instead of exposing all internal services to the outside world.
The orchestration layer would expose only the desired operations of the basic services
to the public. To limit traffic and achieve lower latency we'd like to return all the necessary
information to the client in a single response.
</para>
<para>
The orchestration layer is illustrated in the <xref linkend="rx.client.motivation.problem" xrefstyle="select: label" />.
The layer accepts requests from the outside and is responsible of invoking multiple requests to the internal services.
When responses from the internal services are available in the orchestration layer they're combined into
a single response that is sent back to the client.
<figure xml:id="rx.client.motivation.problem">
<title>Travel Agency Orchestration Service</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/rx-client-problem.png" format="PNG" width="80%" scalefit="1" align="center"/>
</imageobject>
</mediaobject>
</figure>
The next sections describe various approaches (using JAX-RS Client) how the orchestration layer can be implemented.
</para>
</simplesect>
<simplesect>
<title>A Naive Approach</title>
<para>
The simplest way to implement the orchestration layer is to use synchronous approach. For this purpose we can use
JAX-RS Client Sync API (see <xref linkend="rx.client.motivation.naive" />). The implementation is simple to do,
easy to read and straightforward to debug.
<example xml:id="rx.client.motivation.naive">
<title>Excerpt from a synchronous approach while implementing the orchestration layer</title>
<programlisting language="java" linenumbering="numbered">final WebTarget destination = ...;
final WebTarget forecast = ...;
// Obtain recommended destinations.
List<Destination> recommended = Collections.emptyList();
try {
recommended = destination.path("recommended").request()
// Identify the user.
.header("Rx-User", "Sync")
// Return a list of destinations.
.get(new GenericType<List<Destination>>() {});
} catch (final Throwable throwable) {
errors.offer("Recommended: " + throwable.getMessage());
}
// Forecasts. (depend on recommended destinations)
final Map<String, Forecast> forecasts = new HashMap<>();
for (final Destination dest : recommended) {
try {
forecasts.put(dest.getDestination(),
forecast.resolveTemplate("destination", dest.getDestination()).request().get(Forecast.class));
} catch (final Throwable throwable) {
errors.offer("Forecast: " + throwable.getMessage());
}
}</programlisting>
</example>
The downside of this approach is its slowness. You need to sequentially process all the independent requests which
means that you're wasting resources. You are needlessly blocking threads, that could be otherwise used for some real work.
</para>
<para>
If you take a closer look at the example you can notice that at the moment when all the recommended destinations are
available for further processing we try to obtain forecasts for these destinations. Obtaining a weather forecast
can be done only for a single destination with a single request, so we need to make 10 requests to
the <emphasis>Forecast service</emphasis> to get all the destinations covered. In a synchronous way this means getting the forecasts
one-by-one. When one response with a forecast arrives we can send another request to obtain another one. This takes
time. The whole process of constructing a response for the client can be seen in
<xref linkend="rx.client.motivation.graph.sync" xrefstyle="select: label" />.
</para>
<para>
Let's try to quantify this with assigning an approximate time to every request we make to the internal services.
This way we can easily compute the time needed to complete a response for the client. For example, obtaining
<itemizedlist>
<listitem>
<para><emphasis>Customer details</emphasis> takes 150 ms</para>
</listitem>
<listitem>
<para><emphasis>Recommended destinations</emphasis> takes 250 ms</para>
</listitem>
<listitem>
<para><emphasis>Price calculation for a customer and destination</emphasis> takes 170 ms (each)</para>
</listitem>
<listitem>
<para><emphasis>Weather forecast for a destination</emphasis> takes 330 ms (each)</para>
</listitem>
</itemizedlist>
When summed up, 5400 ms is approximately needed to construct a response for the client.
<figure xml:id="rx.client.motivation.graph.sync">
<title>Time consumed to create a response for the client – synchronous way</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/rx-client-sync-approach.png" format="PNG" width="80%" scalefit="1" align="center"/>
</imageobject>
</mediaobject>
</figure>
Synchronous approach is better to use for lower number of requests (where the accumulated time doesn't matter that
much) or for a single request that depends on the result of previous operations.
</para>
</simplesect>
<simplesect>
<title>Optimized Approach</title>
<para>
The amount of time needed by the synchronous approach can be lowered by invoking independent requests in parallel.
We're going to use JAX-RS Client Async API to illustrate this approach. The implementation in this case is slightly
more difficult to get right because of the nested callbacks and the need to wait at some points for the moment
when all partial responses are ready to be processed. The implementation is also a little bit harder to debug and maintain.
The nested calls are causing a lot of complexity here. An example of concrete Java code following the asynchronous approach
can be seen in <xref linkend="rx.client.motivation.optimized" />.
<example xml:id="rx.client.motivation.optimized">
<title>Excerpt from an asynchronous approach while implementing the orchestration layer</title>
<programlisting language="java" linenumbering="numbered">final WebTarget destination = ...;
final WebTarget forecast = ...;
// Obtain recommended destinations. (does not depend on visited ones)
destination.path("recommended").request()
// Identify the user.
.header("Rx-User", "Async")
// Async invoker.
.async()
// Return a list of destinations.
.get(new InvocationCallback<List<Destination>>() {
@Override
public void completed(final List<Destination> recommended) {
final CountDownLatch innerLatch = new CountDownLatch(recommended.size());
// Forecasts. (depend on recommended destinations)
final Map<String, Forecast> forecasts = Collections.synchronizedMap(new HashMap<>());
for (final Destination dest : recommended) {
forecast.resolveTemplate("destination", dest.getDestination()).request()
.async()
.get(new InvocationCallback<Forecast>() {
@Override
public void completed(final Forecast forecast) {
forecasts.put(dest.getDestination(), forecast);
innerLatch.countDown();
}
@Override
public void failed(final Throwable throwable) {
errors.offer("Forecast: " + throwable.getMessage());
innerLatch.countDown();
}
});
}
// Have to wait here for dependent requests ...
try {
if (!innerLatch.await(10, TimeUnit.SECONDS)) {
errors.offer("Inner: Waiting for requests to complete has timed out.");
}
} catch (final InterruptedException e) {
errors.offer("Inner: Waiting for requests to complete has been interrupted.");
}
// Continue with processing.
}
@Override
public void failed(final Throwable throwable) {
errors.offer("Recommended: " + throwable.getMessage());
}
});</programlisting>
</example>
</para>
<para>
The example is a bit more complicated from the first glance. We provided an &jaxrs.client.InvocationCallback; to async
<literal>get</literal> method. One of the callback methods (<literal>completed</literal> or <literal>failed</literal>)
is called when the request finishes. This is a pretty convenient way to handle async invocations when no nested
calls are present. Since we have some nested calls (obtaining weather forecasts) we needed to introduce
a &jdk6.CountDownLatch; synchronization primitive as we use asynchronous approach in obtaining the weather
forecasts as well. The latch is decreased every time a request, to the <emphasis>Forecasts service</emphasis>,
completes successfully or fails. This indicates that the request actually finished and it is a signal for us that
we can continue with processing (otherwise we wouldn't have all required data to construct the response for the
client). This additional synchronization is something that was not present when taking the synchronous approach,
but it is needed here.
</para>
<para>
Also the error processing can not be written as it could be in an ideal case. The error handling is scattered in
too many places within the code, that it is quite difficult to create a comprehensive response for the client.
</para>
<para>
On the other hand taking asynchronous approach leads to code that is as fast as it gets.
The resources are used optimally (no waiting threads) to achieve
quick response time. The whole process of constructing the response for the client can be seen in
<xref linkend="rx.client.motivation.graph.async" xrefstyle="select: label" />. It only took 730 ms instead of
5400 ms which we encountered in the previous approach.
<figure xml:id="rx.client.motivation.graph.async">
<title>Time consumed to create a response for the client – asynchronous way</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/rx-client-async-approach.png" format="PNG" width="80%" scalefit="1" align="center"/>
</imageobject>
</mediaobject>
</figure>
As you can guess, this approach, even with all it's benefits, is the one that is really hard to implement, debug
and maintain. It's a safe bet when you have many independent calls to make but it gets uglier with an increasing
number of nested calls.
</para>
</simplesect>
<simplesect>
<title>Reactive Approach</title>
<para>
Reactive approach is a way out of the so-called <emphasis>Callback Hell</emphasis> which you can encounter when
dealing with Java's <literal>Future</literal>s or invocation callbacks. Reactive approach is based on a data-flow
concept and the execution model propagate changes through the flow. An example of a single item in the data-flow
chain can be a JAX-RS Client HTTP method call. When the JAX-RS request finishes then the next item (or the user code)
in the data-flow chain is notified about the continuation, completion or error in the chain. You're more describing
what should be done next than how the next action in the chain should be triggered. The other important part here
is that the data-flows are composable. You can compose/transform multiple flows into the resulting one and apply
more operations on the result.
</para>
<para>
An example of this approach can be seen in <xref linkend="rx.client.motivation.reactive" />. The APIs would be
described in more detail in the next sections.
<example xml:id="rx.client.motivation.reactive">
<title>Excerpt from a reactive approach while implementing the orchestration layer</title>
<programlisting language="java" linenumbering="numbered">final WebTarget destination = ...;
final WebTarget forecast = ...;
// Recommended places.
CompletionStage<List<Destination>> recommended =
destination.path("recommended")
.request()
// Identify the user.
.header("Rx-User", "CompletionStage")
// Reactive invoker.
.rx()
// Return a list of destinations.
.get(new GenericType<List<Destination>>() {})
.exceptionally(throwable -> {
errors.offer("Recommended: " + throwable.getMessage());
return Collections.emptyList();
});
// get Forecast for recommended destinations.
return recommended.thenCompose(destinations -> {
List<CompletionStage<Recommendation>> recommendations = destinations.stream().map(destination -> {
// For each destination, obtain a weather forecast ...
final CompletionStage<Forecast> forecastResult =
forecast.resolveTemplate("destination", destination.getDestination())
.request().rx().get(Forecast.class)
.exceptionally(throwable -> {
errors.offer("Forecast: " + throwable.getMessage());
return new Forecast(destination.getDestination(), "N/A");
});
//noinspection unchecked
return CompletableFuture.completedFuture(new Recommendation(destination))
// Set forecast for recommended destination.
.thenCombine(forecastResult, Recommendation::forecast)
}).collect(Collectors.toList());
// Transform List<CompletionStage<Recommendation>> to CompletionStage<List<Recommendation>>
return sequence(recommendations);
});</programlisting>
</example>
</para>
<para>
As you can see the code achieves the same work as the previous two examples. It's more readable than the pure
asynchronous approach even though it's equally fast. It's as easy to read and implement as the synchronous approach.
The error processing is also better handled in this way than in the asynchronous approach.
</para>
<para>
When dealing with a large amount of requests (that depend on each other) and when you need to compose/combine the
results of these requests, the reactive programming model is the right technique to use.
</para>
</simplesect>
</section>
<section>
<title>Usage and Extension Modules</title>
<para>
Reactive Client API is part of the JAX-RS specification since version 2.1.
</para>
<para>
When you compare synchronous invocation of HTTP calls (
<xref linkend="rx.client.sync" />)
<example xml:id="rx.client.sync">
<title>Synchronous invocation of HTTP requests</title>
<programlisting language="java" linenumbering="numbered">Response response = ClientBuilder.newClient()
.target("http://example.com/resource")
.request()
.get();</programlisting>
</example>
with asynchronous invocation (<xref linkend="rx.client.async" />)
<example xml:id="rx.client.async">
<title>Asynchronous invocation of HTTP requests</title>
<programlisting language="java" linenumbering="numbered">Future<Response> response = ClientBuilder.newClient()
.target("http://example.com/resource")
.request()
.async()
.get();</programlisting>
</example>
it is apparent how to pretty conveniently modify the way how a request is invoked (from sync to async) only by calling
<literal>async</literal> method on an &jaxrs.client.Invocation.Builder;.
</para>
<para>
Naturally, it'd be nice to copy the same pattern to allow invoking requests in a reactive way. Just instead of
<literal>async</literal> you'd call <literal>rx</literal> on an extension of &lit.jaxrs.client.Invocation.Builder;,
like in <xref linkend="rx.client.reactive" />.
<example xml:id="rx.client.reactive">
<title>Reactive invocation of HTTP requests</title>
<programlisting language="java" linenumbering="numbered">CompletionStage<Response> response = ClientBuilder.newClient()
.target("http://example.com/resource")
.request()
.rx()
.get();</programlisting>
</example>
</para>
<para>
The first reactive interface in the invocation chain is &jaxrs.client.RxInvoker; which is very similar to
&jaxrs.client.SyncInvoker; and &jaxrs.client.AsyncInvoker;. It contains all methods present in the two latter
JAX-RS interfaces but the &lit.jaxrs.client.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.
</para>
<para>
The method "rx()" in the example above is perfect example of that principle. It returns &jaxrs.client.CompletionStageRxInvoker;,
which extends &jaxrs.client.RxInvoker;.
</para>
<para>
As a user of the Reactive Client API you only need to keep in mind that you won't be working with
&lit.jaxrs.client.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.
<note>
<para>
To see how the &lit.jaxrs.client.RxInvoker; should be extended, refer to
<xref linkend="rx.client.spi" />.
</para>
</note>
The important thing to notice here is that an extension of &lit.jaxrs.client.RxInvoker; holds the type
information and the Reactive 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.
</para>
<para>
In order to extend the API to be used with other reactive frameworks, &jaxrs.client.RxInvokerProvider; needs to be
registered into the Client runtime:
</para>
<programlisting language="java" linenumbering="numbered">Client client = ClientBuilder.newClient();
client.register(RxFlowableInvokerProvider.class);
Flowable<String> responseFlowable =
client.target("http://jersey.java.net")
.request()
.rx(RxFlowableInvoker.class)
.get(String.class);
String responseString = responseFlowable.blockingFirst();</programlisting>
<simplesect>
<title>Dependencies</title>
<para>
JAX-RS mandates support for CompletionStage, which doesn't required any other dependency and can be
used out of the box.
</para>
<para>
To add support for a particular library, see the <xref linkend="rx.client.supported" />.
</para>
<note>
<para>
If you're not using Maven (or other dependency management tool) make sure to add also all the transitive
dependencies of Jersey client module and any other extensions (when used) on the class-path.
</para>
</note>
</simplesect>
</section>
<section xml:id="rx.client.supported">
<title>Supported Reactive Libraries</title>
<para>
There are already some available reactive (or reactive-like) libraries out there and Jersey brings support for some of
them out of the box. Jersey currently supports:
<itemizedlist>
<listitem>
<para><xref linkend="rx-client.rxjava" endterm="rx-client.rxjava.title" /></para>
</listitem>
<listitem>
<para><xref linkend="rx-client.rxjava2" endterm="rx-client.rxjava2.title" /></para>
</listitem>
<listitem>
<para><xref linkend="rx-client.guava" endterm="rx-client.guava.title" /></para>
</listitem>
</itemizedlist>
</para>
<section xml:id="rx-client.rxjava">
<title>RxJava – Observable</title>
<titleabbrev xml:id="rx-client.rxjava.title">RxJava (Observable)</titleabbrev>
<para>
&rxjava.link;, 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
<link xlink:href="&wikipedia.uri;Observer_pattern">observer pattern</link> to support these sequences of data/events
via its &rxjava.Observable; entry point class which implements the Reactive Pattern. &lit.rxjava.Observable; is
actually the parameter type in the RxJava's extension of &jaxrs.client.RxInvoker;, called
&jersey.ext.rx.client.rxjava.RxObservableInvoker;. This means that the return type of HTTP method calls is
&lit.rxjava.Observable; in this case (accordingly parametrized).
</para>
<para>
Requests are by default invoked at the moment when a subscriber is subscribed to an observable (it's a cold
&lit.rxjava.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 &jdk6.ExecutorService; when a reactive
&lit.jaxrs.client.Client; is created.
</para>
<simplesect>
<title>Usage</title>
<para>
The extensibility is built-in JAX-RS Client API, so there are no special dependencies on Jersey Client
API other than the extension itself.
<example xml:id="rx.client.rxjava.rx">
<title>Creating JAX-RS Client with RxJava reactive extension</title>
<programlisting language="java" linenumbering="numbered">// New Client
Client client = ClientBuilder.newClient();
client.register(RxObservableInvokerProvider.class);</programlisting>
</example>
</para>
<para>
An example of obtaining &lit.rxjava.Observable; with JAX-RS &lit.jaxrs.core.Response; from a remote service
can be seen in <xref linkend="rx.client.rxjava.usage" />.
<example xml:id="rx.client.rxjava.usage">
<title>Obtaining Observable<Response> from Jersey/RxJava Client</title>
<programlisting language="java" linenumbering="numbered">Observable<Response> observable = RxObservable.newClient()
.target("http://example.com/resource")
.request()
.rx(RxObservableInvoker.class)
.get();</programlisting>
</example>
</para>
</simplesect>
<simplesect>
<title>Dependencies</title>
<para>
The RxJava support is available as an extension module in Jersey. For Maven users,
simply add the following dependency to your &lit.pom.xml;:
<programlisting language="xml" linenumbering="unnumbered"><dependency>
<groupId>org.glassfish.jersey.ext.rx</groupId>
<artifactId>jersey-rx-client-rxjava</artifactId>
<version>&version;</version>
</dependency></programlisting>
After this step you can use the extended client right away. The dependency transitively adds the following
dependencies to your class-path as well: <literal>io.reactivex:rxjava</literal>.
</para>
<note>
<para>
If you're not using Maven (or other dependency management tool) make sure to add also all the transitive
dependencies of this extension module (see &jersey.ext.rx-client.rxjava.deps.link;) on the class-path.
</para>
</note>
</simplesect>
</section>
<section xml:id="rx-client.rxjava2">
<title>RxJava – Flowable</title>
<titleabbrev xml:id="rx-client.rxjava2.title">RxJava (Flowable)</titleabbrev>
<para>
&rxjava.link;, 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
<link xlink:href="&wikipedia.uri;Observer_pattern">observer pattern</link> to support these sequences of data/events
via its &rxjava2.Flowable; entry point class which implements the Reactive Pattern. &lit.rxjava2.Flowable; is
actually the parameter type in the RxJava's extension of &jaxrs.client.RxInvoker;, called
&jersey.ext.rx.client.rxjava2.RxFlowableInvoker;. This means that the return type of HTTP method calls is
&lit.rxjava2.Flowable; in this case (accordingly parametrized).
</para>
<para>
Requests are by default invoked at the moment when a subscriber is subscribed to a flowable (it's a cold
&lit.rxjava2.Flowable;). 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 &jdk6.ExecutorService; when a reactive
&lit.jaxrs.client.Client; is created.
</para>
<simplesect>
<title>Usage</title>
<para>
The extensibility is built-in JAX-RS Client API, so there are no special dependencies on Jersey Client
API other than the extension itself.
<example xml:id="rx.client.rxjava2.rx">
<title>Creating JAX-RS Client with RxJava2 reactive extension</title>
<programlisting language="java" linenumbering="numbered">// New Client
Client client = ClientBuilder.newClient();
client.register(RxFlowableInvokerProvider.class);</programlisting>
</example>
</para>
<para>
An example of obtaining &lit.rxjava2.Flowable; with JAX-RS &lit.jaxrs.core.Response; from a remote service
can be seen in <xref linkend="rx.client.rxjava.usage" />.
<example xml:id="rx.client.rxjava2.usage">
<title>Obtaining Flowable<Response> from Jersey/RxJava Client</title>
<programlisting language="java" linenumbering="numbered">Flowable<Response> observable = RxObservable.newClient()
.target("http://example.com/resource")
.request()
.rx(RxFlowableInvoker.class)
.get();
</programlisting>
</example>
</para>
</simplesect>
<simplesect>
<title>Dependencies</title>
<para>
The RxJava support is available as an extension module in Jersey. For Maven users,
simply add the following dependency to your &lit.pom.xml;:
<programlisting language="xml" linenumbering="unnumbered"><dependency>
<groupId>org.glassfish.jersey.ext.rx</groupId>
<artifactId>jersey-rx-client-rxjava2</artifactId>
<version>&version;</version>
</dependency></programlisting>
After this step you can use the extended client right away. The dependency transitively adds the following
dependencies to your class-path as well: <literal>io.reactivex:rxjava2</literal>.
</para>
<note>
<para>
If you're not using Maven (or other dependency management tool) make sure to add also all the transitive
dependencies of this extension module (see &jersey.ext.rx-client.rxjava2.deps.link;) on the class-path.
</para>
</note>
</simplesect>
</section>
<section xml:id="rx-client.guava">
<title>Guava – ListenableFuture and Futures</title>
<titleabbrev xml:id="rx-client.guava.title">Guava (ListenableFuture and Futures)</titleabbrev>
<para>
&guava.link;, contributed by Google, also contains a type, &guava.ListenableFuture;, which can be decorated with
listeners that are notified when the future completes. The &lit.guava.ListenableFuture; can be combined with
&guava.Futures; to achieve asynchronous/event-based completion aware processing. &lit.guava.ListenableFuture;
is the parameter type in the Guava's extension of &lit.jaxrs.client.RxInvoker;, called
&jersey.ext.rx.client.guava.RxListenableFutureInvoker;. This means that the return type of HTTP method calls is
&lit.guava.ListenableFuture; in this case (accordingly parametrized).
</para>
<para>
Requests are by default invoked immediately. If not said otherwise the &jdk8.Executors.newCachedThreadPool; pool
is used to obtain a thread which processed the request. This behavior can be overridden by providing a
&jdk6.ExecutorService; when a &lit.jaxrs.client.Client; is created.
</para>
<simplesect>
<title>Usage</title>
<para>
The extensibility is built-in JAX-RS Client API, so there are no special dependencies on Jersey Client
API other than the extension itself.
<example xml:id="rx.client.guava.rx">
<title>Creating Jersey/Guava Client</title>
<programlisting language="java" linenumbering="numbered">// New Client
Client client = ClientBuilder.newClient();
client.register(RxListenableFutureInvokerProvider.class);</programlisting>
</example>
</para>
<para>
An example of obtaining &lit.guava.ListenableFuture; with JAX-RS &lit.jaxrs.core.Response; from a remote
service can be seen in <xref linkend="rx.client.guava.usage" />.
<example xml:id="rx.client.guava.usage">
<title>Obtaining ListenableFuture<Response> from Jersey/Guava Client</title>
<programlisting language="java" linenumbering="numbered">
ListenableFuture<Response> response = client.target("http://jersey.java.net")
.request()
.rx(RxListenableFutureInvoker.class)
.get();
</programlisting>
</example>
</para>
</simplesect>
<simplesect>
<title>Dependencies</title>
<para>
The Reactive Jersey Client with Guava support is available as an extension module in Jersey. For Maven users,
simply add the following dependency to your &lit.pom.xml;:
<programlisting language="xml" linenumbering="unnumbered"><dependency>
<groupId>org.glassfish.jersey.ext.rx</groupId>
<artifactId>jersey-rx-client-guava</artifactId>
<version>&version;</version>
</dependency></programlisting>
After this step you can use the extended client right away. The dependency transitively adds the following
dependencies to your class-path as well: <literal>com.google.guava:guava</literal>.
</para>
<note>
<para>
If you're not using Maven (or other dependency management tool) make sure to add also all the transitive
dependencies of this extension module (see &jersey.ext.rx-client.guava.deps.link;) on the class-path.
</para>
</note>
</simplesect>
</section>
</section>
<section xml:id="rx.client.spi">
<title>Implementing Support for Custom Reactive Libraries (SPI)</title>
<para>
In case you want to bring support for some other library providing Reactive Programming Model into your application
you can extend functionality of Reactive JAX-RS Client by implementing &jaxrs.client.RxInvokerProvider;, registering
that implementation into the client runtime and then using rx(Class<T>) in your code.
</para>
<simplesect>
<title>Implement RxInvoker and RxInvokerProvider interfaces</title>
<para>
The first step when implementing support for another reactive library is to implement &jaxrs.client.RxInvoker;.
JAX-RS API itself contains one implementation, which will be used as an example: &jaxrs.client.CompletionStageRxInvoker;.
<example xml:id="rx.client.rxinvoker">
<title>Extending RxIvoker</title>
<programlisting language="java" linenumbering="numbered">public interface CompletionStageRxInvoker extends RxInvoker<CompletionStage> {
@Override
public CompletionStage<Response> get();
@Override
public <T> CompletionStage<T> get(Class<T> responseType);
// ...
}</programlisting>
</example>
</para>
<para>
The important fact to notice is that the generic parameter of &jaxrs.client.RxInvoker; is &jdk8.CompletionStage;
and also that the return type is overriden to be always &jdk8.CompletionStage; with some generic param (&jaxrs.core.Response;;
or T).
</para>
<para>
After having the extended RxInvoker interface, the implementor has to provide &jaxrs.client.RxInvokerProvider;,
which will be registered as an provider to a client instance.
</para>
<example xml:id="rx.client.extend.rxinvoker">
<title>Extending RxInvokerProvider</title>
<programlisting language="java" linenumbering="numbered">public static class CompletionStageRxInvokerProvider implements RxInvokerProvider<CompletionStageRxInvoker> {
@Override
public boolean isProviderFor(Class<?%gt; clazz) {
return CompletionStage.class.equals(clazz);
}
@Override
public CompletionStageRxInvoker getRxInvoker(SyncInvoker syncInvoker, ExecutorService executorService) {
return new CompletionStageRxInvoker() {
// ...
};
}
}</programlisting></example>
</simplesect>
<simplesect>
<title>Example of using custom RxInvokerProvider</title>
<para>
Considering the work above was done and the implementation of custom &lit.jaxrs.client.RxInvoker; and
&lit.jaxrs.client.RxInvokerProvider; is available, the client code using those extensions will be:
</para>
<programlisting language="java" linenumbering="numbered">Client client = ClientBuilder.newClient();
// register custom RxInvokerProvider
client.register(CompletionStageRxInvokerProvider.class);
CompletionStage<Response> response =
client.target("http://jersey.java.net")
.request()
.rx(CompletionStageRxInvoker.class)
// Now we have an instance of CompletionStageRxInvoker returned from our registered RxInvokerProvider,
// which is CompletionStageRxInvokerProvider in this particular scenario.
.get();</programlisting>
</simplesect>
</section>
</chapter>