users@jax-rs-spec.java.net

[jax-rs-spec users] Re: JAX-RS 2.1 - work schedule

From: Markus KARG <markus_at_headcrashing.eu>
Date: Sun, 22 Jan 2017 16:00:06 +0100

Pavel,

 

I know what you mean and I have read that posting of yours back then, and the sole technical solution presumably would be that application programmer has to explicitly resolve that type variable by writing…

request.<RxInvoker<CompletionStage>> rx(CompletionStage.class).get();

…and something the like which is as ugly as the current proposal certainly.

 

Hence, what I wanted to say is: As long as this EG wants to have plugability and type safety, we have to stick with your latest propsal. So we have to trade off plugability, type safety and simplicity for the average programmer. Not more. Not less.

 

The syntax problem you describe is a direct effect of the request to support RX framework plugability within JAX-RS. The EC-accepted charter for this JSR does not enforce dynamic return types. RX support could also be provided without directly supporting such frameworks, e. g. (like aleady discussed) using adapters to / from CompletionStage, so the API could simply use CompletionStage everywhere. But, looking at the discussion threads of the past weeks, this apparently is not intended by the spec leads and the majority of the active EG members (we still don't know that the silent majority wants, BTW), so that discussion simply is over, which I do accept. Hence, the application programmers have to live with the result: An overly complex API. I don't want to heat up that discussion again, I just wanted to make clear the context of my answer.

 

I think this time my point was clearly explained. :-)

-Markus

 

 

From: Pavel Bucek [mailto:pavel.bucek_at_oracle.com]
Sent: Sonntag, 22. Januar 2017 14:44
To: jsr370-experts_at_jax-rs-spec.java.net
Subject: Re: [jax-rs-spec users] Re: JAX-RS 2.1 - work schedule

 

Hi Markus,

I'm not sure whether you were able to follow the whole thread, but please look at this post:

https://java.net/projects/jax-rs-spec/lists/jsr370-experts/archive/2017-01/message/81

In short - we tried to do it with rx(Pavelable.class), but it doesn't fit in current API, since generics doesn't allow us to sufficiently define the return type. (it about returning RxInvoker<T> vs ? extends RxInvoker; more details in the original thread).

If you are able to keep the rx() method in the chain as it is and do it in a way that we can pass only Pavelable.class, I'm all ears.

Thanks and regards,
Pavel

 

On 22/01/2017 13:51, Markus KARG wrote:

Pavel,

 

besides all technical issues your proposal certainly does solve, infact I strongly dislike the idea that an application programmers has to type the word "PavelableInvoker.class" but would stronlgy love to type the word "Pavelable.class" instead. The first approach certainly is absolutely correct and logical, but the latter is, what people actually want to say: "Give me a CompletionStage". Nobody says "Give me a CompletionStageInvokerProvider" in the real world, despite the technical fact that this is what he technically has to do.

 

My proposal would be to make this user-oriented simplification possible if we find a technical way to solve this. If we don't then we just have to live with your proposal as-is.

 

-Markus

 

 

From: Pavel Bucek [mailto:pavel.bucek_at_oracle.com]
Sent: Sonntag, 22. Januar 2017 12:09
To: jsr370-experts_at_jax-rs-spec.java.net
Subject: Re: [jax-rs-spec users] Re: JAX-RS 2.1 - work schedule

 

Hi Ondrej,

just a note:

For me it is important not to pass any implementation to rx() method, and the solution of .rx(CompletionStageRxInvoker.

class) meets that. The developer jut needs to write an empty interface, jut to define proper type conversions, which is nice (except that awful number of methods to override, but I have no energy now to try to propose something better to reduce the number :) )

The developer won't be writing any interface - it won't work if he will do that. He needs to use exactly the same interface which is provide as part of the "extension implementation", so for example if there is a library with support for "org.pavel.Pavelable<T>", it contains:

* org.pavel.PavelInvoker implements RxInvoker<Pavelable>
* org.pavel.PavelProvider implements RxInvokerProvider<PavelInvoker>

and the code will always look like:

 

Client client = ClientBuilder.newClient();
 
client.register(org.pavel.PavelableProvider.class);
 
Pavelable<Response> csResponse =
        client.target( <http://oracle.com> "http://oracle.com")
              .request()
              .rx(org.pavel.PavelableInvoker.class)
              .get();


Simply because underlying client runtime does a lookup for provider for a class PavelableInvoker, which in this case would be something like:




public static class PavelableProvider implements RxInvokerProvider<PavelableInvoker> {
    @Override
    public boolean provides(Class<?> clazz) {
        return Pavelable.class.equals(clazz);
    }
 
    @Override
    public PavelableInvoker getRxInvoker(SyncInvoker syncInvoker, ExecutorService executorService) {
        return //...;
    }
}


(note the "provides" method).

hope the proposal is clearer now. Please let me know if that changed anything about how you like it :).

Best regards,
Pavel





 

On 20/01/2017 18:07, Ondrej Mihályi wrote:

Thanks for your honesty, Pavel.

 

I didn't mean to propose a final solution, just a proof that the approach suggested by me and Markus can work.

 

