Showing posts with label REST. Show all posts
Showing posts with label REST. Show all posts

Sunday, May 26, 2013

Doing JSON on CXF

Doing RESTful web services using Apache CXF can be an adventure. Unfortunately this adventure again requires some ploughing through mud. Let's look at a very simple JAX-RS resource implementing the canonical order-item example:
@Path("/orders")
public class OrderResource {

 @GET
 @Produces(MediaType.APPLICATION_JSON)
 public List<Order> getOrders() {
  List<Order> orders = new ArrayList<>();
  orders.add(new Order(123L, new Item("Foo", 2)));
  //orders.add(new Order(456L, new Item("Foo", 1), new Item("Bar", 3)));
  return orders;
 }

 public static class Order {

  private long id;
  private List<Item> items = new ArrayList<>();

  public Order(long id, Item... toAdd) {
   this.id = id;
   this.items.addAll(Arrays.asList(toAdd));
  }

  public long getId() {
   return id;
  }

  public List<Item> getItems() {
   return Collections.unmodifiableList(items);
  }
 }

 public static class Item {

  private String name;

  private int quantity;

  public Item(String name, int quantity) {
   this.name = name;
   this.quantity = quantity;
  }

  public String getName() {
   return name;
  }

  public int getQuantity() {
   return quantity;
  }
 }
}
Next step is deploying this on a JEE6 application server. I used GlassFish, which internally uses Jersey as JAX-RS implementation and Jackson as JSON provider. The resource spits out the following JSON:
[
  {
    "id":123,
    "items":[
      {
        "name":"Foo",
        "quantity":2
      }
    ]
  }
]
That's pretty much what you would expect: a list of orders which are made up of an id and a list of items. Uncommenting the second order in the Java code above confirms that the structure is consistent:
[
  {
    "id":123,
    "items":[
      {
        "name":"Foo",
        "quantity":2
      }
    ]
  },
  {
    "id":456,
    "items":[
      {
        "name":"Foo",
        "quantity":1
      },
      {
        "name":"Bar",
        "quantity":3
      }
    ]
  }
]
So far so good! Let's repeat this exercise and deploy the resource on CXF. The JSON with a single order now looks like this:
{
  "order":[
    {
      "id":123,
      "items":{
        "name":"Foo",
        "quantity":2
      }
    }
  ]
}
That's not quite what we expected: we've got a wrapping JSON map with a list of orders inside it labelled "order"! Furthermore, the list of items inside the order no longer appears to be a list! Note that there is are no square brackets surrounding it. Getting back the list of two orders makes things even more surprising:
{
  "order":[
    {
      "id":123,
      "items":{
        "name":"Foo",
        "quantity":2
      }
    },
    {
      "id":456,
      "items":[
        {
          "name":"Foo",
          "quantity":1
        },
        {
          "name":"Bar",
          "quantity":3
        }
      ]
    }
  ]
}
Wow, the list of items is back inside the order structure! Apparently you get a list if you have multiple items, and just the single item if you just have one. That's pretty bad since the JSON structure for an order is now no longer consistent, which of course makes parsing it a headache.

The reason for all of this madness is the fact that CXF is at it's core an XML framework (notice the X in CXF). Doing JSON with CXF involves a bit of trickery. Internally, CXF will first use JAXB to marshall your objects into XML. Actually, I was cheating earlier: you can't directly deploy the OrderResource class shown above on CXF. First you'll have to add JAXB annotations, getters and setters, and other such frivolities to appease JAXB:

@XmlRootElement
public class Order {

 private long id;
 private List<Item> items = new ArrayList<>();

 public long getId() {
  return id;
 }

 public void setId(long id) {
  this.id = id;
 }

 public List<Item> getItems() {
  return items;
 }

 public void setItems(List<Item> items) {
  this.items = items;
 }
}
JAXB produces XML but we want JSON! To resolve this conundrum CXF uses a StAX implementation called Jettison which does not actually write XML but instead outputs JSON. Clever! The downside here is that JSON is produced from XML, not from Java objects. Consequently, Java type information is no longer available when JSON is written out. Looking at the XML produced by JAXB for an order with a single item clarifies things:
<orders>
  <order>
    <id>123</id>
    <items>
      <name>Foo</name>
      <quantity>2</quantity>
    </items>
  </order>
