I’ve been prototyping a bit lately for the REST-* effort, specifically for BPM. I rely heavily on Link headers to pass links around. RESTEasy has become pretty decent at handling links on the client side. Here’s an example of client request/responses and link following via link headers:
InputStream jpdl = Thread.currentThread().getContextClassLoader().getResourceAsStream(file);
ApacheHttpClientExecutor executor = new ApacheHttpClientExecutor();
ClientRequest request = executor.createRequest("http://localhost:8081/bpm/definitions");
request.body(mediaType, jpdl);
Link definition = request.create();
Assert.assertNotNull(definition);
ClientResponse response = null;
response = definition.request().head();
Assert.assertEquals(200, response.getStatus());
Link instanceFactory = response.getLinkHeader().getLinkByTitle("instances");
MultipartFormDataOutput form = new MultipartFormDataOutput();
form.addFormData("order", "$199.99", MediaType.APPLICATION_XML_TYPE);
response = instanceFactory.request()
.body(MediaType.MULTIPART_FORM_DATA_TYPE, form)
.post();
Assert.assertEquals(201, response.getStatus());
System.out.println(response.getLinkHeader().toString());
Link instance = response.getLocation();
Assert.assertNotNull(instance);
Link next = response.getLinkHeader().getLinkByTitle("continue");
Assert.assertNotNull(next);
Link variables = response.getLinkHeader().getLinkByTitle("variables");
Link newVariables = response.getLinkHeader().getLinkByTitle("variable-template");
response = variables.request().head();
Assert.assertEquals(200, response.getStatus());
System.out.println(response.getLinkHeader().toString());
Link order = response.getLinkHeader().getLinkByTitle("order");
String xml = order.request().getTarget(String.class);
request = newVariables.request();
response = request.pathParameter("var", "customer")
.body(MediaType.APPLICATION_XML_TYPE, "bill")
.put();
Assert.assertEquals(201, response.getStatus());
response = request.pathParameter("var", "customer")
.body(MediaType.APPLICATION_XML_TYPE, "bill burke")
.put();
Assert.assertEquals(204, response.getStatus());
response = next.request().post();
Assert.assertEquals(204, response.getStatus());
The thing about this code, it is a little hard to read. Because HTTP is being treated like a messaging API, the code is a conglomeration of simple API calls. The RESTEasy client proxy framework provides a nice way to map Java method calls to HTTP requests. It also allows you to map automatically, a response body to a Java object. Unfortunately though, this doesn’t work that well for my usecases. Because I’m relying a lot on Link headers to pass information around in REST-*, I need something that can represent an HTTP response as a whole in a nice way.
A POJO Response Mapping
So, I thought, why not define (or reuse JAX-RS annotations) to map an HTTP response to a Java POJO? It would kind of be the opposite of the RESTEasy @Form feature (where form maps an incoming request to a POJO). It could look something like this:
@ResponseMapping
@ExpectedCode(200)
public interface MyResponse {
@Body
public Customer getCustomer();
@LinkHeader
public Link getNext();
@LinkHeader("last")
public Link getLastCustomer();
@Header("ETag")
public String getHash();
}
In this example, the client code would be expecting a response code of 200 (OK), a message body converted to a Customer object, a Link header named “next”, and a HTTP response header “ETag”. Using the RESTEasy proxy framework, you could then return this as a method return value, i.e.
@Path("/customers")
public interface CustomerClient {
@Path("{id}")
@Produces("application/xml")
public MyResponse getCustomer(@PathParam("id") int custId);
}
What about errors?
For responses where the response code does not match, you could define similar mappings on an exception class.
@ResponseMapping
@ExpectedCode(404)
public class NotFoundException extends RuntimeException {}
You’d integrate it with the RESTEasy proxy framework by putting it within the throws clause.
@Path("/customers")
public interface CustomerClient {
@Path("{id}")
@Produces("application/xml")
public MyResponse getCustomer(@PathParam("id") int custId) throws NotFoundException;
}
What do you think?
Maybe I’m going a little overboard here. Maybe not? I don’t know. Let me know what you think.
Like this:
Like Loading...