users@jersey.java.net

Re: [Jersey] Hello World! and Welcome to jersey-multipart

From: Craig McClanahan <Craig.McClanahan_at_Sun.COM>
Date: Mon, 03 Nov 2008 22:44:03 -0800

Gili wrote:
> I took a look at Jersey's built-in integration for MimeMultipart (from
> Javamail) and your implementation of jersey-multipart. I found the former
> API to be messy, out-of-date and return incorrect values (for example,
> BodyPart.getFilename() returns null even if Content-Disposition contains a
> filename). Your API was better but:
>
> 1) Given:
>
> Content-Disposition: form-data; name="files"
>
> you'd expect an API call that returns a Map with two rows:
> [Content-Disposition, form-data]
> [name, files]
>
> Unfortunately your API returns: Content-Disposition = [form-data;
> name="files"] and expects me to parse it myself. Any chance you'll improve
> upon this?
>
>
Hmm ... maybe something analogous to the MediaType class (perhaps
HeaderValue) that gives you an accessor to get to the value itself
("form-data" in this case), plus a Map<String,String> giving you access
to all the parameters (just one in the case shown here, but there could
be several for general header values)? We'd still want convenient
string-only ways to *construct* the header value, but it would be nice
to have something like this on BodyPart:

    MultivaluedMap<String,HeaderType> getHeaders() { ... }

instead of the current return value of "MultivaluedMap<String,String>".
Then you could call:

    HeaderType header =
bodyPart.getHeaders().getFirst("Content-Disposition");
    System.out.println("Disposition value is " + header.getValue()); //
"form-data"
    System.out.println("Name is " + header.getParameters().get("name"));
// "files"

