Chapter 37. Client Framework
The RESTEasy Client Framework is the alternative to the JAX-RS server-side specification. Instead of using JAX-RS annotations to map an incoming request to your RESTful Web Service method, the client framework creates a HTTP request to invoke on a remote RESTful Web Service, which can be any web resource that accepts HTTP requests.
RESTEasy has a client proxy framework that lets you invoke upon a remote HTTP resource by using JAX-RS annotations. You can write a Java interface and use JAX-RS annotations on methods and the interface. For example:
public interface SimpleClient { @GET @Path("basic") @Produces("text/plain") String getBasic(); @PUT @Path("basic") @Consumes("text/plain") void putBasic(String body); @GET @Path("queryParam") @Produces("text/plain") String getQueryParam(@QueryParam("param")String param); @GET @Path("matrixParam") @Produces("text/plain") String getMatrixParam(@MatrixParam("param")String param); @GET @Path("uriParam/{param}") @Produces("text/plain") int getUriParam(@PathParam("param")int param); }
The RESTEasy API is simple, and based on Apache HttpClient. You generate a proxy, and invoke methods on the proxy. The invoked method is then translated to a HTTP request (based on the method's annotations) and posted to the server. To set it up:
import org.resteasy.plugins.client.httpclient.ProxyFactory; ... // this initialization only needs to be done once per VM RegisterBuiltin.register(ResteasyProviderFactory.getInstance()); SimpleClient client = ProxyFactory.create(SimpleClient.class, "http://localhost:8081"); client.putBasic("hello world");
See the
ProxyFactory
Java Documentation for more options. For instance, you may want to fine tune the HttpClient
configuration.
@CookieParam
creates a cookie header to send to the server. If you allocate your own javax.ws.rs.core.Cookie
object and pass it as a parameter to a client proxy method, you do not require @CookieParam
— the client framework understands that you are passing a cookie to the server, so no extra metadata is required.
The client framework can use the same providers available on the server. You must manually register them through the
ResteasyProviderFactory
singleton using the addMessageBodyReader()
and addMessageBodyWriter()
methods.
ResteasyProviderFactory.getInstance().addMessageBodyReader(MyReader.class);
37.1. Abstract Responses
When you need to access the response code or the response headers of a client request, the
Client-Proxy
framework provides two options:
You can return a
javax.ws.rs.core.Response.Status
enumeration from your method calls, like so:
@Path("/") public interface MyProxy { @POST Response.Status updateSite(MyPojo pojo); }
After invoking on the server, the client proxy internally converts the HTTP response code into a
Response.Status
enumeration.
You can retrieve all data associated with a request with the
org.resteasy.spi.ClientResponse
interface:
/** * Response extension for the RESTEasy client framework. Use this, or Response * in your client proxy interface method return type declarations if you want * access to the response entity as well as status and header information. * * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ public abstract class ClientResponse<T> extends Response { /** * This method returns the same exact map as Response.getMetadata() except as a map of strings * rather than objects. * * @return */ public abstract MultivaluedMap<String, String> getHeaders(); public abstract Response.Status getResponseStatus(); /** * Unmarshal the target entity from the response OutputStream. You must have type information * set via <T> otherwise, this will not work. * <p/> * This method actually does the reading on the OutputStream. It will only do the read once. * Afterwards, it will cache the result and return the cached result. * * @return */ public abstract T getEntity(); /** * Extract the response body with the provided type information * <p/> * This method actually does the reading on the OutputStream. It will only do the read once. * Afterwards, it will cache the result and return the cached result. * * @param type * @param genericType * @param <T2> * @return */ public abstract <T2> T2 getEntity(Class<T2> type, Type genericType); /** * Extract the response body with the provided type information. GenericType is a trick used to * pass in generic type information to the resteasy runtime. * <p/> * For example: * <pre> * List<String> list = response.getEntity(new GenericType<List<String>() {}); * <p/> * <p/> * This method actually does the reading on the OutputStream. It will only do the read once. Afterwards, it will * cache the result and return the cached result. * * @param type * @param <T2> * @return */ public abstract <T2> T2 getEntity(GenericType<T2> type); }
All
getEntity()
methods are deferred until you invoke them. In other words, the response OutputStream
is not read until you call one of these methods. The getEntity()
method with no parameters can only be used if you have templated the ClientResponse
within your method declaration. RESTEasy uses this generic type information to determine which media type the OutputStream
is unmarshaled into. The getEntity()
methods that take parameters let you specify the Object type the response should be marshaled into. This lets you dynamically extract the desired types at runtime. For example:
@Path("/") public interface LibraryService { @GET @Produces("application/xml") ClientResponse<LibraryPojo> getAllBooks(); }
Include the
LibraryPojo
in ClientResponse
's generic declaration so that the client proxy framework can unmarshal the HTTP response body.