Nuances of JAX-RS client side async callback

Going async with JAX-RS

JAX-RS provides client side asynchronous behavior to complement its server side counterpart. It’s powered by the AysncInvoker interface – the best part if that you do not need to get hold of it explicitly. The fluent JAX-RS client API makes it available by including the async() call in your builder calls

e.g. ClientBuilder.newClient().target(“https://test.com”).request().async().get

Like every other asynchronous offering, the JAX-RS client API offers you a couple of options to manage async calls

  • DIY (do-it-yourself) – get back a Future instance and work with it to track the result
  • Declarative callbacks – let the API know what you want it to do in case of a success or failure

Declarative callbacks

A callback is required to implement the InvocationCallback interface. It defines two methods

  • completed: gets called when an invocation successfully finishes. The result response is passed as a parameter to the callback method.
  • failed: it is invoked in case of a failure

Here is an example


@Test
public void testSuccess() throws InterruptedException {
CountDownLatch l = new CountDownLatch(1);
ClientBuilder.newClient().target("https://api.github.com").path("gists").path("ffd6ecff2dfb2fefcf784f114be9b9f7")
.request().async().get(new InvocationCallback<String>() {
@Override
public void completed(String response) {
Logger.getAnonymousLogger().log(Level.INFO, "Response\n{0}", response);
l.countDown();
}
@Override
public void failed(Throwable throwable) {
}
});
boolean result = l.await(5, TimeUnit.SECONDS);
assertTrue("completed method not called", result);
}

There is more to failure scenarios than meets the eye

The invocation of the failed method happens only in case of a failure – no doubts there. But there is a caveat – the criteria is based on the generic entity type (Java class type to be specific) of the response from the server.

Here is an example for the normal scenario

When the response type is anything other than javax.ws.rs.Response

In case of a server side error (e.g. HTTP 404), the failed method is invoked


@Test
public void failedMethodCalledAfterServerSideFailureForStringEntity() throws InterruptedException {
CountDownLatch l = new CountDownLatch(1);
ClientBuilder.newClient().target("http://google.com&quot;).path("first").request().async().get(new InvocationCallback<String>() {
@Override
public void completed(String response) {
}
@Override
public void failed(Throwable throwable) {
Logger.getAnonymousLogger().log(Level.INFO, "Error message: {0}", throwable.getMessage());
l.countDown();
}
});
boolean result = l.await(3, TimeUnit.SECONDS);
assertTrue("Failed method not called", result);
}

What if the generic type if javax.ws.rs.Response?

In such scenario, the completed method is invoked – that’s the catch! You can always investigate the actual HTTP response by calling the getStatus method (as demonstrated in the below example)


@Test
public void completedMethodCalledAfterServerSideFailureForGenericEntity() throws InterruptedException {
CountDownLatch l = new CountDownLatch(1);
ClientBuilder.newClient().target("http://google.com&quot;).path("first").request().async().get(new InvocationCallback<Response>() {
@Override
public void completed(Response response) {
Logger.getAnonymousLogger().log(Level.INFO, "HTTP Respoonse status: {0}", response.getStatus());
l.countDown();
}
@Override
public void failed(Throwable throwable) {
}
});
boolean result = l.await(3, TimeUnit.SECONDS);
assertTrue("Completed method not called", result);
}

At the outset, it might look odd ….

but think about it – you were expecting a response from the service and you did get one (it does not matter if it was success/failure). From the framework’s perspective, only if the API call to the endpoint failed to go through i.e. there was a client side error during invocation, should the failed method should be called. This is an important distinction/caveat which one should make a note of…

Further reading

Cheers!

About Abhishek

Loves Go, NoSQL DBs and messaging systems
This entry was posted in Java, Java EE and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s