What do you think?
> 2) I took a quick glance at the implementations and I see some problematic
> //FIXME comments, such as not being able to configure how big an attachment
> may get before being dumped to disk, requiring explict calls to cleanup(),
> etc.
>
>
Dealing with the configuration seems solvable fairly easily, although
the interesting bit is to find a solution that works for both a servlet
deployment (where there could be multiple independent apps deployed in
the same server, so system properties don't work well) and a non-servlet
deployment (where we don't have any access to servlet APIs. Probably
the best bet is an optional properties file loaded via the thread
context class loader (if any; otherwise the class loader that loaded the
jersey-multipart classes) that can be used to set configuration stuff
like this.

Regarding the need for the cleanup call, I'm open to suggestion for how
to improve this. I've started looking at Jersey filters (which would
impose a Jersey-specific implementation dependency), but haven't settled
on anything yet.

Separately, I took your suggestion to look at Commons FileUpload. It
turns out that there *is* a single important class
(org.apache.commons.fileupload.MultipartStream) -- plus a couple of
small helpers -- that performs the only real task I'm currently
delegating to JavaMail. That's the actual parsing of the multipart/*
input stream. I need to do some experiments, and take heed of the
potential concerns in the javadocs about nested multipart/* body parts,
but it may well be that we could incorporate a variant of just this
class and not even need the entire Commons FileUpload package (or it's
dependence on Commons IO).

> Gili
>
>
Craig

> Craig McClanahan wrote:
>
>> Gili wrote:
>>
>>> Hi Craig,
>>>
>>> On the topic of jersey-multipart:
>>>
>>> 1) Is it really Jersey-specific or can it be used with any JAX-RS
>>> implementation?
>>>
>>>
>> Currently, the only Jersey-specific dependencies are in the unit tests.
>> In principle the runtime should only require the JAX-RS APIs, JavaMail,
>> and JAF. But I haven't tested it with anything else.
>>
>>> 2) I'm curious whether you looked into using Apache Commons FileUpload
>>> instead of Javamail under the hood. FileUpload's jar is only 50k and
>>> deals
>>> exclusively with parsing this kind of data, versus Javamail which is an
>>> entire email client. It might also be more flexible, better integrated
>>> than
>>> JavaMail.
>>>
>>>
>>>
>> I hadn't looked at Commons FileUpload, based on the (perhaps mistaken?)
>> assumption that it only supported "multipart/form-data". I'll
>> definitely go take a look.
>>
>> On the other hand, basically all the server side apps I'm working on are
>> running on Glassfish anyway, so JavaMail comes integrated "for free" :-).
>>
>>> Gili
>>>
>>>
>>>
>> Craig
>>
>>
>>> Craig McClanahan wrote:
>>>
>>>
>>>> For those who don't know me, I have been around the Java web tier for
>>>> quite a while, being the original author of the Struts framework
>>>> (<http://struts.apache.org>), as well as co-spec-lead for JavaServer
>>>> Faces 1.0 and 1.1. My more recent interests have focused on RESTful web
>>>> services, which led me naturally towards JAX-RS and the Jersey
>>>> implementation.
>>>>
>>>> I've been one of the folks inside Sun who has been leveraging Jersey for
>>>> some internal projects over the last few months. We had a particular
>>>> need to support MIME multipart/* media types, and it made sense to
>>>> generalize this into a reusable module -- hence, I've just uploaded the
>>>> "jersey-multipart" module to the "contribs" directory. It relies on 1.0
>>>> or later Jersey code, and provides what I hope are found to be elegant
>>>> solutions to the problems of multipart handling, while leveraging all
>>>> the nice JAX-RS providers support for dealing with the entity content of
>>>> body parts, just like we've grown spoiled by on complete message
>>>> bodies. And, it works both on the server side and the client side, when
>>>> you use jersey-client.
>>>>
>>>> Example server code to build a multipart response might look like this:
>>>>
>>>> // I have also provided an appropriate MessageBodyWriter for the
>>>> MyBean class
>>>> MyBean bean = ...;
>>>> return Response.ok(new MultiPart().
>>>> type(new MediaType("multipart", "mixed").
>>>> bodyPart("This is the first body part in plain text", new
>>>> MediaType("text", "plain")).
>>>> bodyPart(bean, new MediaType("x-application",
>>>> "x-format"))).build();
>>>>
>>>> (Of course, you can do things in a more fine-grained fashion, but the
>>>> builder pattern utilized all over the JAX-RS and Jersey APIs was so cool
>>>> that Paul suggested I use it here too, so I did :-).
>>>>
>>>> To read a MultiPart entity (produced by code like the previous example)
>>>> that was uploaded to the server you might do something like this:
>>>>
>>>> // I have also provided an appropriate MessageBodyReader for the
>>>> MyBean class
>>>> @Path("...")
>>>> @PUT
>>>> @Consumes("multipart/mixed")
>>>> @Produces(...)
>>>> public Response handler(MultiPart multiPart) {
>>>> BodyPart part0 = multiPart.getBodyParts().get(0);
>>>> String text = part0.getEntityAs(String.class);
>>>> BodyPart part1 = multiPart.getBodyParts().get(1);
>>>> MyBean bean = part1.getEntityAs(MyBean.class);
>>>> ...
>>>> multiPart.cleanup();
>>>> }
>>>>
>>>> The need for cleanup() is because the implementation knows how to buffer
>>>> "large" body parts to temporary files on disk, so you don't blow away
>>>> your JVM heap on a multi-gigabyte upload or download. I'm looking for a
>>>> way to avoid the need for the application to call this, but haven't
>>>> found one yet -- in the mean time, everything else about dealing with
>>>> multipart files has seemed pretty easy to deal with.
>>>>
>>>> As mentioned above, this module works on the client side as well, if
>>>> you're using jersey-client. The unit tests have some more worked-out
>>>> examples of the lower level details.
>>>>
>>>> Give it a try and see what you think! And, for sure, if you see
>>>> anything that needs to be improved, please ask here and/or file an issue
>>>> in the issue tracking system.
>>>>
>>>> Craig McClanahan
>>>>
>>>> PS: Among my other interests will be working with the Atom Publishing
>>>> Protocol support, again with the idea of leveraging JAX-RS providers to
>>>> do format translations for custom <content> payloads.
>>>>
>>>>
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>>
>>>>
>>>>
>>>>
>>>>
>>>
>>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>
>>
>>
>>
>
>