users@jersey.java.net

[Jersey] Re: Async client and Response.readEntity

From: cowwoc <cowwoc_at_bbs.darktech.org>
Date: Thu, 29 Jan 2015 23:45:58 -0500

+1000

I've run across some very annoying bugs in my lifetime that resulted
from implementations that violated the superclasses/interface contract.

Gili

On 29/01/2015 10:22 PM, Robert DiFalco 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
> <mailto: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 <mailto: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
> <mailto: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
> <mailto: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
>> <mailto: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
>> <mailto: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
>> <mailto: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
>> <mailto: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
>>> <mailto: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
>>> <mailto: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
>>> <mailto: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 <mailto:mikael.staldal_at_appearnetworks.com>
>>>
>>>
>>>
>>>
>>>
>>> --
>>> Mikael Ståldal
>>> Chief Software Architect
>>> *Appear*
>>> Phone: +46 8 545 91 572
>>> Email: mikael.staldal_at_appearnetworks.com
>>> <mailto:mikael.staldal_at_appearnetworks.com>
>>
>>
>>
>>
>> --
>> Mikael Ståldal
>> Chief Software Architect
>> *Appear*
>> Phone: +46 8 545 91 572
>> Email: mikael.staldal_at_appearnetworks.com
>> <mailto:mikael.staldal_at_appearnetworks.com>
>>
>>
>>
>>
>>
>> --
>> Mikael Ståldal
>> Chief Software Architect
>> *Appear*
>> Phone: +46 8 545 91 572
>> Email: mikael.staldal_at_appearnetworks.com
>> <mailto:mikael.staldal_at_appearnetworks.com>
>
>
>
>
> --
> Mikael Ståldal
> Chief Software Architect
> *Appear*
> Phone: +46 8 545 91 572
> Email: mikael.staldal_at_appearnetworks.com
> <mailto:mikael.staldal_at_appearnetworks.com>
>
>
>
>
>
> --
> Mikael Ståldal
> Chief Software Architect
> *Appear*
> Phone: +46 8 545 91 572
> Email: mikael.staldal_at_appearnetworks.com
> <mailto:mikael.staldal_at_appearnetworks.com>
>
>