Reactive landscape
In the previous sections, we learned how to use pure RxJava and how to combine it with Spring Web MVC. To demonstrate the benefits of this, we have updated our temperature-monitoring application and improved the design by applying RxJava. However, it is worth noting that Spring Framework and RxJava is not the only valid combination. A lot of application servers also value the power of the reactive approach. As such authors of a successful reactive server called Ratpack decided to adopt RxJava as well.
Along with callbacks and a promise-based API, Ratpack provides RxRatpack, a separate module that allows to convert Ratpack Promise to RxJava Observable easily, and vice versa, as shown in the following code:
Promise<String> promise = get(() -> "hello world");
RxRatpack
.observe(promise)
.map(String::toUpperCase)
.subscribe(context::render);
Another example that is famous in the Android world is the HTTP client Retrofit, which also creates a RxJava wrapper around its own implementation of Futures and Callbacks. The following example shows that at least four different coding styles may be used in Retrofit:
interface MyService {
@GET("/user")
Observable<User> getUserWithRx();
@GET("/user")
CompletableFuture<User> getUserWithJava8();
@GET("/user")
ListenableFuture<User> getUserWithGuava();
@GET("user")
Call<User> getUserNatively()
}
Even though RxJava may improve any solution, the reactive landscape is not limited to it or its wrappers. In the JVM world, there are a lot of other libraries and servers that created their reactive implementations. For instance, the well-known reactive server Vert.x only used callback based communication for a period of time, but later created its own solution with the io.vertx.core.streams package, which harbors the following interfaces:
- ReadStream<T>: This interface represents a stream of items that can be read from
- WriteStream<T>: This describes a stream of data that can be written to
- Pump: This is used for moving data from ReadStream to a WriteStream and performing flow control
Let's look at the code snippet with the Vert.x example:
public void vertexExample(HttpClientRequest request, AsyncFile file) {
request.setChunked(true);
Pump pump = Pump.pump(file, request);
file.endHandler(v -> request.end());
pump.start();
}
The number of RxJava adoptions and alternative implementations is enormous, and far from being limited to the mentioned solutions. A lot of companies and open source projects around the world have created their own solutions similar to RxJava, or they have extended ones that are already present.
Admittedly, there is nothing wrong with the natural evolution and competition between libraries, but it is evident that problems will occur as soon as we try to compose a few different reactive libraries or frameworks in one Java application. Moreover, we will eventually find that the behavior of reactive libraries is similar in general, but differs slightly in the details. Such a situation may compromise the whole project because of hidden bugs that would be hard to spot and fix. So, with all these API discrepancies, it is not a very good idea to mix a few different reactive libraries (let's say, Vert.x and RxJava) in one application. At this point, it becomes apparent that the whole reactive landscape demands some standard or universal API, which will provide compatibility guarantees between any implementations. Of course, such a standard was designed; it is called Reactive Streams. The next chapter will cover this in detail.