Want to rewrite JAXB?

4 Comments

I’ve been pretty frustrated with Sun’s JAXB and JBoss’s JBossXB implementations.  Here’s why:

* JAXB is not very extendable.  For example, I’d love to be able to define custom annotations that trigger callback APIs so that I can read/write custom properties from an XML document.  For example, ever try to do Map support?  I’ve also wanted to convert URLs to/from Link objects in resteasy with hooks into RESTEasy runtime for automatic URL writing.  I just couldn’t find a way to do it within resteasy.

* JAXB (both Sun’s and JBoss’s) is very slow to initialize.  Yeah, they parse very fast, but initialization speed is very slow because it has to do a lot of reflection on the classes that make up the JAXBContext and build a lot of data structures to do quick parsing.  You might be saying, “Big Deal!”.  But what I’ve found with JAXB and JBoss’s impl is that for the meta-models we’re trying to parse in JBoss AS, it can take as much as 1 second to initialize all the models.  That’s 1 second more added to boot time.  1 second to make embedded/unit testing slower.

So, this is why I started the fast-jaxb project under the RESTEasy SVN.  The way it works is that you provide it a set of classes and it generates Java code that uses SAX to parse XML.  The problem is, I haven’t had the time finish it, which is why I’m blogging to ask if anybody is interested in taking it over (or suggesting a good alternative for solving the goals/problems mentioned above).  Fast-JAXB has basically no initialization overhead compared to Sun’s JAXB implimentation.  I haven’t worked on the extension points though, nor do I support all the JAXB annotations.  I have also only worked on parsing XML, not generating XML.  Anybody interested in taking it over and driving it?  Give me a ping here on this blog…

Webinar on REST, RESTEasy, JBoss – March 23rd

Leave a comment

I’m doing a webinar tomorrow on REST, JAX-RS, RESTEasy, and REST-*.  I only have 40 minutes, so it will be a brief overview of all those subjects and how they fit into our EAP product.  I’ll be giving it twice:

9am – EST

2pm – EST

For more information, click here

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.

Speaking at new Boston JBoss User Group 2/9

Leave a comment

Jesper Pederson has created a Boston JBoss User Group.  Our first meeting is next Tuesday, February 9th.  I’m the first speaker and will be giving an intro to REST, JAX-RS, and, if I have time, some of the stuff that we’re doing at REST-* (rest-star.org).  Please click here for more details.

Thanks James and Cheyenne

1 Comment

I just want to publicly thanks James Cobb and Cheyenne Weaver for putting together the branding, skin, look and feel for REST-*.org.  The site is nice to look at and gives a professional look and feel that I could never in my wildest dreams created by myself.  I hope to tap their expertise and artistic ability to perform similar branding efforts for RESTEasy.

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.2.GA Released

Leave a comment

More bugs found by our users. See our release notes on 1.0.2.GA for more details.

Next release will be 1.1-RC1 beginning of March which will introduce client and server side interceptors. Client side “Browser” caching. Server side cache support. GZIP encoding support

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.

JAX-RS Atom Support with Resteasy

3 Comments

I have implemented Atom support which will be available with the next release of RESTEasy.

Although the Atom format is used primarily for the syndication of blogs and news, many are starting to use this format as the envelope for Web Services, for example, distributed notifications, job queues, or simply a nice format for sending or receiving data in bulk from a service.

RESTEasy has defined a simple object model in Java to represent Atom and uses JAXB to marshal and unmarshal it. The main classes are in the org.jboss.resteasy.plugins.providers.atom package and are Feed, Entry, Content, and Link. If you look at the source, you’d see that these are annotated with JAXB annotations. The distribution contains the javadocs for this project and are a must to learn the model. Here is a simple example of sending an atom feed using the Resteasy API.

import org.jboss.resteasy.plugins.providers.atom.Content;
import org.jboss.resteasy.plugins.providers.atom.Feed;
import org.jboss.resteasy.plugins.providers.atom.Link;
import org.jboss.resteasy.plugins.providers.atom.Person;

@Path("atom")
public class MyAtomService
{

   @GET
   @Path("feed")
   @Produces("application/atom+xml")
   public Feed getFeed()
   {
      Feed feed = new Feed();
      feed.setId(new URI("http://example.com/42"));
      feed.setTitle("My Feed");
      feed.setUpdated(new Date());
      Link link = new Link();
      link.setHref(new URI("http://localhost"));
      link.setRel("edit");
      feed.getLinks().add(link);
      feed.getAuthors().add(new Person("Bill Burke"));
      Entry entry = new Entry();
      entry.setTitle("Hello World");
      Content content = new Content();
      content.setType(MediaType.TEXT_HTML_TYPE);
      content.setText("Nothing much");
      feed.getEntries().add(content);
      return feed;
   }
}

Because Resteasy’s atom provider is JAXB based, you are not limited to sending atom objects using XML. You can automatically re-use all the other JAXB providers that Resteasy has like JSON and fastinfoset. All you have to do is have “atom+” in front of the main subtype. i.e. @Produces(“application/atom+json”) or @Consumes(“application/atom+fastinfoset”)

Using JAXB with the Atom Provider

The org.jboss.resteasy.plugins.providers.atom.Content class allows you to unmarshal and marshal JAXB annotated objects that are the body of the content. Here’s an example of sending an Entry with a Customer object attached as the body of the entry’s content.

@XmlRootElement(namespace = "http://jboss.org/Customer")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer
{
   @XmlElement
   private String name;

   public Customer()
   {
   }

   public Customer(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return name;
   }
}

@Path("atom")
public static class AtomServer
{
   @GET
   @Path("entry")
   @Produces("application/atom+xml")
   public Entry getEntry()
   {
      Entry entry = new Entry();
      entry.setTitle("Hello World");
      Content content = new Content();
      content.setJAXBObject(new Customer("bill"));
      entry.setContent(content);
      return entry;
   }
}

The Content.setJAXBObject() method is used to tell the content object you are sending back a Java JAXB object and want it marshalled appropriately. If you are using a different base format other than XML, i.e. “application/atom+json”, this attached JAXB object will be marshalled into that same format.

If you have an atom document as your input, you can also extract JAXB objects from Content using the Content.getJAXBObject(Class clazz) method. Here is an example of an input atom document and extracting a Customer object from the content.

@Path("atom")
public class AtomServer
{
   @PUT
   @Path("entry")
   @Produces("application/atom+xml")
   public void putCustomer(Entry entry)
   {
      Content content = entry.getContent();
      Customer cust = content.getJAXBObject(Customer.class);
   }
}

Older Entries Newer Entries

%d bloggers like this: