Richard Wallace wrote:
> 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?
Unfortunately it will never be used as it is not currently possible to
override the ProviderFactory used by Jersey (the first entry in
META-INF/services is used). This was really meant for use by JAX-RS
implementations. For 0.6 ProviderFactory will be removed (we have
already made changes in the 0.6 JAX-RS API, based on this proposal [1]).
> 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 don't really have any solution for you right now :-( you are somewhat
ahead of the implementation curve :-)
We can to make such changes in the 0.6 development (starts in Jan 18th
and the release will be in Feb, or March). Can you wait until the 0.6
development starts ?
I am a bit reluctant to do such changes in the 0.5 release because we
have our work cut out syncing up with the 0.5 JAX-RS API and a lot of
internal changes to the implementation (for example i just made major
changes to the URI matching, Jakub is making major changes to the
modeling of resources).
But i really want to capture your use-case in the 0.6 release, i think
we can have an SPI a bit like that for ResourceProvider so IoC
frameworks can instantiate MessageReader/Writer instances as well as
Jersey performing its own injection. Also i would be very happy to
receive any proposals on such an SPI :-)
I have a use-case where i want the JSP template processor to be an
message writer provider with access to the servlet context. Another
use-case is for a JAXB provider to be scoped to a resource class such
that a specific JAXB context can be used (perhaps a more tricky case).
>>
>>> 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.
>
Good point.
> 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.
Agreed.
Perhaps there could be an annotation that specifies the type using an
agreed convention?
@HttpMethod
@Type(BookmarkListType.class)
List<Bookmark> getBookmarks() {
class BookmarkListType extends TypeInferencer<List<Bookmark>> { }
public abstract class TypeInferencer<T> { }
That way the generic type "List<Bookmark>" is accessible using
reflection and therefore the "Bookmark" type. We can have a method:
Writer.supports(Class<?> c, Types[] types)
Where types is the actual type arguments of c declared by the class
BookmarkListType for the type T.
In this case the supports method can be implemented as follows:
return c == List.class &&
types.length == 1 &&
types[0] == Bookmark.class;
In fact it should be possible to specify the @Type on the resource class
and the runtime to do a lookup to get the Type[] instance.
It seems rather complicated though... not sure i like it...
>>
>>> 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.
I think we may be able to do something container independent for such
cases as some template engines are independent of the container.
> Not a big deal, Sitemesh already does
> something similar quite effectively there aren't many reports of the
> buffering impacting performance.
I guess the size of the bytes produced from processing a template is
manageable i.e. we are probably talking Kbs and not Mbs of size? plus
HTTP caching can reduce the burden on the server in some cases.
Paul.
--
| ? + ? = To question
----------------\
Paul Sandoz
x38109
+33-4-76188109