Skip to main content

JAX-RS 2.0 Client API: Generic Interface

Posted by spericas on October 20, 2011 at 7:22 AM PDT

This is a follow-up to last week's blog in which we introduced the new Client API in JAX-RS 2.0. In that blog, we defined a simple AtmService and showed how to use the Client API to access its resources. For example, the following code was used to check an account's balance:

Client client = ClientFactory.newClient();

String bal = client.target("http://.../atm/balance")
                   .queryParam("card", "111122223333")
                   .queryParam("pin", "9876")
                   .request("text/plain").get(String.class);

 The Client API uses a fluent style with which a request can be built by chaining method calls. The purpose of the last method call in the chain is threefold: (i) it specifies the HTTP method (ii) indicates the Java type requested for the response and (iii) executes the request. But what if we need to build a request for execution at a later time? What if we need to separate creation from execution? Perhaps we need to store a request in a data structure, pass it to another module in the application, etc. This is the purpose of the Generic Interface: it enables requests to be first-class citizens in our applications.

Let us re-write the requests in last week's blog using the Generic Interface:

Invocation inv1 = client.target("http://.../atm/balance")
                        .queryParam("card", "111122223333")
                        .queryParam("pin", "9876")
                        .request(“text/plain”).buildGet();

Invocation inv2 = client.target("http://.../atm/withdrawal")
                        .queryParam("card", "111122223333")
                        .queryParam("pin", "9876")
                        .request("application/json").buildPost(text("50.0"));

The key difference in this example is the use of the buildXXX() methods. This family of methods returns an instance of Invocation that can be executed at a later time. Note that the decision of the Java type to be returned is also delayed, and it is now the responsability of the submitter rather than the builder.

We can now store these Invocation objects or pass them around or even execute them multiple times if needed. The following example stores the invocations created above in a collection and invokes them via a map operation:

Collection<Invocation> invs = Arrays.asList(inv1, inv2);

Collection<Response> ress =
    Collections.transform(invs,
        new F<Invocation, Response>() {
            public Response apply(Invocation inv) {
                return inv.invoke();
            }
        });

The invoke() method without any arguments returns an instance of Response. Next week we are going to dive into the topic configurations and configuration inheritance.