users@jersey.java.net

[Jersey] Re: Async client and Response.readEntity

From: Mikael Ståldal <mikael.staldal_at_appearnetworks.com>
Date: Fri, 30 Jan 2015 10:03:05 +0100

Then the method should not return a Future, but only a more limited
interface allowing only cancellation.

On Fri, Jan 30, 2015 at 4:22 AM, Robert DiFalco <robert.difalco_at_gmail.com>
wrote:

> Mikael, my point is that code should work deterministically. A note in
> javadoc that says you shouldn't do something permitted by the interface is
> not my idea of good design. The interface should enforce the contract.
>
> On Thu, Jan 29, 2015 at 7:35 AM, Mikael Ståldal <
> mikael.staldal_at_appearnetworks.com> wrote:
>
>> Just because it happens to work fine for you in this case, doesn't mean
>> it's a good idea to do it in general.
>>
>> On Thu, Jan 29, 2015 at 4:24 PM, Robert DiFalco <robert.difalco_at_gmail.com
>> > wrote:
>>
>>> No because if the Callback and Future are parameterized on the entity
>>> type rather than Response it works fine. I think a simpler solution would
>>> be to wrap the callback such that when completed or error completes it sets
>>> the Future value and unblocks #get. That way you can at least make
>>> assurances that #get does not unblocked until the Callback has completed.
>>>
>>> On Thu, Jan 29, 2015 at 12:05 AM, Mikael Ståldal <
>>> mikael.staldal_at_appearnetworks.com> wrote:
>>>
>>>> Then I guess that you should never use the get() method on the returned
>>>> Future when you use a callback. Perhaps the documentation should mention
>>>> this.
>>>>
>>>> On Wed, Jan 28, 2015 at 6:37 PM, Marek Potociar <
>>>> marek.potociar_at_oracle.com> wrote:
>>>>
>>>>> The reason for that is that you should be able to cancel the running
>>>>> task. There is nothing strange really - look at Executor APIs in Java.
>>>>>
>>>>> Marek
>>>>>
>>>>> On 23 Jan 2015, at 09:43, Mikael Ståldal <
>>>>> mikael.staldal_at_appearnetworks.com> wrote:
>>>>>
>>>>> To me i seems strange that the same API method both takes a callback
>>>>> and returns a Furure. It should be either or, not both.
>>>>>
>>>>> On Thu, Jan 22, 2015 at 7:48 PM, Robert DiFalco <
>>>>> robert.difalco_at_gmail.com> wrote:
>>>>>
>>>>>> Marek, the issue I had is that the behavior of getting a
>>>>>> Future<Response> with a callback is indeterminate. There seems to be a race
>>>>>> condition with readEntity such that even if you request the entity to be
>>>>>> buffered it can still end up as malformed in either the Future#get or the
>>>>>> InvocationCallback#completed.
>>>>>>
>>>>>> On Thu, Jan 22, 2015 at 9:34 AM, Mikael Ståldal <
>>>>>> mikael.staldal_at_appearnetworks.com> wrote:
>>>>>>
>>>>>>> > 1. InvocationCallback<Order> is run from some other thread (i.e.
>>>>>>> asynchronously) that has issued the request
>>>>>>>
>>>>>>> Yes, so far so good. But then I cannot access the response metadata
>>>>>>> (HTTP status, response headers).
>>>>>>>
>>>>>>> > 2. The callback is only invoked AFTER the
>>>>>>> Response.readEntity(Order.class) has completed. So having an
>>>>>>> InvocationCallback<Response> and invoking Response.readEntity(..) inside is
>>>>>>> not making things worse.
>>>>>>>
>>>>>>> So if I do like this:
>>>>>>>
>>>>>>> Invocation invocation = client.target(someURL).request().buildGet();
>>>>>>> invocation.submit(new InvocationCallback<Response> {
>>>>>>> public void completed(Response response) {
>>>>>>> int statusCode = response.getStatus();
>>>>>>>
>>>>>>> Order order = response.readEntity(Order.class); // blocking? process(statusCode, order);
>>>>>>> }
>>>>>>> public void failed(Throwable throwable) {
>>>>>>> error();
>>>>>>> }
>>>>>>> });
>>>>>>>
>>>>>>>
>>>>>>> It will not block on I/O when invoking response.readEntity(Order.
>>>>>>> class)?
>>>>>>> If so, my understanding of how it works was wrong, and the issue is
>>>>>>> invalid.
>>>>>>>
>>>>>>> > Perhaps you are looking for a non-blocking I/O API support
>>>>>>>
>>>>>>> I am, but this particular issue is not really about that.
>>>>>>>
>>>>>>> > Something like Response.readEntity(Order.class, order -> { … }) ?
>>>>>>>
>>>>>>> If it works the way you say (Response.readEntity is non-blocking
>>>>>>> since it just return something which has already been read, parsed and
>>>>>>> buffered in memory), that shouldn't be necessary.
>>>>>>>
>>>>>>> > Btw. do you know that you can easily check the response code from
>>>>>>> a JAX-RS ClientResponseFilter
>>>>>>> <https://jax-rs-spec.java.net/nonav/2.0-rev-a/apidocs/javax/ws/rs/client/ClientResponseFilter.html>
>>>>>>> ?
>>>>>>>
>>>>>>> To me, it seems quite clumsy if I would have to use a filter in this
>>>>>>> use case.
>>>>>>>
>>>>>>>
>>>>>>> On Thu, Jan 22, 2015 at 1:42 PM, Marek Potociar <
>>>>>>> marek.potociar_at_oracle.com> wrote:
>>>>>>>
>>>>>>>> Hi Mikael,
>>>>>>>>
>>>>>>>> Please see inline.
>>>>>>>>
>>>>>>>> On 12 Jan 2015, at 11:29, Mikael Ståldal <
>>>>>>>> mikael.staldal_at_appearnetworks.com> wrote:
>>>>>>>>
>>>>>>>> No, I did not get any answer to this. And neither to the JIRA
>>>>>>>> issues I have filed:
>>>>>>>>
>>>>>>>> https://java.net/jira/browse/JERSEY-2682
>>>>>>>>
>>>>>>>>
>>>>>>>> C&Ping my comment from the issue:
>>>>>>>>
>>>>>>>> I have looked at the issue and I’m not sure I understand the
>>>>>>>> problem you are pointing out:
>>>>>>>>
>>>>>>>> 1. InvocationCallback<Order> is run from some other thread (i.e.
>>>>>>>> asynchronously) that has issued the request
>>>>>>>> 2. The callback is only invoked AFTER the
>>>>>>>> Response.readEntity(Order.class) has completed. So having an
>>>>>>>> InvocationCallback<Response> and invoking Response.readEntity(..) inside is
>>>>>>>> not making things worse.
>>>>>>>>
>>>>>>>> Perhaps you are looking for a non-blocking I/O API support, which
>>>>>>>> is planned for the next release of JAX-RS
>>>>>>>> <https://jcp.org/en/jsr/detail?id=370>? Something like
>>>>>>>> Response.readEntity(Order.class, order -> { … }) ?
>>>>>>>> Btw. do you know that you can easily check the response code from a
>>>>>>>> JAX-RS ClientResponseFilter
>>>>>>>> <https://jax-rs-spec.java.net/nonav/2.0-rev-a/apidocs/javax/ws/rs/client/ClientResponseFilter.html>
>>>>>>>> ?
>>>>>>>>
>>>>>>>>
>>>>>>>> To me, it seems like the async part of the JAX-RS 2.0 client API is
>>>>>>>> flawed (not to mention the default implementation in Jeresy:
>>>>>>>> https://java.net/jira/browse/JERSEY-2058).
>>>>>>>>
>>>>>>>>
>>>>>>>> The primary problem is in lack of NIO support in the current JAX-RS
>>>>>>>> API. We’re looking into fixing it for the next JAX-RS release, as noted
>>>>>>>> above. Obviously, we will first try to make it work in Jersey, so Jersey
>>>>>>>> users should have a working solution long before JAX-RS.next is released. I
>>>>>>>> do not have any ETA at this point though.
>>>>>>>>
>>>>>>>>
>>>>>>>> It's a pity, since the synchronous client API in JAX-RS 2.0 (and
>>>>>>>> its implementation in Jersey) is really nice.
>>>>>>>>
>>>>>>>> I am considering using something else for async REST client, such
>>>>>>>> as https://github.com/AsyncHttpClient/async-http-client or Jetty
>>>>>>>> client.
>>>>>>>>
>>>>>>>>
>>>>>>>> FWIW, our grizzly connector is actually wrapped into async HTTP
>>>>>>>> client API. But again, current lack of NIO support in JAX-RS APIs is making
>>>>>>>> it difficult to come up with a good async client solution. That said, we’re
>>>>>>>> already trying, stay tuned.
>>>>>>>>
>>>>>>>> Marek
>>>>>>>>
>>>>>>>>
>>>>>>>> On Wed, Dec 31, 2014 at 9:06 PM, Robert DiFalco <
>>>>>>>> robert.difalco_at_gmail.com> wrote:
>>>>>>>>
>>>>>>>>> Did you ever get an answer to this?
>>>>>>>>>
>>>>>>>>> On Mon, Oct 6, 2014 at 7:45 AM, Mikael Ståldal <
>>>>>>>>> mikael.staldal_at_appearnetworks.com> wrote:
>>>>>>>>>
>>>>>>>>>> Consider using Jersey client in async mode with an
>>>>>>>>>> InvocationCallback<Response>, and then use readEntity() on the Response.
>>>>>>>>>> Order is a custom domain object for which we have a MessageBodyReader
>>>>>>>>>> available.
>>>>>>>>>>
>>>>>>>>>> Invocation invocation =
>>>>>>>>>> client.target(someURL).request().buildGet();
>>>>>>>>>> invocation.submit(new InvocationCallback<Response> {
>>>>>>>>>> public void completed(Response response) {
>>>>>>>>>> int status = response.getStatus();
>>>>>>>>>> if (status == 200) {
>>>>>>>>>> Order order = response.readEntity(Order.class); //
>>>>>>>>>> blocking ?
>>>>>>>>>> process(order);
>>>>>>>>>> } else {
>>>>>>>>>> error();
>>>>>>>>>> }
>>>>>>>>>> }
>>>>>>>>>> public void failed(Throwable throwable) {
>>>>>>>>>> error();
>>>>>>>>>> }
>>>>>>>>>> });
>>>>>>>>>>
>>>>>>>>>> Is the response.readEntity() call blocking? Is it I/O bound if
>>>>>>>>>> the response is large? Or is the whole response read from network before
>>>>>>>>>> the completed() callback is invoked?
>>>>>>>>>>
>>>>>>>>>> Will the asynchronicity be improved if I do
>>>>>>>>>> InvocationCallback<Order> instead? What if I want to get other information
>>>>>>>>>> from the Response?
>>>>>>>>>>
>>>>>>>>>> --
>>>>>>>>>> Mikael Ståldal
>>>>>>>>>> Chief Software Architect
>>>>>>>>>> *Appear*
>>>>>>>>>> Phone: +46 8 545 91 572
>>>>>>>>>> Email: mikael.staldal_at_appearnetworks.com
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> Mikael Ståldal
>>>>>>>> Chief Software Architect
>>>>>>>> *Appear*
>>>>>>>> Phone: +46 8 545 91 572
>>>>>>>> Email: mikael.staldal_at_appearnetworks.com
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Mikael Ståldal
>>>>>>> Chief Software Architect
>>>>>>> *Appear*
>>>>>>> Phone: +46 8 545 91 572
>>>>>>> Email: mikael.staldal_at_appearnetworks.com
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Mikael Ståldal
>>>>> Chief Software Architect
>>>>> *Appear*
>>>>> Phone: +46 8 545 91 572
>>>>> Email: mikael.staldal_at_appearnetworks.com
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> Mikael Ståldal
>>>> Chief Software Architect
>>>> *Appear*
>>>> Phone: +46 8 545 91 572
>>>> Email: mikael.staldal_at_appearnetworks.com
>>>>
>>>
>>>
>>
>>
>> --
>> Mikael Ståldal
>> Chief Software Architect
>> *Appear*
>> Phone: +46 8 545 91 572
>> Email: mikael.staldal_at_appearnetworks.com
>>
>
>


-- 
Mikael Ståldal
Chief Software Architect
*Appear*
Phone: +46 8 545 91 572
Email: mikael.staldal_at_appearnetworks.com