users@jersey.java.net

Re: First hurdles

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Wed, 28 Nov 2007 11:03:44 +0100

Richard Wallace wrote:
>>
>> 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.

Yes.


> 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.)
>

This thinking is along similar lines, although slightly differs on the
use of message body writers (see below).


> To make this possible one change I'd like to see is to the isWritable()
> method (has it been renamed to supports()?),

Other way around it was "supports" when the MessageBodyWriter and
MessageBodyReader were one interface called EntityProvider.


> 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).

Yes.


> 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 was thinking instead that Jersey could provide some infrastructure for
managing the set of view templates engines. A template engine could provide:

   - a message body writer (that specifies the media types produced using
     ConsumeMime)

   - the conventions for the template suffixes; and

   - the translation of a resource instance and the path that
     was matched to the location of the template file.

Internally Jersey could use an internal "meta" message body writer for
views or something else when a message body writer is requested, for
example, Jersey could first search for a message body writer that is not
for a view, if there is a match use that, otherwise Jersey could search
for a message body writer for a view and use that. It should allow us
more flexibility with experimentation of implementation and optimization
strategies for managing templates.

In general this hides complexity for those wishing to plug in their own
template engine, and we can avoid requiring a change the message body
writer iface (i.e. it takes time for changes to get agreed and
propagated to Jersey so it would be good not to be blocked waiting...).

Also i would like to ensure that templates can be used with resources
whereby a resource is the model. This is the case when there is a
template but there is no corresponding HTTP method returning a model.

Plus we could have a RenderResponse class that allows a developer to
explicitly choose a template and pass in a model. I would prefer that
approach than using annotations to specify such information in this case.


Potentially this can be hacked into a branch of Jersey as of now. The
branch could change the look up of message body writers to not use
ProviderFactory e.g. in line 249 of

   com.sun.ws.rest.spi.container.AbstractContainerResponse

and instead defer to the WebApplication instance for getting an
appropriate message body writer. The template providers could be hacked
in by registering via a resource config property.

Would you like to work on a branch of Jersey to experiment?


>>> 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.
>

The Bookmark class will be available because the JVM stores reflection
information on the parameterized types of the BookmarkListType class. So
basically the types[] parameter represents the array of actual type
arguments of a parameterized type (in this case {Bookmark.class})
associated with the first and only actual type argument of the
parameterized type of BookmarkListType. The reflection of generics is a
bit funky... see the Class.getGenericSuperclass() method for more details.


>> 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.
>

OK.

Paul.

-- 
| ? + ? = To question
----------------\
    Paul Sandoz
         x38109
+33-4-76188109