users@jax-rs-spec.java.net

[jax-rs-spec users] Re: Reactive Programming - Community Feedback

From: Markus KARG <markus_at_headcrashing.eu>
Date: Tue, 20 Oct 2015 18:54:48 +0200

On Oct 19, 2015, at 4:11 PM, Markus KARG <markus_at_headcrashing.eu> wrote:

 

  The use of an rx() fits better with the rest of the fluent API. It is just another “modifier” like async(), that is, just another “invoker”. If you start from ClientBuilder, then why not do that for other invokers?

 

 In fact, the person that designed the original Jersey API liked this approach better as well, especially for the JAX-RS API.

 

I see your point, but there is a difference between async and rx from a conceptional view: Typically one will write a complete application in a reactive style COMPLETELY and will not mix reactive and non-reactive style in one single application (this would be like a hybrid using OOP and non-OOP). So it makes not much sense to repeat the rx() again and again. async is actually decided per method, as typically most methods are non-async but only SOME are async. So rx() makes sense when building a client, while async() typically makes sense only when building an invocation.

 

 I don’t agree with your argument. Look, this is what you can do in 2.0:

 

Future<String> f = target.request().async().get(String.class);

 

 This is the new proposal in 2.1:

 

CompletableStage<String> f = target.request().rx().get(String.class);

 

 Symmetric, and easy to understand. No need to create a new path starting from Client that developers aren’t familiar with. The Invoker idea was an extension point created for this.

 

I understand your driver, but I cannot see why rx() and async() SHOULD be symmetric, as both cover diferent cases, as I pointed out. I do not see a need for rx() in in a request, only in a client, while I do see a need for async() on a request, but not on a client. I think we shouldn't design an API around "mathematical symmetry", but around use cases. But what do the other experts think? Any opinions?

 

 

 Easy, only Java 8. I think reactive APIs are still evolving, so an extension point to allow future APIs to work with JAX-RS (without the need to rev the spec) would be useful.

 

I understand your vision, but the question is, whether we can provide a simple Java 8 support without adding the complexity needed solely for the sake of future flexibility? I mean, this goes a bit into premature optimization in the sense of D. E. Knuth: Wouldn't it be evil to specify an extension point before we know that we really will need it in future? What if Java 10 comes with a full-monty reactive support that completely is built ontop of CompletableStage, and no other framework actually will be of broad interest? We will then have to maintain a complexity level that is not needed.

 

 I think RxJava may be of interest to a lot of people. Extension points and premature optimization are unrelated.

 

Yes I second that, but simply had neither customers nor community contacts doing anything productive with RxJava, actually, while CompletableFuture seems to be in active use. What I liked to say is, as long as RxJava is not much common, do we really have to care about it, or could we add "real" (deeper) RxJava integration at a later time?

 

 

* The code snipped Santiago was pointing to does not showcase how to use RX on server side. In particular, it might be useful to glue together pending CompletableFutures with @Asynchronous,

 

 @Asynchronous is not part of JAX-RS, only Jersey. Other examples using AsyncResponse are very easy to write.

 

Do you have some example at hand which you can disclose so we better understand what your plans on server side are?

 

 Maybe I misunderstood what you meant by RX on the server side. RX support is for the client. Of course, servers can be clients as well, in which case you may want to use AsyncResponse in your resource method. That is what I meant.

 

Yes I meant server side support, not a client running on a server. Given the case a client requests a list of flight prices, and the server resource tries to resolve this by asking lots of external ticket dealers for a quote, then we have two options with RX: First, we could write a last code line that makes the thread BLOCK inside of the resource method until the completable stages have joined. This is not nice, as the server's limited thread pool drains rather easily that way. Or, we could simply return the joined stages to the JAX-RS container (as the return value of the resource method), and let the container put that chains of stages on the work stack (i. e. supply it to the managed executor service). This way, the thread is free for other work and can safely be returned to the server's pool -- and the code is simple to write, as the application programmer does not need to deal with threads at all:

 

@Resource ManagedExecutorService CONTAINER;

 

