users@jersey.java.net

[Jersey] Re: Async client and Response.readEntity

From: Robert DiFalco <robert.difalco_at_gmail.com>
Date: Wed, 28 Jan 2015 10:52:08 -0800

I think you should be able to make it happen like this:

Make a POST method that returns a complex JSON object. In my case it was
the GCM server. My code looked like this:

Future<Response> future = asyncInvoker.post(Entity.json(request), new
PrintResponseEntityHandler<Response>());
assertEquals(expectedGcmResult, future.get().readEntity(GcmResult.class));

In the handler have #complete just print out the
response.readEntity(GcmResult.class).

You can change GcmResult to whatever JSON object you want.

What happens is that the stream reader gets a race condition during
#readEntity and gets corrupted. And it is not clear where to buffer it so
it can be called repeatedly because It's not well defined if the future
will execute first or if #complete in the handler will. (And buffering does
not seem to be atomic). If you change the Future and CallBack to
parameterize on the actual Response entity class instead of the Response
itself, the problem goes away.

    Entity.json(request),
    new Handler(request, deviceDao));


On Wed, Jan 28, 2015 at 9:38 AM, Marek Potociar <marek.potociar_at_oracle.com>
wrote:
>
> Hi Robert,
>
> Do you have a simple use case that could demonstrate the issue you are
seeing? (Even if only intermittently…)
>
> Cheers,
> Marek
>
> On 22 Jan 2015, at 19:48, 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 ?
>>
>> 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? Something like
Response.readEntity(Order.class, order -> { … }) ?
>>> Btw. do you know that you can easily check the response code from a
JAX-RS ClientResponseFilter ?
>>>
>>>
>>> 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
>
>
>