What I'm after is really the consistency - Java EE API is mostly about standardizing what should be done and not how. Therefore it is more natural for me to declare that I want to use CompletionStage, RxJava or whatever, rather than to define how to do this conversion by passing a converter each time I want to use it. Even more if the converter is meant to be provided by the implementation and not by users of the API.

 

I accept that my solution is not perfect, I just wanted to point out that the approach is possible.

 

I agree that the last suggestion by Santiago is the best solution so far. For me it is important not to pass any implementation to rx() method, and the solution of .rx(CompletionStageRxInvoker.class) meets that. The developer jut needs to write an empty interface, jut to define proper type conversions, which is nice (except that awful number of methods to override, but I have no energy now to try to propose something better to reduce the number :) )

 

Thanks for the discussion :)

Ondrej

 

2017-01-20 15:27 GMT+01:00 Pavel Bucek <pavel.bucek_at_oracle.com>:

Hi Ondrej,

I like your determination and consistency.

Unfortunately, I have to strongly disagree with your last proposal. GenericType is nothing more than a standardized hack, which has limited usability and should be avoided if possible. Here, we do have that option.

Ad limited usability: imagine that user of the API will wrap your code in generic method, replacing List<String> with T. Then the information passed to the rx method will be exactly CompletionStage<T>, which cannot be used by the implementation for selecting the correct provider. Also, the code is (no offense) quite hard to write and read - we don't want to force anyone to write "Foo<Bar<Baz<Qux>>>". (I always wanted to use "qux" somewhere!)

I believe that the last variant brought up by Santiago fits nicely with existing APIs and uses already established provider pattern. I'd appreciate if you could evaluate it, maybe try to use it and share your thoughts once again.

Thanks and regards,
Pavel






On 20/01/2017 12:56, Ondrej Mihályi wrote:

Pavel raised a valid point, but it can really be solved by the GenericType class, just moving the usage of the GenericType from method get to method rx.

 

Here is a compilable and type-safe solution:

 

client.target("remote/forecast/{destination}")
      .resolveTemplate("destination", "mars")
      .request()
      .header("Rx-User", "Java8")
      .rx(new GenericType<CompletionStage<List<String>>>())
      .get();

with the new rx() method on Invocation.Builder defined simply like this:

public <T> RxInvoker<T> rx(GenericType<T> type);

You can play with the code in my fork here: https://github.com/OndrejM/api/blob/reactive_provider_by_return_type/jaxrs-api/src/test/java/javax/ws/rs/core/RxClientTest.java#L105

However, this solution also provides cons:

  - it is uglier at first sight (although GenericType is always necessary at some point sooner or later)

 - once converted to CompletionStage with proper type, it's not possible to retrieve the generic Response object in a typesafe way (making the methods like get(Class<R>) and get(GenericType<R> redundant - but maybe it;s a good thing and simplifies the RxInvoker interface)

The ugly part can be simplified by any developer using a class that extends GenericType like this (the code in my fork already shows that):

class CompletionStageType<T> extends GenericType<CompletionStage<T>>

And I don't think that the second drawback matters - if interested in Response, it can be retrieved by

.rx(new GenericType<CompletionStage<Response>() {})
      .get();

and converted later if needed.

Ondrej

2017-01-19 19:31 GMT+01:00 Santiago Pericasgeertsen <santiago.pericasgeertsen_at_oracle.com>:

On Jan 19, 2017, at 1:12 PM, Markus KARG <markus_at_headcrashing.eu> wrote:

Because in that "more complex" style you can write this then…

 

Client rxClient = client.register(Java8.class); // i. e. technically a CompletionStageRxInvokerProvider, which effectively is part of JAX-RS!!!

 
CompletionStage<UserPojo> cs =
        rxClient.target( <http://foo.bar/> "http://foo.bar")
                .request()
                .rx(CompletionStage.class)
                .get(UserPojo.class);

 

…so the average programmer clearly understands that this will register Java 8 as one possible RX provider (possibly in addition to others), so he can get a CompletionStage. It feels just more correct and simple, particular for JAX-RS beginners.

 Of course, but the point raised by Pavel is that this code again cannot (at least not obviously) be accepted by the Java type checker. Hence, the less desirable suggestion. Please read the rest of the e-mail thread.

— Santiago

They will not understand why they shall repeat the provider again and again, and they will not understand why rx(T) will return not return T. But I think they will accept to repeat the stage again and again, as stages are not reusable (they are used to do that with stages already).

 

-Markus

 

From: Santiago Pericasgeertsen [mailto:santiago.pericasgeertsen_at_oracle.com] Sent: Donnerstag, 19. Januar 2017 16:55 To: jsr370-experts_at_jax-rs-spec.java.net Subject: Re: [jax-rs-spec users] JAX-RS 2.1 - work schedule

 

Hi Pavel,

 

 Just catching up with this issue. I guess the two levels of indirection has led us into a generic wall :)

 

 

Client rxClient = client.register(CompletionStageRxInvokerProvider.class);

 
CompletionStage<UserPojo> cs =
        rxClient.target( <http://foo.bar/> "http://foo.bar")
                .request()
                .rx(CompletionStage.class)
                .get(UserPojo.class);

 So what if we reduce indirection and write:

 

   .rx(CompletionStageRxInvoker.class)

 

 as before, still keeping the provider for it? Less ideal of course.

 

— Santiago