Mapping response on client side

8 Comments

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.

RESTEasy 1.2.GA Released

Leave a comment

After a few months RESTEasy 1.2.GA is finally ready.  This is mostly a cleanup, bug fix, and refactoring release, but here are some features of note:

I’d also like to thank Attila Kiraly for fixing some bugs that cropped up in Multipart and XOP support.  Pascal de Kloe, a new committer, also helped refactor content negotiation to support charset variants.  Solomon Duskis continues to be a seasoned veteran and helps with bugs and features here and there.

What’s next?  I’d like to focus next on getting complete OAuth support in.  I think it will help out our REST-* efforts as we look to secure the services we’re defining there.  RESTEasy is also going to expand beyond a simple JAX-RS implementation.  As JBoss projects like HornetQ, jBPM, Drools, Transactions, and Infinispan obtain RESTful interfaces, I’ll be creating a REST profile under the RESTEasy umbrella.

Useful links:

Parsing Link headers with Javascript and Java

Leave a comment

I wrote a Link header parser in Javascript. Thanks Mark Nottingham for sending me a link to his python equivalent.

var linkexp=/<[^>]*>\s*(\s*;\s*[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*")))*(,|$)/g;
var paramexp=/[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*"))/g;

function unquote(value)
{
    if (value.charAt(0) == '"' && value.charAt(value.length - 1) == '"') return value.substring(1, value.length - 1);
    return value;
}

function parseLinkHeader(value)
{
   var matches = value.match(linkexp);
   var rels = new Object();
   var titles = new Object();
   for (i = 0; i < matches.length; i++)
   {
      var split = matches[i].split('>');
      var href = split[0].substring(1);
      var ps = split[1];
      var link = new Object();
      link.href = href;
      var s = ps.match(paramexp);
      for (j = 0; j < s.length; j++)
      {
         var p = s[j];
         var paramsplit = p.split('=');
         var name = paramsplit[0];
         link[name] = unquote(paramsplit[1]);
      }

      if (link.rel != undefined)
      {
         rels[link.rel] = link;
      }
      if (link.title != undefined)
      {
         titles[link.title] = link;
      }
   }
   var linkheader = new Object();
   linkheader.rels = rels;
   linkheader.titles = titles;
   return linkheader;
}

I had written a non-regular-expression, hand-coded parser in Java for RESTEasy. Thought it might be faster than regexp. That could probably be ported to Javascript too.

Oracle and Java

5 Comments

Let’s face it.  We all knew Sun would get acquired.  The question was just when and by whom.  Now that its happened, and Oracle is the winner, I must say I like the situation better than if IBM had bought them.  Just from a EE specification perspective, IBM was always a bit reactionary when it came to anything new.  On EE 5, Oracle was a staunch ally in our push to get JPA through and a part of EE 5 (and thus retiring CMP and making JDO obsolete).  On EE 6, we’ve met a bit of resistence from them when it comes to the-spec-formally-known-as-Web-Beans, but they’ve been at least open to the idea.  Who knows how the Oracle business guys will want to deal with Java.  That is the wildcard IMO.  Should be fun… Interesting times.

OSGi doesn’t need to be in your face

8 Comments

Awhile back on TSS, I complained about OSGi possibly bleeding too much into applications.  That OSGi did not belong within application code.  This IBM article is a prime example of my point.  Here we have standard Spring code and XML.  Nowhere in there do you have OSGi APIs.  Yet, you get OSGi classloading and lifecycle.  I bet, that if you can’t already right now, Spring will eventually be able to register IoC references as direct dependencies within an OSGi kernel.  There’s really no reason or advantage to have OSGi APIs bleed into application code.

Another example is JBoss 5.  While not running on top of an OSGi kernel (instead its running on JBoss’s own special kernel) it can manage dependency and lifecycle for a variety of different component models like:  Spring, JBoss MC, EJBs, JCA adapters, Queues, Topics, Servlets, etc.  This can work because JBoss’s kernel provides a deployment abstraction and framework to publish and manage inter-component dependencies.  The end goal here is that all these different component models can take advantage of all this dependency management without writing to specific APIs and to stay within the confines of their own component model’s specification.  The metadata is there.  The dependency information is there.  There’s no reason to write to a specific deployment API to leverage these types of services.

This is what I had hoped OSGi would become.  A standard kernel where classloading, deployment, and dependency management would be provided as low level services so that existing and new component models would have a standard way to mix and match themselves and to deploy themselves.  In theory, such a kernel would allow JBoss’s Transaction Manager to run within a Web Logic server.  JBoss’s EJB container to run within Websphere.  Sun’s WS-* implementation to run within JBoss.

Maybe my concern is not real.  Maybe most OSGi evangelists are not positioning OSGi as an application API.  Let’s hope they aren’t.

RESTEasy 1.0.0.GA Released!

2 Comments

See more info on blogs.jboss.com.

RESTEasy 1.0-RC1 Released: Need help finalizing GA!

Leave a comment

RESTEasy 1.0-RC1 has just been released.  Please see our main project page and follow links to documentation and downloads.  Not much new functionality in this release.  After 77 days of waiting for Sun to allow us to download the JAX-RS TCK, I was able to pass all TCK tests after a fixing a few minor bugs.  1.0-RC1 reflects these changes.

Onward to 1.0 GA!

After a 2 week incubation of RC1, 1.0 GA will be released (sometime January 21st) and will be fully certified with Sun as a JAX-RS implementation.  I need your help finalizing the GA release! Specifically

  • I need existing RESTEasy users to upgrade and test their applications.
  • I need pointers on improving usability
  • Documentation improvements
  • Bugs reports and patches are always welcome too!

Since the window for the GA is only 2 weeks, there’s not a lot of features I can accomodate in the usability department, but I will try.

Mini-conference in Nice, France: RESTEasy/JAX-RS talk there

Leave a comment

Stéphane Epardaud, one of RESTEasy’s active users, is having a mini-conference/JUG in Nice, France on January 14th.  One of the talks is on RESTEasy and JAX-RS.  See his wiki for more details.

Wrong Documentation in RESTEasy beta-9. Redownload please.

Leave a comment

I accidentally bundled the old beta-7 documentation with the binary release of 1.0-beta-9.  Please re-download to get updated documentation.

Apologies!

RESTEasy Beta 9 Released

5 Comments

Follow documentation and download links from our main project page.  View our full release notes from JIRA.  Special thanks goes out to Solomon Duskis for helping me nail down the Spring integration and for implementing the Spring MVC bridge.  This is a pretty extensive release with a lot of new features:

  • Asynchronous HTTP (Comet) support via Tomcat 6, JBoss Web, or Servlet 3.0 (Jetty 7).  This is a tiny, but simple abstraction over these APIs
  • Expanded Multipart support.  You can now marshal collections(Maps and Lists) of objects to and from multipart/mixed and multipart/form-data.
  • Atom + JAXB support.  I wrote a few JAXB annotated classes to represent the Atom format.  You can embed JAXB classes within Atom content as well as marshall Atom to and from XML, JSON, and Fastinfoset.
  • Atom support via Apache Abdera
  • Arrays and Collections of JAXB objects marshalled automatically.
  • Enhanced Spring support.  We now support autoproxied beans as well as some nice Spring MVC support and the ability to output ModelAndView objects.  Thanks Solomon!!!

This will hopefully be the last beta.  I’ve been waiting patiently (since October 1st) for Red Hat and Sun to hammer out the licensing terms so I can download the JAX-RS TCK and get RESTEasy certified.  I should be getting it any day now and so the next release of RESTEasy will be RC1 and certified.  I’m not sure how long the TCK will take as I don’t have my hands on it yet, but I’m hoping around 6-8 weeks.  Shortly after RC1 will be 1.0.

Older Entries Newer Entries