JBoss.orgCommunity Documentation
The two principal client side classes in Resteasy 2 are ClientRequest
and ClientResponse:
ClientRequest request = new ClientRequest("http://localhost:8081/test");
request.body("text/plain", "hello world");
ClientResponse<?> response = request.post();
String result = response.getEntity(String.class);
ClientRequest holds the target URL and entity, if any.
ClientResponse holds the response entity, which can be
extracted by the getEntity() method.
In JAX-RS 2.0, these classes are replaced by four classes that support a fluent
call pattern: Client, WebTarget,
Invocation.Builder, and Response:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8081/test");
Invocation.Builder builder = target.request();
Entity<String> entity = Entity.entity("hello world", "text/plain");
Response response = builder.post(entity);
String result = response.readEntity(String.class);
The invocation process begins with Client, whose primary
responsibility is to create a WebTarget. Clients
are somewhat expensive to build, so it often makes sense to reuse a Client
to create multiple WebTargets.
Resteasy extends ClientBuilder and Client
with methods that allow the registration of providers:
static class TestWriter implements MessageBodyWriter<String>
{
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
{
return false;
}
@Override
public long getSize(String t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
{
return 0;
}
@Override
public void writeTo(String t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException
{
//
}
}
ResteasyClientBuilder clientBuilder = new ResteasyClientBuilder();
Client client = clientBuilder.register(TestWriter.class).build();
All Clients created by that ResteasyClientBuilder,
and all invocations on all WebTargets created by those Clients,
will have TestWriter available.
WebTarget, as its name implies, constructs and holds a URL which
targets a server side resource. It has various options for extending and manipulating
URIs:
WebTarget target = client.target("http://localhost:8081/test/{index}");
WebTarget target1 = target.resolveTemplate("index", "1");
WebTarget target2 = target.resolveTemplate("index", "2");
Here, two new WebTargets are created from the original target, each
with a different ending path segment. Query and matrix parameters can also be appended:
WebTarget target3 = target2.queryParam("x", "y");
Here, target3 targets "http://localhost:8081/test/2?x=y".
Resteasy also extends WebTarget with the ability to register providers:
Client client = ClientBuilder.newClient();
String url = "http://localhost:8081/test/{index}";
WebTarget target = client.target(url).register(TestWriter.class);
WebTarget target1 = target.resolveTemplate("index", "1");
WebTarget target2 = target.resolveTemplate("index", "2");
WebTarget target3 = target2.queryParam("x", "y");
Here, TestWriter is available to all invocations on target1, target2, and target3.
Invocation.Builder plays a role similar to the old ClientRequest:
Response response = builder.header("User-Agent", "Mozilla/5.0").get();
or
String s = builder.header("User-Agent", "Mozilla/5.0").get(String.class);
Finally, note that Response, unlike the old ClientResponse<T>,
is not a generic type, so it is necessary to give a type when extracting a response entity:
String result = response.readEntity(String.class);
Note. Response.getEntity() still exists, but it
plays a different role, which could easily lead to bugs. It is necessary to call
readEntity() to extract the response entity. If
getEntity() is called instead, it will return null.
Note. Unlike the old getEntity(),
readEntity() is not idempotent. Once it is called, the response is
closed, and subsequent calls will throw an IllegalStateException. This
behavior can be circumvented by calling Response.bufferEntity() before
calling readEntity(). I.e., this will work:
response.bufferEntity(); System.out.println(response.readEntity(String.class)); System.out.println(response.readEntity(String.class));
The client framework in Resteasy 2 included a facility for interacting with JAX-RS resources through client side POJOs, not unlike the Java RMI facility:
@Path("/test")
public static interface TestResource
{
@GET
@Produces("text/plain")
public String test();
}
public void testProxy() throws Exception
{
String url = "http://localhost:8081";
TestResource pojo = ProxyFactory.create(TestResource.class, url);
String result = pojo.test();
This technique avoids a lot of complications, but, perhaps because it is perceived as
not being in the RESTful spirit, it is not part of the JAX-RS 2.0 client framework. It
still exists in Resteasy 3, but in a re-worked form that fits into the official client
framework. Now, a proxy is created by a call to ResteasyWebTarget.proxy():
Client client = ClientBuilder.newClient(); String url = "http://localhost:8081"; ResteasyWebTarget target = (ResteasyWebTarget) client.target(url); TestResource pojo = target.proxy(TestResource.class); String result = pojo.test();
Resteasy 2 had two facilities for handling errors on the client side.
An instance of an org.jboss.resteasy.client.core.ClientErrorInterceptor
could be registered to handle exceptions thrown during a proxied call. Also, an instance of an
org.jboss.resteasy.client.exception.mapper.ClientExceptionMapper could
be registered to map exceptions thrown during a proxied call. A default
ClientExceptionMapper was installed that mapped exceptions thrown by
the HttpClient transport layer to Resteasy specific analogs. For example, an
org.apache.http.client.ClientProtocolException would be mapped to an
org.jboss.resteasy.client.exception.ResteasyClientProtocolException.
These two facilities do not exist in Resteasy 3. Instead, the JAX-RS 2.0 specification mandates the
use of javax.ws.rs.ProcessingException and
javax.ws.rs.client.ResponseProcessingException. In particular, exceptions
thrown while processing a request should be mapped to a ProcessingException,
and exceptions thrown while processing a response should be mapped to a
ResponseProcessingException.
For example, the ProcessingException javadoc lists possible conditions
leading to a ProcessingException:
java.io.IOExceptions thrown by
javax.ws.rs.ext.MessageBodyReaders and
javax.ws.rs.ext.MessageBodyWriters
during entity serialization and de-serialization
Note that ProcessingException and ResponseProcessingException
represent internal problems. If the client side receives a response with status codes 3xx, 4xx or 5xx,
it will map the response to an instance of javax.ws.rs.WebApplicationException
or one of its subclasses.