users@jersey.java.net

[Jersey] Re: cannot get AsyncResponse CompletionCallback to fire

From: Ric Bernat <ric_at_brinydeep.net>
Date: Tue, 11 Nov 2014 20:36:48 -0800

Paul, thanks much for your reply, thoughts, and quick chunkedOutput
test. Yes, that excerpt from the Jersey docs is in my original post:
"This callback will be executed only if the connection was prematurely
terminated or lost while the response is being written to the back client."

I have read that sentence many times, and I ended up deciding it was
intended to be read this way:

-> This callback will be executed only if a) the connection was
prematurely terminated OR if b) the connection was lost while the
response is being written to the back client. [In other words, the
callback would be executed if the connection was prematurely terminated
under any circumstances, without regard to whether the response was
currently being written back to the client.]

I considered your interpretation as well:

-> This callback will be executed only if, while the response is being
written to the back client, the connection was either a) prematurely
terminated or b) lost.

I ended up ruling out the second interpretation, though, since I could
not see any way to distinguish between a connection being "prematurely
terminated" vs. "lost" in the case of both of these things happening
while the response is being written to the client.

Let's say the second interpretation is the right one, though. I don't
see that the API offers any way to check for dead/zombie connections. If
of the calling computers went offline 30 minutes ago, there would be no
way for the server to know about this, short of calling resume() on that
computer's connection (asyncResponse instance) and seeing if it throws
an exception. There doesn't seem to be any way to "poke" each open
connection to verify which ones are still alive (or, to say it the other
way, which are now dead). Perhaps that ability isn't important for most
use cases, but it is important for us. Let me know if you see any way
around that limitation.

The alternative solution I am considering is to use Server-Sent Events.
At least with SSE I would be able to send each open connection a
"heartbeat" message frequently, to verify that each connection was still
alive. Does that sound like a reasonable approach?

Thanks again for taking a look.

Ric

On 11/11/2014 7:04 PM, Paul O'Fallon wrote:
> I found a post on the CXF mailing list that pointed out the very
> specific verbiage in the Jersey docs. In the Jersey docs, it says
> "This callback will be executed only if the connection was prematurely
> terminated or lost while the response is being written to the back
> client." Note the "while the response is being written back to the
> client."
>
> To test this I added these lines to the end of your "poll" method:
>
> ChunkedOutput<String> chunkedOutput = new
> ChunkedOutput<String>(String.class);
> asyncResponse.resume(chunkedOutput);
> for (int i = 0; i < 50000; i++) {
> chunkedOutput.write("CHUNK-");
> }
>
> When I hit the URL with curl and CTRL-C'ed the response (after seeing
> several "CHUNK-"s), the ConnectionCallback fired. (I found this in
> one of the Jersey tests
> <https://github.com/jersey/jersey/blob/master/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncCallbackTest.java>).
>
> Hope this helps!
>
> - Paul
>
> On Tue, Nov 11, 2014 at 3:03 PM, Ric Bernat <ric_at_brinydeep.net
> <mailto:ric_at_brinydeep.net>> wrote:
>
> Really would appreciate any feedback at all on this question. It
> is a significant blocker for us. If this mailing list is not
> active, are there other resources I might check for Jersey
> development support/mentoring?
>
> Ric
>
>
> On 11/8/2014 8:34 AM, Ric Bernat wrote:
>
> For asynchronous programming, Jersey provides a
> ConnectionCallback callback that is to be executed when a
> connection is broken. From the Jersey docs:
>
> > As some async requests may take long time to process the
> client may
> > decide to terminate its connection to the server before the
> response
> > has been resumed or before it has been fully written to the
> client. To
> > deal with these use cases a ConnectionCallback can be used. This
> > callback will be executed only if the connection was prematurely
> > terminated or lost while the response is being written to
> the back
> > client. Note that this callback will not be invoked when a
> response is
> > written successfully and the client connection is closed as
> expected.
>
> https://jersey.java.net/documentation/latest/async.html#d0e8863
>
> Sounds great, but I can never get this to fire.
>
> I am running Jersey 2.13 on Tomcat 7.0.53. Clients are
> connecting directly to Tomcat (no Apache).
>
> Here is the web service:
>
> @GET
> @Produces(MediaType.TEXT_PLAIN)
> @ManagedAsync
> @Path("/poll")
> public void poll(@Suspended final AsyncResponse
> asyncResponse) {
> asyncResponse.register(new CompletionCallback() {
> @Override
> public void onComplete(Throwable throwable) {
> logger.info <http://logger.info>("onComplete called.");
> }
> });
>
> asyncResponse.register(new ConnectionCallback() {
> @Override
> public void onDisconnect(AsyncResponse disconnected) {
> logger.info <http://logger.info>("onDisconnect called.");
> }
> });
>
> asyncResponse.setTimeout(POLL_TIMEOUT_SECONDS,
> TimeUnit.SECONDS);
> asyncResponse.setTimeoutHandler(new TimeoutHandler() {
> @Override
> public void handleTimeout(AsyncResponse
> asyncResponse) {
> logger.info <http://logger.info>("handleTimeout called.");
> asyncResponse.resume(Response.status(Response.Status.OK).entity("TIMEOUT").build());
>
> }
> });
> }
>
> The other two callbacks shown, CompletionCallback and
> TimeoutHandler, fire just fine, without fail. If the specified
> timeout duration is reached, TimeoutHandler fires. If an
> AsyncResponse instance is resumed, CompletionCallback fires.
>
> However, with ConnectionCallback, I can close, kill, or
> otherwise stop the client that is sitting connected to the web
> service shown above, and ConnectionCallback never gets fired.
>
> Am I missing something?
>
> Any input would be appreciated.
>
>
>