users@jax-rs-spec.java.net

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

From: Pavel Bucek <pavel.bucek_at_oracle.com>
Date: Sun, 22 Jan 2017 12:08:49 +0100

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")
               .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 PavelableProviderimplements 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
> <mailto: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
>> <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
>> <mailto:santiago.pericasgeertsen_at_oracle.com>>:
>>
>>> On Jan 19, 2017, at 1:12 PM, Markus KARG
>>> <markus_at_headcrashing.eu <mailto: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
>>> whyrx(T)will return*not*returnT. 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
>>> <mailto:santiago.pericasgeertsen_at_oracle.com>]*Sent:*Donnerstag,
>>> 19. Januar 2017 16:55
>>> *To:*jsr370-experts_at_jax-rs-spec.java.net
>>> <mailto: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
>>