Enterprise Integration Zone is brought to you in partnership with:

I'm Solomon Duskis, NYC consultant and a Java/J2EE guy. I work at Sungard Consulting Services in NYC. The postings on this site are my own and do not necessarily represent the positions, strategies or opinions of my employer. Solomon is a DZone MVB and is not an employee of DZone and has posted 22 posts at DZone. You can read more from them at their website. View Full User Profile

Declarative Hyperlinking in RESTEasy

06.23.2009
| 8530 views |
  • submit to reddit

REST APIs must be hypertext driven. That requirement has quite a few implications on how one defines a RESTful interface which in turn has quite a few implications on the kinds of tools one needs to develop RESTful APIs. As a Java developer, I'm interested in how the well recieved JAX-RS specification can handle these implications. However, before I explore JAX-RS, let's take a look at some of the hypertext-driven implications.

What's a Resource?

A resource is a thing

I'll start with a question: "What's a resource?" Well, if you know anything about REST, you know that resources are "something that's represented by a URL." A resource is a unique thing that has its own URL.

I'm sure that at this point you're thinking: "A resource is a thing with a URL. Thank you very much, Solomon. That explains everything." in a sarcastic way. A resource is a thing. That's really a great explanation.

Second Stab - the domain

Okay, fine. I'll take another stab at defining a resource. "A resource can be a representation of a domain object." For example, "GET http://mymusic.com/artist/1249938" can produce data related to MC Hammer (remember him?). That URL encapsulates MC Hammer-ness in "My Music Company, Inc."

That's better, but definitely not the complete picture, so to speak. A resource can also be a picture of MC Hammer, a spread-sheet of MC Hammer's assets, an HTML page detailing his history, a Java Script file representing some logic and presentation code, search functionality and a host of other good "things".

Back to "thing"?

Oh well, I guess I'm unfortunately back to defining a resource as a thing. However, notice that I snuck in the "search functionality" as a resource. Search functionality is one of those resources that I as a back-end developer care about quite a bit.

A resource can really be any "thing," but from my perspective as a back-end developer of RESTful APIs, I really care bout domain data "things" the most and other API functionality a close second.

Now that I've defined what types of "things" I care about (domain data and other API functionality), what's unique to REST about how those "things" are implemented? Hypermedia, of course. All interaction between a client and a server must be definied by hyperlink information specified by the server. All navigatable relationships between the set of exposed domain objects and API functionality must be presented by the server as a URL (or some other globally understood identifier). The exception to that rule is the entry point into the system, a.k.a. the bookmark

Hammer it out

Let's get back to the MC Hammer example. The user performs the following steps in order to "GET" to the MC Hammer resource:

  1. GET http://mymusic.com/
  2. GET <the most popular artist list url>
  3. GET <the url to the first artist>

That three step process "got" me to my MC Hammer destination. I had a bookmark in step #1 which gave me URI/API/workflow access to the most popular functionality which I invoked in step #2. Step #2 produced a result that I was looking for, so I traversed that URL to my final destination.

Server-side perspective: What's a Resource?

We now know how to find Mr. Hammer from a client's perspective. How is this three step process implemented? Well, if we're using JAX-RS we have three "Resources:"

  1. A homepage controller. This can be a simple controller or even an XML file. It's an entry point into the system. Onve the links on the home page data refers to the Most Popular URL.

    public class HomepageController{
    @GET
    @Produces({"application/xml", "application/json"})
    @Path("/")
    public Homepage getHomepage(){
    // homepage retrieval magic goes here
    }
    }
  2. An Artists controller which has a few methods that return different lists of Artists. Somehow it has to convert an Artist into a name, a bio and a URL.

    public class ArtistsController{

    private ArtistService service;

    @GET
    @Produces({"application/xml", "application/json"})
    @Path("/most-popular-artist")
    public Artists getMostPopular(){
    // do something with ArtistService
    // add on URL to each Artist object in the list
    // we'll fill this in later
    }
    }
  3. An Artist Controller which knows how to GET, POST, PUT, DELETE a marshallable Artist domain object. There's nothing new here, You've probably seen this quite a few times in other JAX-RS blogs/articles/books.

    public class ArtistController{

    private ArtistService service;

    @GET
    @Produces({"application/xml", "application/json"})
    @Path("/artist/{id: \\d+}")
    public Artist getArtist(@PathParam("id") Integer id){
    return service.getById(id);
    }

    @PUT
    @Consumes({"application/xml", "application/json"})
    @Path("/artist/{id: \\d+}")
    public void updateArtist(@PathParam("id") Integer id, Artist artist){
    service.save(artist);
    }

    ...
    }

The most interesting aspect of this article is how the URL conversion in the getMostPopular() method in step #2 works. Let's take a look at how RESTEasy will do it in the next release.

Domain Data Declarative Hyperlinking

There are lots of articles on what REST should be, and lots of articles about how JAX-RS works, but I've found the Domain Object to URL conversion to be a "missing link" (pun intended). The JAX-RS committee recognizes this, and have proposed "Declarative Hyperlinking" as part of JAX-RS 2.0. I've been working on this concept for quite a while in RESTEasy, and I'd like to show you what I've done.

The end result

In order to fully understand the "hows" that I'm proposing, I need to first show you the end result first. The getMostPopular() method has to return XML (or JSon, or something else). It has to convert a list of the most popular Artist objects into a list of abridged information about which artists are popular, a bio, and a link. Here's an example output:

