Monday, January 05, 2009

JAX-RS and HATEOAS : A Proposal

This is a follow-up to my latest post: JAX-RS and HATEOAS, AKA "JAX-RS should provide a way to identify and resolve resource URLs".

I'd like to outline a possible solution, but first, let me recap the problem with a simple example.

The Problem.

JAX-RS let you define web resources out of simple POJOs by properly placing simple, meaningful, annotations.
Let's say we have a Library resource, containing Book resources.

@Path("/")
public class BookResource {

@GET
@Path("/library/book-{id}")
public Response getBook(@PathParam("id") id) {
// ...
}
}


@Path("/")
public class LibraryResource {

@GET
@Path("/library")
public Response getLibrary() {
// ...
List<Book> books = getBooks();
for (Book book : books) {
String id = book.getId();
// Now I have the book id:
// how can I obtain the URL of the resource corresponding to that book?
}
// ...
}
}

So we have two kind of resources, and a relation between the two: from LibraryResource to BookResource.
If you're still wondering why the LibraryResource needs to know the URLs of the BookResources, the answer is Hypermedia As The Engine Of Application State: that is, in a REST application the only way to access the books in the library is to go through the LibraryResource and follow the published book links towards the proper BookResource.
The true question is: how can LibraryResource obtain the BookResource URLs?

The Proposal.

My proposal involves two new annotations and a new utility method.

The ResourceUri annotation must be placed on the web resource method defining the path to use for accessing the resource itself.
It has two arguments: resource (mandatory), defining the class of the identified resource, and locator (optional), eventually defining the resource locator for the resource itself.

The LocatorUri annotation must be placed on the web resource method defining the path for accessing a sub-resource: so, it identifies a sub-resource locator method.
It has two arguments: resources (mandatory), defining the classes of the sub-resources located by the locator method, and locator (optional), eventually defining another locator up in the chain.

Finally, the UriInfo#getResourceUri(Class resourceClass, Object... uriParameters) should resolve the resource URI of the given resource class by inspecting the annotations above.

So, we could rewrite the example above in the following way:

@Path("/")
public class BookResource {

@ResourceUri(resource=BookResource.class)
@GET
@Path("/library/book-{id}")
public Response getBook(@PathParam("id") id) {
// ...
}
}


@Path("/")
public class LibraryResource {

@GET
@Path("/library")
public Response getLibrary(@Context UriInfo uriInfo) {
// ...
List<Book> books = getBooks();
for (Book book : books) {
String id = book.getId();
URI bookUri = uriInfo.getResourceUri(BookResource.class, id);
// ...
}
// ...
}
}


So, with a bunch of annotations and a simple method call, we are now able to clearly say what is the URI that identifies a web resource, and how to resolve it.

What do you think?

2 comments:

Anonymous said...

Its very important and there are even implementations already:

http://markmail.org/message/fegpx2n2hbb6bszp

I hope they will be incorporated into official releases.
Maybe one needs to lobby for it?

http://markmail.org/message/zcrptvxwlflnjupq

Sergio Bossa said...

I wasn't able to find any implementation, indeed.
So I've built, for the project I'm working on, an implementation based on the concepts explained in my posts.

However, I absolutely agree: we need an official solution.

Thanks for your comment,
Cheers,

Sergio B.