Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AsyncResponse ConnectionCallback does not fire in Jersey

For asynchronous programming, Jersey (JAX-RS) 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.

Sounds great, but I can never get this to fire.

Here's some code:

@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("onComplete called.");
        }
    });

    asyncResponse.register(new ConnectionCallback() {
        @Override
        public void onDisconnect(AsyncResponse disconnected) {
            logger.info("onDisconnect called.");
        }
    });

    asyncResponse.setTimeout(POLL_TIMEOUT_SECONDS, TimeUnit.SECONDS);
    asyncResponse.setTimeoutHandler(new TimeoutHandler() {
        @Override
        public void handleTimeout(AsyncResponse asyncResponse) {
            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? Is ConnectionCallback implemented in Jersey? (It's optional in the JAX-RS spec, but the Jersey docs talk about it as though it's implemented.)

Any input would be appreciated.

like image 616
ricb Avatar asked Nov 07 '14 04:11

ricb


1 Answers

ConnectionCallback is indeed implemented in Jersey. And 'onDisconnect' callback is also invoked. You can take a look at the following code in Jersey:

https://github.com/jersey/jersey/blob/a6ff4d50da13d45ad90fd7375a15a31afa02e489/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java#L723

https://github.com/jersey/jersey/blob/b7907e279010e7035a7a3e529993d22f77a21e08/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java#L246-L252

These are thrown in case of IOException while writing response. So, to answer your question, there is no polling or similar mechanism that keeps checking whether the client is connected, rather the onDisconnect method is called only when there is an IOException which usually occurs while writing response.

Update 1:

Also I would like to quote your own question:

"This callback will be executed only if the connection was prematurely terminated or lost while the response is being written to the back client"

So, unless you try to write to that stream, your callback will never be fired. To be clear, it's not meant to be called when there is no response to write or when the response is 202, rather it's meant to be invoked when the connection was terminated prematurely while response is being written.

I am afraid there is no workaround to this problem unless you write some low level network programming with polling of some sort. But I wouldn't recommend you to go for this.

I suggest that you rethink the way you want to handle this failure.

like image 83
James Avatar answered Oct 13 '22 21:10

James