users@jersey.java.net

Re: Resources and subresources

From: Marc Hadley <Marc.Hadley_at_Sun.COM>
Date: Thu, 29 Nov 2007 14:53:41 -0500

On Nov 28, 2007, at 12:52 AM, Richard Wallace wrote:
>
> I've been playing with with resources and subresources and was
> trying to fit in with the way the JSR311 spec works. So, you have
> root resources that return subresources. What I was hoping to do
> was get an instance of a ResourceFactory to create the the resource
> to return. Unfortunately, the createResource() method needs the
> resource features and properties from the ResourceConfig which I
> don't have. I was then hoping to just use my Guice injector to
> create the resource, but I don't have access to the ServletContext
> (well, I can, but it's an ugly hack).
>
> What I tried next is the way I would prefer to create my resources.
> That is to annotate each resource with the @UriTemplate that it
> handles. So for a list of all bookmarks and then a single bookmark
> I'd have resources like:
>
> @UriTemplate("/")
> public class BookmarksResource { ... }
>
> @UriTemplate("/{uri}")
> public class BookmarkResource { ... }
>
> With this pattern I don't have to worry about creating
> BookmarkResource instances from BookmarkResource and injecting all
> the right values. It would also make deeper hierarchies easier to
> build because I'm not sure how it's intended to be done, but from my
> understanding hitting a resource with a path of /bookmarks/{uri}/
> comments/{comment-id} would require the BookmarksResource to be
> created, which would create a BookmarkResource, which would create a
> CommentsResource which would finally create a CommentResource that
> is returned. That means you could need to inject a whole bunch of
> dependencies into your root level resource so they can trickle down
> to the deeper resource objects. I'd much rather have all my
> resources created equally.
>
If you're resources are such that you can use a URI template to
identify them then that is definitely the way to go rather than using
sub-resources. Sub-resources are there to handle cases where the URI
path is somewhat dynamic or when the same resource can be found at
multiple paths (though you can always create a subclass for each and
then still use a template).

> I had thought this might still work even if it seems to go against
> the grain of JSR311, but found that it doesn't.

Its not against the grain of 311, that is how it is supposed to be
used for the majority of cases. I think you've just run into an issue
using '/' as a template. The value of @UriTemplate is a relative URI
whose base URI is supplied by the deployment context. I suspect we may
have an issue dealing with what is effectively an empty template.

> What happens is that when I hit http://localhost:8080/spike/http://google.com
> , everything works as expected. But when I try and hit http://localhost:8080/spike/
> , I get a 500 response with a message that says "The "Content-Type"
> header is set to text/xml, but the response has no entity". After
> doing some digging it seems this is because for the / path the
> BookmarkResource is being used instead of the BookmarksResource.
> Because the uri is empty no bookmark is found and null is returned.
>
> There were 2 things unexpected about this. The first was that
> BookmarkResource was being used, which I'll get to in a minute. The
> second is that returning null resulted in a 500. My expectation was
> that returning null would indicate a 404, because the resource at
> the location could not be found. To me, that's really the only
> reason a method annotated with @HttpMethod would ever return null,
> is if there was nothing to return. Otherwise it would throw an
> exception.
>
> Getting back to the issue at hand, I did some more digging to find
> out exactly how it was being decided that the BookmarkResource
> should handle / instead of the BookmarksResource. What I found was
> that the rule for matching the BookmarkResource was being checked
> first and was using the regex /(.*?)(/)?, or something similar,
> that's from memory. The important part is the /.* bit. This means
> the BookmarkResource will be matched for anything. That would solve
> this problem. Is there any ordering going on when the resources are
> being added, or are they simply matched in the order they're found
> when scanning? If there is an order, shouldn't the rule for the
> BookmarksResource be first, since it is less specific? If there
> isn't any ordering being done, don't you think there probably should
> be?
>
Matching resources are sorted using the number of literal characters
(i.e. those not resulting from template variables) as the primary key
and the number of template variables as the secondary key.

I think if you changed your template to be something like:

@UriTemplate("bookmarks")
public class BookmarksResource { ... }

@UriTemplate("bookmarks/{uri}")
public class BookmarkResource { ... }

with the uris

http://localhost:8080/spike/bookmarks/http://google.com
http://localhost:8080/spike/bookmarks

then everything would be OK.

Marc.

---
Marc Hadley <marc.hadley at sun.com>
CTO Office, Sun Microsystems.