Skip to content


HATEOAS for legacy object models

One of the best aspects of RESTful orientated services is getting your resources connected, possibly even achieving HATEOAS. Making that jump with with legacy object models or data can be tricky, depending on how intrusive you want to be.

There is such a large amount of legacy data in various forms, usually with minimal links/relations that could be leveraged.

So it is a worthy challenge to try and do it in the least intrusive manner possible.

This is one approach that is reasonably decoupled from an existing legacy model.

There are three main parts to this technique:

  1. Enhance your model objects so they have a placeholder/collection so you can accumulate links
  2. Annotate your model at interesting points with link templates, around attributes or methods or classes
  3. Use a custom ContainerResponseFilter class of jersey to scan for annotations of interest, then evaluate your link templates (at run time), using the data of your models and any request based information you may require.

I chose to enhance my model classes using AspectJ at compile time.

Download the source

mvn glassfish:run
./curl_test #will hit two urls

and you should see some results, with links like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<country xmlns:ns2="http://www.w3.org/1999/xlink">
  <links>
    <link rel="" ns2:type="locator"
        ns2:href="http://localhost:8080/hateoas4legacy/rs/cities/Brisbane"/>
    <link rel=""
        ns2:type="locator" ns2:href="http://localhost:8080/hateoas4legacy/rs/cities/Sydney"/>
    <link rel=""
        ns2:type="locator" ns2:href="http://localhost:8080/hateoas4legacy/rs/cities/Melbourne"/>
    <link rel=""
        ns2:type="locator" ns2:href="http://localhost:8080/hateoas4legacy/rs/countries/AU"/>
  </links>
  <cities>
    <city>
      <links/>
      <name>Brisbane</name>
    </city>
    <city>
        <links/>
        <name>Sydney</name>
    </city>
    <city>
      <links/>
      <name>Melbourne</name>
    </city>
  </cities>
  <isoCode>AU</isoCode>
  <name>Australia</name>
</country>

All countries Results

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<countries>
<country xmlns:ns2="http://www.w3.org/1999/xlink">
  <links>
    <link rel=""
        ns2:type="locator" ns2:href="http://localhost:8080/hateoas4legacy/rs/countries/AU"/>
  </links>
  <cities/>
  <isoCode>AU</isoCode>
  <name>Australia</name>
</country>
<country xmlns:ns2="http://www.w3.org/1999/xlink">
  <links>
    <link rel=""
        ns2:type="locator" ns2:href="http://localhost:8080/hateoas4legacy/rs/countries/USA"/>
  </links>
  <cities/>
  <isoCode>USA</isoCode>
  <name>United States of America</name>
</country>
</countries>

It is very crufty, but does work in the simple cases. I haven't had a chance to demonstrate all of the features yet.

I'm likely to further flesh out this example as it is interesting in other RESTful related ways.

Post to Twitter Tweet This Post

Posted in Patterns, RESTful, Web. Tagged with , , , .

