users@jersey.java.net

Re: First hurdles

From: Richard Wallace <rwallace_at_thewallacepack.net>
Date: Mon, 26 Nov 2007 19:16:28 -0800

Paul Sandoz wrote:
> Richard Wallace wrote:
>> Hello,
>>
>> So I'm getting over my first hurdles using Jersey and just thought
>> I'd give a bit more feedback. After figuring out that the issue with
>> the scanning was because I was trying to run the webapp inplace with
>> Jetty my next challenge was actually getting it to do something. I
>> like the idea of having a provider to generate the response. But
>> it's quite a pain for it to have to be specified in a file in the
>> META-INF/services directory. Because of this I wound up having to
>> split my webapp and my REST classes into separate projects because
>> otherwise I couldn't get the dang things in the right place for the
>> class loader to find it.
>>
>
> We are going to change this for 0.6 so that:
>
> 1) instances of reader/writer are scopped per the application; and
>
> 2) there is a way other than META-INF/services for users to register
> their own readers/writers e.g. using something like resource class
> discovery or passing in instances as a property of the
> ResourceConfig.
>
That sounds promising. One thing I'm struggling with right now with my
Guice integration is how to let Guice be responsible for creating the
entity reader/writer providers. I plan on trying to create my own
implementation of the ProviderFactory to overcome this. There are 2
things that I'm worried about with this: 1) Will my implementation
always be used? This is a concern because the jersey jar will have a
META-INF/services file for the ProviderFactory and so will my jar. So
who wins and will it be consistent across classloaders? 2) The
MediaType class has a final static field for a MediaType
HeaderProvider. If the ProviderFactory.getInstance() is called this
early, then my Guice injector will not have gotten a chance to
initialize. So, for the HeaderProviders I may have to fall back on the
Jersey ProviderFactoryImpl, though I'd rather not.
>
>> I understand there is some work going on to get better view handling
>> better, so let me second (or third or whatever I would be) getting
>> Freemarker integrated.
>>
>
> Good to know. We will concentrate on JSP/JSTL and Freemarker.
>
>
>> Something else I was wondering was what the best to declare a
>> MessageBodyWriter for a Collection of objects. Naturally I'd really
>> like to be able to just have a method in my resource like
>>
>> @HttpMethod("GET")
>> @ProduceMime("text/plain")
>> public List<Bookmark> getBookmarks() {
>> return ...;
>> }
>>
>> and a provider like
>>
>> @Provider
>> @ProduceMime("text/plain")
>> public class BookmarksProvider implements
>> MessageBodyWriter<BookmarkList> {
>>
>> public boolean isWriteable(Class<?> clazz) {
>> return List<Bookmark>.class.isAssignableFrom(clazz);
>> }
>>
>> ...
>> }
>>
>> Naturally that doesn't work because of erasures. So what I've
>> instead done is create a BookmarksList that implements List<Bookmark>
>> and just delegates to whatever actual List object is used to
>> construct it. For this simple spike it's pretty easy to do, but if
>> my domain model was larger managing all these extra List classes
>> would be a major pain. I don't know what the best solution is for
>> this (other than pushing for reified generic types that is), but
>> maybe something as simple as adding annotations to the resource
>> method indicating what the MessageBodyWriter or MessageBodyReader is
>> for that method.
>>
>
> Yes, you have highlighted a very frustrating area. Florian (in other
> messages on this list) wrote something similar (using the old
> 'EntityProvider' interface) for writing collections of JAXB elements.
>
> Specifying the reader/writer as annotations for these special cases
> would work, although it annoys me that we would have to resort to that
> :-)
>
> Note that readers/writers can now be scoped with Produce/ConsumeMime
> so it is possible to limit such readers/writers to particular media
> types. But still that does not solve the general issue.
>
> One way we can sort of do this is by having the writer method:
>
> Writer.supports(Class<?> c, Object o)
>
> that at least the contents of the list can be verified. But still...
> and something similar for reader:
>
> Reader.supports(Class<?> c, Type t)
>
> with type information extracted from the method signature.
>
> It needs some more discussion in the 311 EG.
>
The problem with the Writer.supports(Class<?> c, Object o) method is
what happens when the type is a generic collection and the collection is
empty. In that case you can't really use the contents to verify the type.

After thinking about it, I'm not too sure this is really that big of a
concern. I really can't think of any instance where I'd want to return
something as simple as a collection. Usually I'd expect to return a
list of things for a search or as a sub-resource of some other type.
And to make it really RESTful, you of course have to make your resources
well-connected so you'll need some extra context around the collection
of things.
>
>> One other thing that I noticed in the HttpResponseAdaptor.commitAll()
>> method that the status code is set and then the MessageBodyWriter
>> provider is called. So, if there is an exception when creating the
>> response body, such as an exception when parsing a Freemarker
>> template, the client will get a 200 even though the response is going
>> to be incomplete, if not completely invalid. I understand that this
>> would most likely be caused by a bug in the template or provider, but
>> it would make tracking down the problem much easier if the client got
>> a 500.
>>
>
> This is also, in general, a trade off between buffering and streaming.
> I would prefer not to enforce that all Jersey containers have to
> buffer the bytes of what is written so as to catch exceptions when
> writing (the underlying container may or may not buffer).
>
> I am not sure the client will get a 'correct' 200 response because, in
> the case of the LW HTTP server a RuntimeException or an IOException
> will be passed to the server, that is likely to terminate the
> connection or produce invalid response due to an Content-Length or
> chunked encoding validation error (i hope the former).
>
> Hmm... but for views support allowing buffering does seem to be useful
> to report 500 errors as for some template engines code 'leaks' into
> the template.
>
> I think we at least need to document the traps that writer developers
> need to be aware of. And it may be possible for writers to do their
> own buffering by Jersey supporting an implicit commit of the status
> and headers when the first byte to the OutputStream passed to the
> writer is written.
>
It might be a nice parameter for the ServletContainer to optionally
buffer the response entity so that the response code can be set
appropriately if there is an error when generating the output. The
alternative is for someone to create a servlet filter that wraps the
response to buffer the output. Not a big deal, Sitemesh already does
something similar quite effectively there aren't many reports of the
buffering impacting performance.
>
>> Now I'm off to play with getting Guice working as per Christians
>> blog. Wish me luck!
>>
>
> Good luck!
>
> Paul.
>