users@jersey.java.net

Re: Injection of message body readers/writers now supported in latest builds

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Mon, 28 Jan 2008 11:10:17 +0100

Hi Ross

Ross Judson wrote:
> I've got my simple form-based example working now, where my POST method
> receives the multipart MIME message.

Great.


> Looking over the problem, I think what
> would be _really_ cool would be another Param annotation, FieldParam.
>
> What I expected initially was that I could do something like this:
>
> @POST
> @Path("upload")
> @FormData
> public void uploadGraphML(
> @FieldParam(name="graphml",type="application/xml") GraphML graphML,
> @FieldParam(name="owner") String ownerName
> ) {
> }
>
>
> Sure, I'd like Jersey to just do everything for me ;)

Make coffee, take dog for a walk, feed baby :-)


> Marking @FormData
> tells Jersey that that form data from a post is inbound, so it knows to
> break those into named fields. The @FieldParam tells Jersey the name and
> mime type of the field within the multipart MIME data. It can then perform
> its normal data conversion steps to get from there to the type of the field
> (in this case I have a JAXB context defined for GraphML). For each parameter
> the process is repeated, locating the appropriate field on the inbound
> message and converting it.
>
> I've got what I need working, but there's a certain elegance (not to mention
> great ease of use) to the scheme outlined above...
>

We are trying to avoid adding lots and lots of annotations to the core
of Jersey and JAX-RS, but forms are so very common...

Here is an alternative proposal that should be possible to implement
soon with 0.6 (later in the email i will point to another way that might
get closer to what you want that builds on this).

What if the developer can do this:

   public class MyForm implements FormBean {
     public @FieldParam(name="graphml",type="application/xml")
       GraphML graphML;

     public @FieldParam(name="owner") String ownerName;

     public Map<String, Object> otherParameters;
   }

   @POST
   @Path("upload")
   @ConsumeMime("multipart/form-data")
   public void uploadGraphML(MyForm form) { ... }


The runtime will supply a FormBean interface and a message body reader:

   public interface FormBean {}

   @Provider
   @ConsumeMime("multipart/form-data")
   public class FormBeanReader extends MessageBodyReader<FormBean> {
     // HttpContext will be renamed to Context soon
     @HttpContext MessageBodyContext mContext;

     public boolean isReadable(Class<?> type) {
       return FormBean.class.isAssignable(type);
     }

     public FormBean readFrom(Class<FormBean> type, MediaType mediaType,
             MultivaluedMap<String, String> httpHeaders,
             InputStream entityStream) {

       // Use mContext to get a reader for reading Java type
       // anotatated FormParam that declared media types e.g.
       // @FieldParam(name="graphml",type="application/xml") GraphML
       MessageBodyReader<GraphML> r = mContext.getMessageBodyReader(
         GraphML.class, mediaType);
     }
   }

The important aspect here is that the above message body reader has
access to the complete set of registered readers and can reuse them
(this may get close to what Jose was wanting to do about chaining
readers/writers).

I think that allows for a very modular and reusable approach.


Once this is in place there is a way to plugin in a solution to unwrap
the form fields of a form bean. For more details see a blog entry i
wrote on extending HTTP to support Scala-based support for closures [1].
This solution is a bit "raw" and is not exposed as a public API but i
believe it should be possible to implement a prototype solution fairly
easily.

Paul.

[1] http://blogs.sun.com/sandoz/entry/using_scala_s_closures_with

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