6 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. NN said

    O.k. so this is for “legacy” models. But what about say hibernate annotated models or models where I can put up my own annotation. Is there any library that achieves an automatic interconnectednes output for jersey?

  2. admin said

    Although I label “HATEOAS4legacy”, the technique isn’t just for applicable for legacy models nor jaxb annotated models.

    This is all about getting a degree of decoupling from your model objects through to your representations aka views. We all know this separation has worked well for us in the past.

    You could avoid this separation and implement it in a simpler fashion, but it you would only solve it for a specific problem or set of classes.

    There are three main parts of this technique:
    1. Enhance your model objects so they have a placeholder/collection so you can accumulate links
    2. Annotate your model at interesting members where you want links
    3. In a ContentResponseFilter, scanning for annotations of interest and inserting them into the collection

    Step 1, I used aspectj, to keep things decoupled, but you could take a simpler and more intrusive approach and add in a Links collection to your hibernate objects that isn’t persisted. To use aspectj on this over your hibernate objects, would likely lead to complications.

    In Step 2, If you are doing really simple links like parent to child etc. you probably could get away with this.

    In Step 3, You can scan any parts of your class, scanning annotations is convenient, but you could just as easily scan for particular classes or fields, but once again that is more coupling than the annotation scanning.

    In my sample code I had link generation being done by a LinkTemplateProcessor, but maybe you could have a HibernateAssociationProcessor.

    Largely it will depend on the links you need to generate and whether or not you can get enough contextual information from your hibernate annotation.
    Some links you will want to drill down, others you may want to jump back up to a parent, or even just a particular page for a particular user, like user preferences.

    More specifically when you have links that aren’t in hibernate mapping, how are you going to specify and generate them?

    Sure you can insert them directly, probably in your ContainerResponseFilterDumb, it’s just a matter of choice.

    Overall, for really simple links, I guess you could probably use Hibernate.
    But for some of the other links, it may just be easier to use the LinkTemplate annotations I’m suggesting.

    The ContainerResponseFilterDumb that I’ve implemented, is really shabby and inefficient. Time permitting, I should improve on it, probably using something like scannotations.sf.net, it may be a good fit.

    On a side note, I personally would not advocate using Hibernate for this approach in general as it takes away from the designing of your resources free (kind of) of the constraints of database implementation.

    But yes I’m pragmatic enough to understand there are times when you just need to pump stuff out.

  3. Ross McDonald said

    Hi Brett.

    This is great stuff, it is exactly what I need to do. However, my domain objects have an ID of type ‘Long’, and the ‘getResourceLinkFields’ method in the filter is falling over with an NPE. I am having trouble modifying the code to work with Longs rather than strings like in your example.

    Can you suggest how to update the code to work in my case ?

  4. admin said

    sure, send me the stack trace and I’ll take a look.

  5. Ross McDonald said

    Hi.

    OK, I added a ‘private Long id’ to Country, and ran your sample.

    I added some debug so the line numbers are different, but the error is occurring on line :

    ‘Class clazz = entity.getClass();’ in the ‘getResourceLinkFields’ method.

    Stacktrace below:

    Hit ENTER for redeploy
    Entity is : [com.example.rest.model.jaxb.Country@7f6a92, com.example.rest.model.jaxb.Country@2dbcf0]
    clazz is : class com.example.rest.model.jaxb.Country
    declared fields of : com.example.rest.model.jaxb.Country@7f6a92 are : [Ljava.lang.reflect.Field;@cf64f8
    Apr 4, 2009 3:01:27 PM org.apache.catalina.core.StandardWrapperValve log
    SEVERE: StandardWrapperValve[Jersey Web Application]: PWC1406: Servlet.service() for servlet Jersey Web Application threw exception
    java.lang.NullPointerException
    at com.example.rest.connectedness.filter.ResourceLinkResponseFilterDumb.getResourceLinkFields(ResourceLinkResponseFilterDumb.java:101)
    at com.example.rest.connectedness.filter.ResourceLinkResponseFilterDumb.addResourceLinks(ResourceLinkResponseFilterDumb.java:65)
    at com.example.rest.connectedness.filter.ResourceLinkResponseFilterDumb.getResourceLinkFields(ResourceLinkResponseFilterDumb.java:109)
    at com.example.rest.connectedness.filter.ResourceLinkResponseFilterDumb.addResourceLinks(ResourceLinkResponseFilterDumb.java:65)
    at com.example.rest.connectedness.filter.ResourceLinkResponseFilterDumb.filter(ResourceLinkResponseFilterDumb.java:46)
    at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:581)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:514)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:505)
    at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:359)

    Thanks for your help,

    Ross

Continuing the Discussion

  1. HATEOAS: URITemplateAspects, LinkAwareModels and LinkTemplateProcessors - brettdargan.com linked to this post on April 29, 2009

    [...] I have some old code, to show this working. One day I’ll get a chance to clarify terms and simplify the code [...]

Some HTML is OK

(required)

(required, but never shared)

or, reply to this post via trackback.


Twitter links powered by Tweet This v1.6.1, a WordPress plugin for Twitter.