Skip to main content

JAX-RS 2.0 - Client API

Posted by spericas on October 14, 2011 at 6:35 AM PDT

The Early Draft for JAX-RS 2.0 (JSR 339) has recently been submitted for publication. There are number of interested features in the new draft, and I'd like to start with the new Client API. The Client API is used to access Web resources. It provides a higher-level API than HttpURLConnection, and perhaps more importantly, integration with JAX-RS providers.

Let us start by defining a simple ATM service using JAX-RS:

@Path("/atm")
public class AtmService {

    @GET
    @Path("/balance")
    @Produces("plain/text")
    public String balance(@QueryParam("card") String card,
                          @QueryParam("pin") String pin) {
        ...
    }

    @POST
    @Path("/withdrawal")
    @Consumes("plain/text")
    @Produces("application/json")
    public Money withdrawal(@QueryParam("card") String card,
                            @QueryParam("pin") String pin, Double amount) {
        ...
    }
}

 This web resource class defines a simple ATM service with two subresources to retrieve an account's balance and to withdraw money. Accessing these resources requires the client to provide a card number and a PIN. Naturally, the withdrawal subresource also requires the client to provide an amount. The actual implementation of the service isn't important for our purposes.

 The entry point to the Client API is the Client class. Using this class we can create one or more Target instances. From each Target instance we can create one or more requests. The fluency of the API somewhat blurs all these steps, but it's important to understand them in order to write better code. The following code snippet can be 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);

 In this example, the target is created from a URI and later extended by adding two query parameters. After that, a request is obtained and a resource's representation selected, namely "text/plain". Finally, the HTTP access method is specified (GET) as well as the Java type to be used for the response. Note that JAX-RS provides a built-in Message Body Reader (MBR) to convert a "text/plain" representation into a Java String, so no further configuration is required.

The following code shows how to interact with the withdrawal subresource:

Money mon = client.target("http://.../atm/withdrawal") 
                  .queryParam("card", "111122223333")
                  .queryParam("pin", "9876")
                  .request("application/json")
                  .post(text("50.0"), Money.class);

Note that the post() method has two parameters: the request entity and the Java type to be used for the response. The variant text() is a convenient way to specify an entity of type "text/plain" while at the same time providing a value for it, i.e. the string "50.0". The execution of this request will require the conversion of the Java string "50.0" into a "text/plain" representation, for which a built-in Message Body Writer or MBW is already provided by JAX-RS, as well as a MBR to convert a representation of type "application/json" into an instance of the Money class. 

Given that the Money class is not known to the JAX-RS runtime, the application must provide an MBR for it. This can be done by accessing a configuration object. Most of the Client API classes are configurable; let us show how to register this provider using our Client instance.

Client client = ClientFactory.newClient();
client.configuration().register(MoneyMBR.class);

 As we shall see in later blog entries, registration of providers is also possible on targets and requests, and perhaps even more interestingly, configurations can be inherited.