</orders>
Looking at this XML, you have no way of knowing that you can have multiple <items> elements. Since Java type information is no longer available, Jettison cannot see that items is actually a java.util.List and consequently it omits the list in the JSON structure:
{
  "order":[
    {
      "id":123,
      "items":{
        "name":"Foo",
        "quantity":2
      }
    }
  ]
}
If you have an order with multiple items, multiple <items> elements will be present in the XML and as a result the JSON will contain a list.

This type of translation from XML to JSON is called the mapped convention. CXF (or rather Jettison) also supports the BadgerFish convention, but it's much more esoteric.

By default CXF uses Jettison to produce JSON. Although this might be useful if you're using JAXB for other reasons, it might be better to configure CXF to use Jackson if you're just doing JAX-RS with JSON. Luckily this is easy to do.

Tuesday, January 8, 2013

Check caching of HTTP resources

Setting up HTTP caching can be a bit of pain. Essentially the HTTP response needs to contain appropriate cache control headers, either an Expires header or a Cache-Control max-age directive. Here's a quick Java program using the HttpClient and HttpClient Cache (both part of the Apache HttpComponents) to test client-side caching of HTTP responses.
public class TestHttpCaching {

 public static void main(String[] args) throws Exception {
  CacheConfig config = new CacheConfig();
  config.setMaxObjectSize(Long.MAX_VALUE);
  HttpClient httpClient = new CachingHttpClient(config);

  doGetRequest(httpClient, args[0]);
  doGetRequest(httpClient, args[0]);
 }

 private static void doGetRequest(HttpClient httpClient, String url) throws Exception {
  HttpGet httpGet = new HttpGet(url);
  HttpContext httpContext = new BasicHttpContext();
  try {
   System.out.print("Getting... ");
   System.out.print(httpClient.execute(httpGet, httpContext).getStatusLine());
   System.out.println(": " + httpContext.getAttribute(CachingHttpClient.CACHE_RESPONSE_STATUS));
  } finally {
   httpGet.releaseConnection();
  }
 }
}
The standard DefaultHttpClient does not do any caching. Instead you have to use the CachingHttpClient which wraps a DefaultHttpClient and adds caching functionality. Also notice how the cache is configured with a very large maximum object size. This avoids resources not being cached because they are too large for the standard in-memory cache (BasicHttpCacheStorage).

Friday, January 1, 2010

REST Reservations

I'm currently reading RESTful Web Services by Leonard Richardson and Sam Ruby, and happened to come across an interesting QCon talk by Mark Nottingham on the status of HTTP. To me, this talk carries an interesting critical undertone on REST.

I've followed the whole REST and RESTful Web Services hype with some interest the last few years, and genuinely like several aspects of REST:
  • You can't go wrong with a KISS approach to software development.
  • Most everybody is familiar with how the Web works, so exploiting that familiarity makes a lot of sense.
  • The HTTP underpinnings of REST make it very well supported by a multitude of tools.
  • By leveraging HTTP, REST has excellent support for intermediaries (i.e. proxies and caches) and all the cool things they can bring to the table.

On the other hand, I also have some reservations about the whole REST thing, and Mark Nottingham touched on several of these topics in his talk:
  • As always, there is a lot of dogma to go around, and everybody has his own flavour. Is using this or that HTTP method in a particular way RESTful? Mark, who's obviously intimately familiar with HTTP, seems to have a much more pragmatic view on how you can or should use HTTP (i.e. listen to his comments on POST at the very end of the talk).
  • HTTP is deceptively simple. The spec is BIG, and full of ambiguities. The entire first part of Mark's talk deals with this, and I would argue that most people (me included) only have a superficial familiarity with HTTP.
  • Another thing I dislike about REST is that it's not generally applicable. For instance, suppose you're developing a search service, like Google (a typical text-book RESTful Web Service example). Most everybody in the REST community agrees that you should use HTTP GET for the search and encode the query in the URI: http://www.google.be/search?q=jellyfish. Imagine a similar search service that allows you to search for images that look like a given image. Logically this is completely in-line with the simple search service, but because of technical limitations of HTTP GET you're forced to implement this in a different way (WS-* is more consistently applicable this way).

For me, it's all about sane software engineering. If you have a well designed service, using established standards and conventions where relevant, people are going to be able to use your service without much problems. Things like REST and WS-* all have there place as tools in a developers toolbox, and it's up to us developers to make informed decisions on when to use what to build the best possible software we can.