@GET public CompletableStage<List<Quote>> listQuotes(Flight flight) {

    Client client = ClientBuilder.newClient(CONTAINER);

    CompletableStage<Quote> quote1 = client.target(URI_1).request().get();

    CompletableStage<Quote> quoteN = client.target(URI_N).request().get();

    return quote1.thenCombineAsync(quoteN, MERGE_LISTS);

}

 

As you can see, the thread executing this method can be returned to the thread pool within microseconds, as no blocking operations are performed. The work will completely be put on the work stack of the managed executor service immediately. The sole contact the application programmer has to that executor is in the line where the client is created!

 

What the JAX-RS container has to do after the end of the method "listQuotes" is (a) return the thread into pool (typically be done by the underlying servlet container anyways, as it is "his" pool, so a no-brainer for the JAX-RS container itself) and (b) add a terminating step at the end of the function result (i. e. the resulting CompletableStage) that will fire the continuation of the async response (MBW, Interceptor Chain, Filter Chain --> Servlet response) in the form of "result.thenApply(asyncResponse::resume)".

 

This separation of thread pools into "LAN threads" and "worker threads" is really essential in heavy-duty production environments, as both pools typically will apply different sizes and policies. The API I proposed above shows that the application programmer has rather nothing to do with the technology under the hood, at the same time; it is simple to understand, and it is rather simple to implement in the JAX-RS engine.

 

-Markus

 

 

From: Santiago Pericasgeertsen [ <mailto:santiago.pericasgeertsen_at_oracle.com> mailto:santiago.pericasgeertsen_at_oracle.com]
Sent: Montag, 12. Oktober 2015 16:30
To: <mailto:jsr370-experts_at_jax-rs-spec.java.net> jsr370-experts_at_jax-rs-spec.java.net
Subject: Drafts for Rx, NIO and SSE

 

Dear Experts,

 

 My apologies for sending all these drafts in one shot, but I feel we need to start making progress quickly if we want to produce an EDR in a few months.

 

 The following pull request [1] includes proposals for Rx, NIO and SSE. Let me point you to the examples to get you started:

 

 (1) Rx:

 

  <https://github.com/spericas/api/blob/master/jaxrs-api/src/test/java/javax/ws/rs/core/RxClientTest.java> https://github.com/spericas/api/blob/master/jaxrs-api/src/test/java/javax/ws/rs/core/RxClientTest.java

 

 This is an extension to our Client API that integrates with the CompletableFuture API in JDK 8 (note that CompletionStage is a super type of CompletableFuture). The proposal is extensible and can use other APIs similar to CompletableFuture —see the optional parameter to the rx() method for that purpose. The spec will only mandate support for CompletableFuture, but implementations are free to provide support for other APIs, such as RxJava.

 

 (2) NIO:

 

 <https://github.com/spericas/api/blob/master/examples/src/main/java/jaxrs/examples/nio/FileResource.java> https://github.com/spericas/api/blob/master/examples/src/main/java/jaxrs/examples/nio/FileResource.java

 <https://github.com/spericas/api/blob/master/examples/src/main/java/jaxrs/examples/nio/FileResourceClient.java> https://github.com/spericas/api/blob/master/examples/src/main/java/jaxrs/examples/nio/FileResourceClient.java

 

 On the server side, the proposal follows the pattern set by StreamingOutput, but of course using lambdas this time around. As you know, Servlet already supports NIO, but with a completely different API.

 

 (3) SSE:

 

 <https://github.com/mpotociar/api/tree/master/examples/src/main/java/jaxrs/examples/sse> https://github.com/mpotociar/api/tree/master/examples/src/main/java/jaxrs/examples/sse

 

 Note that like in NIO, there are proposed extensions to both the Client and Server APIs for SSE.

 

 I understand that this is a lot to digest, so feel free to start new e-mail threads to discuss each one independently. I just felt it was important to start all these discussions ASAP.

 

— Santiago

 

[1] <https://github.com/jax-rs/api/pulls> https://github.com/jax-rs/api/pulls