users@jersey.java.net

Re: [Jersey] Putting the RESTful "connectedness" around my existing Domain objects

From: Craig McClanahan <Craig.McClanahan_at_Sun.COM>
Date: Wed, 03 Dec 2008 23:08:22 -0800

Brett Dargan wrote:
>
> One of the great things about jersey is that it gives us a simple way
> to take our existing JAXB annotated objects and expose them as resources.
>
> So at my company we are progressing with creating more RESTful Web
> Services, I would like a standard way to facilitate adding the
> "connectedness" aspect of existing JAXB annotated objects.
>
> Obviously right now, we don't have domain objects that are linked in
> that way.
>
> Has anyone come across this problem?
>
>
> Maybe we could create some Annotation, add to our existing JAXB
> annotated objects that use the same URITemplate style specified in the
> jsr311.
>
> Then by using a ContainerResponseFilter, traverse the entity object
> and add links to those objects prior to unmarshalling.
>
> I've got some running code that does this, nothing worth posting yet.
>
> But there are a few issues, I'm not sure if I should continue down
> this path, or if someone has a better idea.
>
> So something like this:
>
> @XmlRootElement
>
> class Book {
>
> private String id;
>
> @ResourceLink("/authors/{author}")
>
> private String author;
>
> @ResourceLinkContainer
>
> private List<ResourceLinkItem> resourceLinks = new
> ArrayList<ResourceLinkItem>();
>
> ...
>
> Then in the ContainerResponseFilter
> grab the entity out of the contentResponse, scan it for ResourceLinks,
> add ResourceLinkItems with hrefs that have been evaluated, from some
> context.
>
> Since we support multiple representations, the ResourceLinkItem, would
> need to handle links like standard html as well as xlink.
>
> The other potential niceness, is being able to preserve the suffix in
> links from the requested resource.
>
> Eg. Requested specific representations, such as /books/1231.xml should
> be able to return an xml representation with the same suffix, like
> /authors/bob.xml.
>
> Or /books/1231.es.html should contain a link to /authors/bob.es.html
>
> Using a filter, would enable me to easily slightly tailor the links to
> other resources, without affecting the original domain object.
>
> Sure it raises questions like what is a suffix, but there are
> conventions we could follow around that.
>
> cheers,
>
> Brett.
>
>
While the approach you suggest is, at least in theory, technically
feasible, I would actually suggest that it is really not the direction
you should take. What you are proposing to do is embed information
about the "view" (which is what the return value from a JAX-RS resource
class's method is all about) inside the "model" classes that represent
your data. This implies that you will never ever use the model classes
in any other scenario -- and, in my experience, that is actually pretty
rare. Or, at least, it *should* be rare.

The approach I would suggest is along the following lines:

* Keep your model classes purely model related, using whatever persistence
  technology you prefer. They should *not* have anything related to how
they
  might be represented.

* Using whatever persistence API you like (I like JPA, others prefer
things like
  Hibernate), make your model objects available to business logic,
independent
  of whether you might be in a webapp, inside a Swing based app, or
(tomorrow :-)
  referenced by a JavaFX app.

* For a typical server side webapp presenting the data in HTML, use your
favorite
  web application framework (JSF, Struts, whatever).

* To present the data as a RESTful web service (presumably with CRUD
  interface), write a *separate* JAX-RS resource class that performs
this task.

With this approach, you can use your model classes in every environment
they make sense, not just as web service classes. And the resource
classes you have to write will already (thanks to JAX-RS) deal with the
issue of multiple representations. Consider a CRUD environment where
you have to support a GET request that returns a representation of a
customer, in either XML or JSON depending on what the
client wants. Assume also that you're using JAXB annotations, as you
describe. Consider what the method that handles this request might look
like:

    @GET
    @Path("{id}")
    @Produces({"application/xml","application/json"})
    public Response findCustomer(@PathParam("id") String id) {
        Customer cust = MyBusinessLogic.findCustomer(id); // Use
JPA/Hibernate/whatever
        if (cust == null) { // Assume null is returned if there is no
such customer
            return Response.status(404);
        } else {
            return Response.ok(cust);
        }
    }

With a class like this *separate* from your model classes, you get some
benefits:

* JAX-RS deals with either JSON or XML output for you, since you used
JAXB annotations.

* Your model classes don't have to change if you change your mind on
what URI patterns
  should be supported -- or if you want to use more than one URI pattern
(in the same app
  or in different apps) to retrieve this information.

* Your model classes don't have to know how to handle error conditions like
  "the customer id you gave me was not valid".

* Your model classes can be unit tested completely separate from any
assumptions about
  how the data they contain will be transmitted to end users.

* Your view class (in this case a RESTful web service) is free to define
whatever URI scheme is
  appropriate, or even provide more than one, and change them later,
without any
  impact on the model classes.

* Your model class is free to change the set of properties represented,
without impacting
  the view classes.

IMHO, you are *much* better off respecting Model-View-Controller (MVC)
principles, and separating the issues into different concerns. I can
tell you, from ~30 years of professional experience (including attempts
to do things similar to what you describe, and then kicking myself when
I couldn't do what was really needed because of the view assumptions
built in to my model classes) that you will *really* benefit from
following the separation practices.

Craig McClanahan