users@jersey.java.net

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

From: jstrachan <james.strachan_at_gmail.com>
Date: Wed, 28 Jan 2009 06:37:41 -0800 (PST)

Craig McClanahan wrote:
>
> 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
>

I hear you Craig and totally agree with the importance of separating the
View from the Model. Where I'm less sure though is if the URI is definitely
part of the View :)

For example if we're talking about an entity bean, its primary key is
usually considered part of the model so that it can be used as a link within
an application (such as to pass the primary key around as a reference within
the UI so one entity can be referred to by another entity or chosen from
some kind of list UI or whatever). We don't hide keys and say they are not
part of the model and that they are internal to persistence. Indeed the
primary key is typically the most crucial part of any model and the main
thing required to be in the model to be able to relate entities to each
other.

If you consider resources from the consumer/client side; their URIs are
their primary keys. i.e. think of a REST service as like a
database/persistence engine where we replace 'entity bean' for 'resource'.
With resources we can typically view/add/update/delete by key (their URI)
rather like with entity beans and create links between them. So from a
client side perspective the URI is its primary key of sorts - the thing
which changes are the many views & representations of what is available at
that URI.

So I don't think its quite so clear cut. I can certainly see both sides of
the argument here - both Craig and Brett have valid points to make.

If you are implementing a model - you might not want to hardcode the URI in
the model for fear of someone exposing the model at a different URI - but
then I'd argue thats like wanting to hide the primary key from your model in
case someone takes your entity and wraps it in a different entity bean and
uses a different kind of primary key in a totally different database. i.e.
if someone wants to change the URI of a resource, its a different resource.
Within the system from which it originates; the URI of a resource should
really be just as much a constant as the primary key of an entity bean.

Or to say that another way - a URI is for life not for christmas; exposing a
URI (not a URL) on a model I think is totally valid. That URI can be
relative to many different web sites in different environments; you could
implement aliases and redirects to deal with changes to URIs. Or you could
write a resource bean to create a totally different resource with a totally
different URI. A view may choose to abstract the use of the URI from a model
to allow it to tinker with it or transform it in some way, but thats the
choice of the view.

However going back to Bretts point; being able to link to things by URI is
kinda critical to REST. If I've got a Customer resource bean and I'm
generating a PurchaseOrder representation in XML or JSON or HTML or whatever
I'm going to want to know what the URI is of the Customer model. I may not
care what the views look like or even which URL its gonna be hosted on - but
I do want to know the URI. Within a view that URI could be mangled to make a
full URL which could well be totally view specific mind you :). Now I could
hardcode the URI template of the Customer in every representation that
wishes to link to the Customer; but then its much simpler to have one
standard well defined URI for a Customer which a view can choose to reuse or
ignore isn't it?

For example - imagine implementing an Atom entity provider for a JAXB
object; you'd probably want a canonical way to extract the URI from it using
some annotation with a URI template as Brett proposes. I can think of many
times when I'd want to create a link to a resource in the model layer.

Incidentally I could imagine folks putting the actual URI into the model as
a URI/String property that can be easily modified before passing it to the
view; or generating the URI using a URI template is a nice fallback option
if the URI can be generated from the other fields available.

But ultimately I think JAX-RS could use some kind of standard way to deduce
the URI of a bean (whether a view decides to use it or not is another matter
:)




-- 
View this message in context: http://n2.nabble.com/Putting-the-RESTful-%22connectedness%22-around-my-existing-Domain-objects-tp1612424p2232569.html
Sent from the Jersey mailing list archive at Nabble.com.