users@jersey.java.net

Re: Resources and subresources

From: Richard Wallace <rwallace_at_thewallacepack.net>
Date: Thu, 29 Nov 2007 19:46:06 -0800

Awesome. Thanks for the response. That is exactly what I was hoping for.

Rich

Marc Hadley wrote:
> 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.
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>