<artists>
<artist url="/artists/23523">
<name>MC Hammer</name>
<bio></bio>
</artist>
<artist url="/artists/23992">
<name>Vanilla Ice</name>
<bio></bio>
</artist>
<artist url="/artists/21421">
<name>Michael Jackson</name>
<bio></bio>
</artist>
<artist url="/artists/41025">
<name>Britany Spears</name>
<bio></bio>
</artist>
....
</artists>

(you can pretend there's a bio there... Or you can comment about what you think the bio should be)

Decorating the Artist

That seems straight forward enough of an output, so let's take a closer look at a potential implementation using the new RESTEasy I've put into SVN. The most interesting thing here is going to be how we get that url to show up. Let's say the Artist object had some information on it that described how it should be translated from an object to a URL:

...
@URITemplate("artist/{id}")
// this maps to "/artist/{id}" - JavaBeans conventions are used to convert
// '{id}' with the results of getId()
@XmlRootElement
public class Artist {
private Long id;
private String name;
private List<Albums> albus;

//getters and setters
}

Is it a Good Thing (tm) to add URI elements to your objects? I'm not not sure since it probably breaks Separation of Concerns. However, it's something that makes your live as a REST developer a bit easier.

Let's see how we can use Artist in other settings.

Mini Artist DTO - Artist url

Since we only want a little bit of the Artist (for example, we want name, and we don't want to the albums). We need a way to get only the data we want, including the URL. For that, I'm going to create a new DTO. Take a look at the Object to URL conversion magic:

@XmlElement(name="artist")
public class MiniArtist{
public String name;

@XmlJavaTypeAdapter(value = UriAdapter.class)
@Attribute
public Artist url;

public MiniArtist(Artist artist){
this.name = artist.first;
this.url = artist;
}
}

JAXB goodness

Here's a good discussion on XmlAdapters, just in case you want to learn more. UriAdapter is half baked at the moment, so it's not in the resteasy SVN. It's just an example of how the new RESTEasy utilties can be used (I'll be writing more on these utilities in future blog posts). Here's what UriAdapter looks like:

public class UriAdapter extends XmlAdapter<String, Object> {
@Override
public String marshal(Object domainObject) throws Exception {
return ObjectToURI.getInstance().resolveURI(domainObject);
}

@Override
public Object unmarshal(String uri) throws Exception {
return ResteasyProviderFactory.getContextData(InternalDispatcher.class)
.getEntity(uri);
}
}

Conversion through Declarative Magic

ArtistController ends up being elegantly simple because all of the work is really done elsewhere:

 @GET
@Produces({"application/xml", "application/json"})
@Path("/most-popular-artist")
public Artists getMostPopular(){
Artists artists = new Artists();
for(Artist artist : service.getMostPopular()){
artists.add(new MiniArtist(artist))
}
return artists;
}

So what's the big deal?

ArtistsController.getMostPopular() needs to somehow convert an Artist to a URL. Sure, we can use String manipulation to create the URL. IMHO, However, while the server it's probably a bad practice to scatter that kind of logic across your code. While URLs usually don't change once they're live, you can still get into quite a bit of complicated logic in creating those URLs:

  • You allow multiple server hosts to expose your data, and you want the outgoing full URLs to match the incoming host. This can get a bit tricky when you have a layered system. CDNs, for example, will use a different host name than your server. They do give you enough information about the initial host in custom headers that you may want to reuse.
  • You want to add cross-cutting parameters to your URLs, for example a query parameter tha allows the user to specify format (for example "?format=xml|json" or ".xml|.json"). You'll want to make sure that all subsequent URLs use that same content-negotiation URL strategy.
  • You decide to change the URL structure before production

For those reasons, you really need a two step conversion:

  1. object to url conversion
  2. cross-cutting concerns to url enhancements

For the former concern, it's a good idea to declare just enough information about how to perform the object instance to URL translation in a location relative to the domain object, and let a centralized URLConverter take care of the "rest". Annotations are the du jour strategy for this kind of information, but RESTEasy does provide more mechanisms beyond annotations

Where do we go from here?

I hope that you have good questions and constructive criticism. I'd be more than glad to hear it. The topic of URL <-> object is a core REST concern that needs to be "addressed." Tools need to be built and best practices need to be invented. There's plenty of work left.

I showed you how the Object -> URL conversion works, but not URL -> Object. I'll show you that in another blog post. I also need to give you more details about how to use the underlying RESTEasy utilities that I mentioned earlier. There are also plenty more RESTful goodies that I've worked that I'd like to talk about in the upcoming weeks.

References
Published at DZone with permission of Solomon Duskis, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

sub online replied on Fri, 2009/06/26 - 2:32am

Biased (prejudiced). Stop saying those things about it. You're just biased.links of londonWatch me.links of londonMake a long story short.You're getting on my nerves! You're really annoying.Tiffany JewelleryI need it badly.Money makes the world go round.Do you have an opinion!There's no need. There's no necessary.Don't worry! It's nothing.Stop being so indecisive.Let's see you do it.I probably can't help you with this.

wang zi replied on Thu, 2009/07/09 - 7:06am

Nice articles, but I am not clear about the point you mentioned about how to distinguish fake and real louis vuitton handbags.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.