Hello again,
First off, I just wanted to say that I appreciate the responsiveness
from all you guys and the openness to feedback. I think this is my last
major issue for the time being.
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.
I had thought this might still work even if it seems to go against the
grain of JSR311, but found that it doesn't. 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?
Or is there some other simple thing that I'm just overlooking?
Thanks again,
Rich