Paul Sandoz wrote:
> 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]).
>
Great! I'll be looking forward to it!
>
>> 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 :-)
Those are the risks of being an early adopter I guess. But it's not
without it's benefits, so I'll deal. ;)
>
> 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 ?
>
That would be fine. I can probably workaround it until then.
> 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).
Completely reasonable.
>
> 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).
That's similar to what I was thinking as well. My current line of
thought is that I'd like to see any view technology implemented in terms
of the JSR311 API and I think this is completely possible with a few
changes. What I've been trying to cook up is a fairly generic
MessageBodyWriter that would be able to handle any object type and any
media type the user might want. It could look up templates to use based
on a standard naming convention (com.my.company.MyObject.json.ftl or
com.my.company.MyObject.xhtml.ftl to produce either JSON or XHTML output
from Freemarker templates). But it could also inspect the resource or
something else maybe and look for something like an @Result annotation
that can tell it more information about what template should be, etc.
(This is also why I was looking to create my own ProviderFactory as well.)
To make this possible one change I'd like to see is to the isWritable()
method (has it been renamed to supports()?), to add as a parameter the
media type. As it stands, if I wanted this generic Writer I would leave
the @ProduceMime annotation off so that it will be considered for all
media types (if my understanding is correct). So what I'd like to be
able to do is in the isWritable() method look for the template that
would be used and if it doesn't exist return false. But I can only do
that if I know exactly what the media type is that is trying to be
written. The alternative is to throw an illegal argument exception from
the write() method, but I don't like that because it doesn't seem like
it should get there if the Writer can't handle it.
>
>
>>>
>>>> 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;
>
Shouldn't that be BookmarkListType.class? Because you still wouldn't
have Bookmark available at runtime due to erasures.
> 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...
I agree. And like I said, I don't think I would actually use it in
production. It only came up because I started with this dead simple
example that I wouldn't expect to ever actually occur when writing a
real app.
>
>
>>>
>>>> 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.
>