As there is no client API defined in JAX-RS 1.1, both RESTEasy and CXF provide a product-specific API to simplify REST calls. For RESTEasy, the class is ClientRequest for requests and the JAX-RS Response subclass ClientResponse for responses; CXF has WebClient and does not presently subclass Resource. Not covered here, but both also provide proprietary client proxies (CXF) (RESTEasy), which reuse a root resource's Java interface to construct REST client calls. If client-side REST implementation independence is desired, the more verbose java.net.HttpURLConnection and Apache HttpClient are options.
I'm showing below some of the syntactical differences between ClientRequest and WebClient, helpful for converting a client from one JAX-RS implementation to the other. These differences are rather minor as I learned when converting the RESTful Java book examples 9-1 and 9-2 from RESTEasy to CXF. The CustomerResourceTest
class in Example 9-1, for example, just needs these changes:
package com.restfully.shop.test; import com.restfully.shop.domain.Customers; // import org.jboss.resteasy.client.ClientRequest; // RESTEasy import org.apache.cxf.jaxrs.client.WebClient; // CXF import org.junit.Test; /** * @author Bill Burke * @version $Revision: 1 $ */ public class CustomerResourceTest { @Test public void testQueryCustomers() throws Exception { String url = "http://localhost:9095/customers"; while (url != null) { // ClientRequest request = new ClientRequest(url); // RESTEasy WebClient wc = WebClient.create(url); // CXF // String output = request.getTarget(String.class); // RESTEasy String output = wc.get(String.class); // CXF System.out.println("** XML from " + url); System.out.println(output); // Customers customers = request.getTarget(Customers.class); // RESTEasy Customers customers = wc.get(Customers.class); // CXF url = customers.getNext(); } } }
The OrderResourceText class in Example 9-2 is lengthier and shows a few more differences between RESTEasy and CXF:
RESTEasy's Response subclass provides a getHeaders() convenience method that returns a Map of Strings instead of Objects, while CXF JAX-RS relies on the standard Response
RESTEasy:getMetadata()
that requires subsequent casting of the Objects to Strings:List<String> linkHeaders = (List<String>) response.getHeaders().get("Link");
CXF JAX-RS:List<Object> linkHeaders = response.getMetadata().get("Link");
Setting the body for a POST call and making the calls consists of two steps with RESTEasy, one with CXF JAX-RS. CXF also does not need the body type ("application/xml") as it apparently relies on the JAXB
RESTEasy:XmlRootElement
annotation on the Customer object:Customer customer = new Customer(); ...populate Customer... request = new ClientRequest(customers.getHref()); request.body("application/xml", customer); response = request.post(); Assert.assertEquals(201, response.getStatus());
CXF JAX-RS:Customer customer = new Customer(); ...populate Customer... wc = WebClient.create(customers.getHref()); response = wc.post(customer); Assert.assertEquals(201, response.getStatus());
For GETs, RESTEasy splits out the call from the retrieval of the result, while they are combined in the same step with CXF JAX-RS:
RESTEasy:System.out.println("** New list of orders"); request = new ClientRequest(orders.getHref()); response = request.get(); System.out.println(response.getEntity(String.class));
CXF JAX-RS:System.out.println("** New list of orders"); wc = WebClient.create(orders.getHref()); System.out.println(wc.get(String.class));