Hello World! and Welcome to jersey-multipart

43 messages Options
Embed this post
Permalink
1 2 3
Craig McClanahan

Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
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: [hidden email]
For additional commands, e-mail: [hidden email]

Paul Sandoz

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
Hi Craig,

Welcome to the list of Jersey committers :-)

Thanks very much for the multipart MIME contribution, it looks really  
good. I would like to leverage this to improve the support for  
multipart/form-data and also consider support for Multipart Java Beans.

Classes supporting AtomPub support would be great!

Re: the cleanup aspect. I think we need to review Jersey's IoC support  
and integration capabilities with the likes of Spring/Guice. It is  
really hard to be abstract from an IoC framework :-( If the returned  
MultiPart was instantiated in the life-cycle of the request then when  
the response is about to the sent a pre destroy method could be called  
to do the clean up. That would not work for the conditions of the  
client working standalone (perhaps cleanup could also be called before  
being GC'ed although that cannot be relied upon).

Paul.


On Oct 17, 2008, at 1:00 AM, 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: [hidden email]
> For additional commands, e-mail: [hidden email]
>


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Jeff Schmidt

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
Some javascript/style in this post has been disabled (why?)
Hi Craig:

Thanks for your work on jersey-multipart. I have some SOAP based endpoints where a request can consist of various XML meta data, and then there is an element of type base64Binary where an arbitrary file can be 'attached' to the request. I  would like to do something equivalent in a RESTful manner. It seems like a request comprised of an XML entity and a binary entity would be the way to go. The JAX-RS resource does not need to understand the binary entity, just make it available to some server-side code.

Looking at some of the jersey-multipart code, it looks like I can use BodyPart.getEntityAs(MyMetaData.class) to access the XML meta data, and BodyPart.getEntity().getInputStream() to access the binary data. When I'm done, MultiPart.cleanup() will delete the temporary binary file (if > threshold bytes).  Am I reading that right?

Also, I don't know if doing this on the server side will make it difficult for clients. You said the jersey-client can easily work with jersey-multipart. I've not yet looked into the Commons HttpClient or other client libraries to see if they have issues with making multipart requests.

Thanks,

Jeff

On Oct 17, 2008, at 12:58 AM, Paul Sandoz wrote:

Hi Craig,

Welcome to the list of Jersey committers :-)

Thanks very much for the multipart MIME contribution, it looks really good. I would like to leverage this to improve the support for multipart/form-data and also consider support for Multipart Java Beans.

Classes supporting AtomPub support would be great!

Re: the cleanup aspect. I think we need to review Jersey's IoC support and integration capabilities with the likes of Spring/Guice. It is really hard to be abstract from an IoC framework :-( If the returned MultiPart was instantiated in the life-cycle of the request then when the response is about to the sent a pre destroy method could be called to do the clean up. That would not work for the conditions of the client working standalone (perhaps cleanup could also be called before being GC'ed although that cannot be relied upon).

Paul.


On Oct 17, 2008, at 1:00 AM, 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: [hidden email]
For additional commands, e-mail: [hidden email]



---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]




--

Jeff Schmidt






Craig McClanahan

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
Some javascript/style in this post has been disabled (why?)
Jeff Schmidt wrote:
Hi Craig:

Thanks for your work on jersey-multipart. I have some SOAP based endpoints where a request can consist of various XML meta data, and then there is an element of type base64Binary where an arbitrary file can be 'attached' to the request. I  would like to do something equivalent in a RESTful manner. It seems like a request comprised of an XML entity and a binary entity would be the way to go. The JAX-RS resource does not need to understand the binary entity, just make it available to some server-side code.
Yep, that is exactly the sort of thing I use it for.

Looking at some of the jersey-multipart code, it looks like I can use BodyPart.getEntityAs(MyMetaData.class) to access the XML meta data, and BodyPart.getEntity().getInputStream() to access the binary data. When I'm done, MultiPart.cleanup() will delete the temporary binary file (if > threshold bytes).  Am I reading that right?

That's exactly right.
Also, I don't know if doing this on the server side will make it difficult for clients. You said the jersey-client can easily work with jersey-multipart. I've not yet looked into the Commons HttpClient or other client libraries to see if they have issues with making multipart requests.

You can also look at the Jersey client code itself.  One of the really cool things about jersey-client is you can use the same providers (including the jersey-multipart stuff) on the client just like you can use it on the server.  There's some small samples of this in the jersey-multipart unit tests.

Thanks,

Jeff

Craig

On Oct 17, 2008, at 12:58 AM, Paul Sandoz wrote:

Hi Craig,

Welcome to the list of Jersey committers :-)

Thanks very much for the multipart MIME contribution, it looks really good. I would like to leverage this to improve the support for multipart/form-data and also consider support for Multipart Java Beans.

Classes supporting AtomPub support would be great!

Re: the cleanup aspect. I think we need to review Jersey's IoC support and integration capabilities with the likes of Spring/Guice. It is really hard to be abstract from an IoC framework :-( If the returned MultiPart was instantiated in the life-cycle of the request then when the response is about to the sent a pre destroy method could be called to do the clean up. That would not work for the conditions of the client working standalone (perhaps cleanup could also be called before being GC'ed although that cannot be relied upon).

Paul.


On Oct 17, 2008, at 1:00 AM, 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: [hidden email]
For additional commands, e-mail: [hidden email]



---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]




--

Jeff Schmidt







Jeff Schmidt

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
Some javascript/style in this post has been disabled (why?)
Thanks Craig! This all sounds good.

I guess my client side question was more generic. Potential clients may be using Java, Python, Ruby etc. and whatever related libraries. I don't know if issuing multi-part requests is unusual or difficult in general for such clients. I don't want to put up anymore barriers to entry to use the API then I have to. :)  What has been your experience in this regard? Any complaints from clients?

Cheers,

Jeff

On Oct 23, 2008, at 1:10 PM, Craig McClanahan wrote:

Jeff Schmidt wrote:
Hi Craig:

Thanks for your work on jersey-multipart. I have some SOAP based endpoints where a request can consist of various XML meta data, and then there is an element of type base64Binary where an arbitrary file can be 'attached' to the request. I  would like to do something equivalent in a RESTful manner. It seems like a request comprised of an XML entity and a binary entity would be the way to go. The JAX-RS resource does not need to understand the binary entity, just make it available to some server-side code.
Yep, that is exactly the sort of thing I use it for.

Looking at some of the jersey-multipart code, it looks like I can use BodyPart.getEntityAs(MyMetaData.class) to access the XML meta data, and BodyPart.getEntity().getInputStream() to access the binary data. When I'm done, MultiPart.cleanup() will delete the temporary binary file (if > threshold bytes).  Am I reading that right?

That's exactly right.
Also, I don't know if doing this on the server side will make it difficult for clients. You said the jersey-client can easily work with jersey-multipart. I've not yet looked into the Commons HttpClient or other client libraries to see if they have issues with making multipart requests.

You can also look at the Jersey client code itself.  One of the really cool things about jersey-client is you can use the same providers (including the jersey-multipart stuff) on the client just like you can use it on the server.  There's some small samples of this in the jersey-multipart unit tests.

Thanks,

Jeff

Craig

On Oct 17, 2008, at 12:58 AM, Paul Sandoz wrote:

Hi Craig,

Welcome to the list of Jersey committers :-)

Thanks very much for the multipart MIME contribution, it looks really good. I would like to leverage this to improve the support for multipart/form-data and also consider support for Multipart Java Beans.

Classes supporting AtomPub support would be great!

Re: the cleanup aspect. I think we need to review Jersey's IoC support and integration capabilities with the likes of Spring/Guice. It is really hard to be abstract from an IoC framework :-( If the returned MultiPart was instantiated in the life-cycle of the request then when the response is about to the sent a pre destroy method could be called to do the clean up. That would not work for the conditions of the client working standalone (perhaps cleanup could also be called before being GC'ed although that cannot be relied upon).

Paul.


On Oct 17, 2008, at 1:00 AM, 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: [hidden email]
For additional commands, e-mail: [hidden email]



---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]




--
Jeff Schmidt


--

Jeff Schmidt






Craig McClanahan

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
Jeff Schmidt wrote:
> Thanks Craig! This all sounds good.
>
> I guess my client side question was more generic. Potential clients
> may be using Java, Python, Ruby etc. and whatever related libraries. I
> don't know if issuing multi-part requests is unusual or difficult in
> general for such clients. I don't want to put up anymore barriers to
> entry to use the API then I have to. :)
Agreed -- this is one of the nicest things about REST.
>  What has been your experience in this regard? Any complaints from
> clients?
>
Constructing a multipart request is actually very easy, with pretty much
any client language and HTTP framework.  From the point of view of such
a framework, it's a single request of type "multipart/mixed" (or
whatever), and the interesting part of the problem is constructing the
message body.  For that, it's mostly a matter of alternating between a
bunch of headers and content for each body part, with a little extra
glue for the "boundary" string.  In jersey-client, the code that does
this is in MultiPartWriter.java, and as you can see it is pretty
straightforward (assuming your library already has a way to format the
request body for your data entity -- which is the same code you'd need
to send that body part by itself).

Parsing a multipart response, on the other hand, is somewhat more
complicated.  In the case of jersey-multipart I punted :-) and used
JavaMail APIs, because systems that understand mail streams already have
to know how to do this.  If you use a different Java HTTP library you
can still use JavaMail for this part; in other languages, I'd start by
looking for parsing libraries that already exist, and then just glue
them in to the client.

One of my upcoming tasks will be to create a Ruby client library for
some of the services I'm working on, and that will ultimately include
some multipart processing -- so I'll soon get to practice what I preach
above :-).

Craig

> Cheers,
>
> Jeff
>
> On Oct 23, 2008, at 1:10 PM, Craig McClanahan wrote:
>
>> Jeff Schmidt wrote:
>>> Hi Craig:
>>>
>>> Thanks for your work on jersey-multipart. I have some SOAP based
>>> endpoints where a request can consist of various XML meta data, and
>>> then there is an element of type base64Binary where an arbitrary
>>> file can be 'attached' to the request. I  would like to do something
>>> equivalent in a RESTful manner. It seems like a request comprised of
>>> an XML entity and a binary entity would be the way to go. The JAX-RS
>>> resource does not need to understand the binary entity, just make it
>>> available to some server-side code.
>> Yep, that is exactly the sort of thing I use it for.
>>>
>>> Looking at some of the jersey-multipart code, it looks like I can
>>> use BodyPart.getEntityAs(MyMetaData.class) to access the XML meta
>>> data, and BodyPart.getEntity().getInputStream() to access the binary
>>> data. When I'm done, MultiPart.cleanup() will delete the temporary
>>> binary file (if > threshold bytes).  Am I reading that right?
>>>
>> That's exactly right.
>>> Also, I don't know if doing this on the server side will make it
>>> difficult for clients. You said the jersey-client can easily work
>>> with jersey-multipart. I've not yet looked into the Commons
>>> HttpClient or other client libraries to see if they have issues with
>>> making multipart requests.
>>>
>> You can also look at the Jersey client code itself.  One of the
>> really cool things about jersey-client is you can use the same
>> providers (including the jersey-multipart stuff) on the client just
>> like you can use it on the server.  There's some small samples of
>> this in the jersey-multipart unit tests.
>>
>>> Thanks,
>>>
>>> Jeff
>>>
>> Craig
>>
>>> On Oct 17, 2008, at 12:58 AM, Paul Sandoz wrote:
>>>
>>>> Hi Craig,
>>>>
>>>> Welcome to the list of Jersey committers :-)
>>>>
>>>> Thanks very much for the multipart MIME contribution, it looks
>>>> really good. I would like to leverage this to improve the support
>>>> for multipart/form-data and also consider support for Multipart
>>>> Java Beans.
>>>>
>>>> Classes supporting AtomPub support would be great!
>>>>
>>>> Re: the cleanup aspect. I think we need to review Jersey's IoC
>>>> support and integration capabilities with the likes of
>>>> Spring/Guice. It is really hard to be abstract from an IoC
>>>> framework :-( If the returned MultiPart was instantiated in the
>>>> life-cycle of the request then when the response is about to the
>>>> sent a pre destroy method could be called to do the clean up. That
>>>> would not work for the conditions of the client working standalone
>>>> (perhaps cleanup could also be called before being GC'ed although
>>>> that cannot be relied upon).
>>>>
>>>> Paul.
>>>>
>>>>
>>>> On Oct 17, 2008, at 1:00 AM, 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: [hidden email]
>>>>> <mailto:[hidden email]>
>>>>> For additional commands, e-mail: [hidden email]
>>>>> <mailto:[hidden email]>
>>>>>
>>>>
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: [hidden email]
>>>> <mailto:[hidden email]>
>>>> For additional commands, e-mail: [hidden email]
>>>> <mailto:[hidden email]>
>>>>
>>>
>>>
>>>
>>> --
>>> Jeff Schmidt
>>>
>>>
> --
>
> Jeff Schmidt
>
>
>
>
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Jeff Schmidt

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
Some javascript/style in this post has been disabled (why?)
Thanks for your thoughts on that Craig.  I feel better about going this route. :)

Cheers,

Jeff

On Oct 23, 2008, at 5:51 PM, Craig McClanahan wrote:

Jeff Schmidt wrote:
Thanks Craig! This all sounds good.

I guess my client side question was more generic. Potential clients may be using Java, Python, Ruby etc. and whatever related libraries. I don't know if issuing multi-part requests is unusual or difficult in general for such clients. I don't want to put up anymore barriers to entry to use the API then I have to. :)
Agreed -- this is one of the nicest things about REST.
What has been your experience in this regard? Any complaints from clients?

Constructing a multipart request is actually very easy, with pretty much any client language and HTTP framework.  From the point of view of such a framework, it's a single request of type "multipart/mixed" (or whatever), and the interesting part of the problem is constructing the message body.  For that, it's mostly a matter of alternating between a bunch of headers and content for each body part, with a little extra glue for the "boundary" string.  In jersey-client, the code that does this is in MultiPartWriter.java, and as you can see it is pretty straightforward (assuming your library already has a way to format the request body for your data entity -- which is the same code you'd need to send that body part by itself).

Parsing a multipart response, on the other hand, is somewhat more complicated.  In the case of jersey-multipart I punted :-) and used JavaMail APIs, because systems that understand mail streams already have to know how to do this.  If you use a different Java HTTP library you can still use JavaMail for this part; in other languages, I'd start by looking for parsing libraries that already exist, and then just glue them in to the client.

One of my upcoming tasks will be to create a Ruby client library for some of the services I'm working on, and that will ultimately include some multipart processing -- so I'll soon get to practice what I preach above :-).

Craig
Cheers,

Jeff

On Oct 23, 2008, at 1:10 PM, Craig McClanahan wrote:

Jeff Schmidt wrote:
Hi Craig:

Thanks for your work on jersey-multipart. I have some SOAP based endpoints where a request can consist of various XML meta data, and then there is an element of type base64Binary where an arbitrary file can be 'attached' to the request. I  would like to do something equivalent in a RESTful manner. It seems like a request comprised of an XML entity and a binary entity would be the way to go. The JAX-RS resource does not need to understand the binary entity, just make it available to some server-side code.
Yep, that is exactly the sort of thing I use it for.

Looking at some of the jersey-multipart code, it looks like I can use BodyPart.getEntityAs(MyMetaData.class) to access the XML meta data, and BodyPart.getEntity().getInputStream() to access the binary data. When I'm done, MultiPart.cleanup() will delete the temporary binary file (if > threshold bytes).  Am I reading that right?

That's exactly right.
Also, I don't know if doing this on the server side will make it difficult for clients. You said the jersey-client can easily work with jersey-multipart. I've not yet looked into the Commons HttpClient or other client libraries to see if they have issues with making multipart requests.

You can also look at the Jersey client code itself.  One of the really cool things about jersey-client is you can use the same providers (including the jersey-multipart stuff) on the client just like you can use it on the server.  There's some small samples of this in the jersey-multipart unit tests.

Thanks,

Jeff

Craig

On Oct 17, 2008, at 12:58 AM, Paul Sandoz wrote:

Hi Craig,

Welcome to the list of Jersey committers :-)

Thanks very much for the multipart MIME contribution, it looks really good. I would like to leverage this to improve the support for multipart/form-data and also consider support for Multipart Java Beans.

Classes supporting AtomPub support would be great!

Re: the cleanup aspect. I think we need to review Jersey's IoC support and integration capabilities with the likes of Spring/Guice. It is really hard to be abstract from an IoC framework :-( If the returned MultiPart was instantiated in the life-cycle of the request then when the response is about to the sent a pre destroy method could be called to do the clean up. That would not work for the conditions of the client working standalone (perhaps cleanup could also be called before being GC'ed although that cannot be relied upon).

Paul.


On Oct 17, 2008, at 1:00 AM, 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: [hidden email] <[hidden email]>
For additional commands, e-mail: [hidden email] <[hidden email]>



---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email] <[hidden email]>
For additional commands, e-mail: [hidden email] <[hidden email]>




--
Jeff Schmidt


--

Jeff Schmidt








---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]


--

Jeff Schmidt






Gili

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
In reply to this post by Craig McClanahan
Hi Craig,

On the topic of jersey-multipart:

1) Is it really Jersey-specific or can it be used with any JAX-RS implementation?
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.

Gili

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@jersey.dev.java.net
For additional commands, e-mail: users-help@jersey.dev.java.net
Robertson, Jeff

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
In reply to this post by Craig McClanahan
Some javascript/style in this post has been disabled (why?)
Re: [Jersey] Hello World! and Welcome to jersey-multipart

Isn't  javamail required to be provided by a JEE server? That could be a reason to use it instead of something you'd always have to bundle with the app.

Of course that only helps people deploying in a full blown server.

--------------------------
Sent using BlackBerry


----- Original Message -----
From: Gili <[hidden email]>
To: [hidden email] <[hidden email]>
Sent: Mon Nov 03 15:26:10 2008
Subject: Re: [Jersey] Hello World! and Welcome to jersey-multipart


Hi Craig,

On the topic of jersey-multipart:

1) Is it really Jersey-specific or can it be used with any JAX-RS
implementation?
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.

Gili


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: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>
>

--
View this message in context: http://n2.nabble.com/Hello-World%21-and-Welcome-to-jersey-multipart-tp1343189p1452012.html
Sent from the Jersey mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]


Robertson, Jeff

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
In reply to this post by Craig McClanahan
Some javascript/style in this post has been disabled (why?)
Re: [Jersey] Hello World! and Welcome to jersey-multipart

Also I'm sure Craig is familiar with commons fileupload since it is used in struts and (iirc) was a spinoff from struts in the first place.

--------------------------
Sent using BlackBerry


----- Original Message -----
From: Gili <[hidden email]>
To: [hidden email] <[hidden email]>
Sent: Mon Nov 03 15:26:10 2008
Subject: Re: [Jersey] Hello World! and Welcome to jersey-multipart


Hi Craig,

On the topic of jersey-multipart:

1) Is it really Jersey-specific or can it be used with any JAX-RS
implementation?
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.

Gili


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: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>
>

--
View this message in context: http://n2.nabble.com/Hello-World%21-and-Welcome-to-jersey-multipart-tp1343189p1452012.html
Sent from the Jersey mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]


Craig McClanahan

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
In reply to this post by Gili
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: [hidden email]
>> For additional commands, e-mail: [hidden email]
>>
>>
>>
>>    
>
>  


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Gili

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
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?

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.

Gili

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@jersey.dev.java.net
>> For additional commands, e-mail: users-help@jersey.dev.java.net
>>
>>
>>
>>    
>
>  


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@jersey.dev.java.net
For additional commands, e-mail: users-help@jersey.dev.java.net
Gili

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
Craig,

Here is what I am proposing:

Let "example header" refer to "Content-Type: application/xml; charset=UTF-8"

1a) BodyPart.getHeaders should return MultivaluedMap<String, MatrixParameters> instead of MultivaluedMap<String, String>. Where MatrixParameters extends Map<String, String>.

The example header would return MatrixParameters = {[Content-Type,application/xml], [charset, UTF-8]}

*or*

1b) BodyPart.getHeaders should return MultivaluedMap<String, HeaderValue> instead of MultivaluedMap<String, String>. Where HeaderValue defines two methods:

String HeaderValue.getValue() and
Map<String, String> HeaderValue.getAttributes()

The example header would return:

HeaderValue.getValue() = "application/xml"
HeaderValue.getAttributes() = "[charset, UTF-8]"


Let me know if you can think of a cleaner way to represent this...

2) Here is the algorithm I used to parse the matrix parameters:

        /**
         * Converts matrix parameters to key-value pairs.
         *
         * @param key the header name
         * @param value the header value
         * @return the matrix parameters
         */
        private Map<String, String> getMatrixParameters(String key, String value)
        {
                Map<String, String> result = new HashMap<String, String>();
                String[] pairs = value.split(";");
                if (pairs.length > 0)
                        result.put(key, unquoted(pairs[0]));
                for (int i = 1, size = pairs.length; i < size; ++i)
                {
                        String[] tokens = pairs[i].split("=");
                        assert (tokens.length == 2): Arrays.toString(tokens);
                        result.put(tokens[0], unquoted(tokens[1]));
                }
                return result;
        }

        /**
         * Removes any quotes surrounding the input text.
         *
         * @param text input text
         * @return unquoted text
         */
        private String unquoted(String text)
        {
                text = text.trim();
                if (text.charAt(0) == '\"' && text.charAt(text.length() - 1) == '\"')
                        return text.substring(1, text.length() - 1);
                return text;
        }

3) Modify the package name so it's obvious that this implementation isn't specific to Jersey (I think it's okay that the tests are Jersey-specific) and publish it on JAX-RS's mailing list in the hopes of getting other people to use and support it.

It looks to me like pretty much all major JAX-Rs implementations have built-in MimeMultipart (from javax.mail) providers but this API is not as easy to use as it should be. The value proposition of your provider over those implementations would be a cleaner, more modern API.

What do you think?

Gili
Gili

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
The more I look at Commons FileUpload, the more I think it does everything we need. They provide a better parser than my own: http://commons.apache.org/fileupload/apidocs/org/apache/commons/fileupload/ParameterParser.html

Clearly my code is mishandling quotes. Also I noticed that they mention multipart/mixed here: http://commons.apache.org/fileupload/apidocs/org/apache/commons/fileupload/portlet/PortletFileUpload.html though it's not clear if other components support multipart/mixed properly.

I'm not a fan of their API (and their documentation is full of typos) but it looks like it does the job from an implementation point of view. Perhaps we can throw it underneath your API.

Gili

Gili wrote:
Craig,

Here is what I am proposing:

Let "example header" refer to "Content-Type: application/xml; charset=UTF-8"

1a) BodyPart.getHeaders should return MultivaluedMap<String, MatrixParameters> instead of MultivaluedMap<String, String>. Where MatrixParameters extends Map<String, String>.

The example header would return MatrixParameters = {[Content-Type,application/xml], [charset, UTF-8]}

*or*

1b) BodyPart.getHeaders should return MultivaluedMap<String, HeaderValue> instead of MultivaluedMap<String, String>. Where HeaderValue defines two methods:

String HeaderValue.getValue() and
Map<String, String> HeaderValue.getAttributes()

The example header would return:

HeaderValue.getValue() = "application/xml"
HeaderValue.getAttributes() = "[charset, UTF-8]"


Let me know if you can think of a cleaner way to represent this...

2) Here is the algorithm I used to parse the matrix parameters:

        /**
         * Converts matrix parameters to key-value pairs.
         *
         * @param key the header name
         * @param value the header value
         * @return the matrix parameters
         */
        private Map<String, String> getMatrixParameters(String key, String value)
        {
                Map<String, String> result = new HashMap<String, String>();
                String[] pairs = value.split(";");
                if (pairs.length > 0)
                        result.put(key, unquoted(pairs[0]));
                for (int i = 1, size = pairs.length; i < size; ++i)
                {
                        String[] tokens = pairs[i].split("=");
                        assert (tokens.length == 2): Arrays.toString(tokens);
                        result.put(tokens[0], unquoted(tokens[1]));
                }
                return result;
        }

        /**
         * Removes any quotes surrounding the input text.
         *
         * @param text input text
         * @return unquoted text
         */
        private String unquoted(String text)
        {
                text = text.trim();
                if (text.charAt(0) == '\"' && text.charAt(text.length() - 1) == '\"')
                        return text.substring(1, text.length() - 1);
                return text;
        }

3) Modify the package name so it's obvious that this implementation isn't specific to Jersey (I think it's okay that the tests are Jersey-specific) and publish it on JAX-RS's mailing list in the hopes of getting other people to use and support it.

It looks to me like pretty much all major JAX-Rs implementations have built-in MimeMultipart (from javax.mail) providers but this API is not as easy to use as it should be. The value proposition of your provider over those implementations would be a cleaner, more modern API.

What do you think?

Gili
Craig McClanahan

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
In reply to this post by Gili
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: [hidden email]
>>>> For additional commands, e-mail: [hidden email]
>>>>
>>>>
>>>>
>>>>    
>>>>        
>>>  
>>>      
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: [hidden email]
>> For additional commands, e-mail: [hidden email]
>>
>>
>>
>>    
>
>  


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Gili

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
Craig McClanahan wrote:
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?
That sounds exactly like the kind of thing I was looking for.

BTW, I just discovered this related discussion: http://forums.sun.com/thread.jspa?threadID=5333195
This, in turn, led me to javax.mail.internet.ContentDisposition and more importantly javax.mail.internet.HeaderTokenizer. We should be able to use these fairly easily to implement the aforementioned APIs.

Craig McClanahan wrote:
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.
Why do you need to support non-servlet deployments?

Craig McClanahan wrote:
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.
As soon as we start talking about class loaders things get ugly real quick. Ideally I'd like to cheat by simplifing the requirements if possible ;)

Craig McClanahan wrote:
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.
FileUpload has a background thread that monitors when its File instances get garbage-collected (Guice does something similar) and when this happens it goes and deletes the files off the hard-drive automatically. It also uses a Servlet Filter to hook servlet shutdown to force immediate deletion of all outstanding files. At least, that's what I understood from their Javadoc.

Craig McClanahan wrote:
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).
The only reason I suggested looking at FileUpload is I thought it was an active project that it handles temporary files and configuration under the hood for us. Now I'm not so sure anymore. If you believe that adding their configuration on top of Javamail isn't much hassle I think I'd prefer that than relying on their code. I say this because I noticed that Javamail has been open-sourced and is now part of Glassfish and I trust those guys a heck of a lot more than I do your average Apache project.

My main concern now is stability and simplicity. I guess I no longer care about the underlying JAR size ;)

Gili
Craig McClanahan

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
Some javascript/style in this post has been disabled (why?)
Gili wrote:
Craig McClanahan wrote:
  
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?

    

That sounds exactly like the kind of thing I was looking for.

  
Cool.  I love it when great minds think alike :-).
BTW, I just discovered this related discussion:
http://forums.sun.com/thread.jspa?threadID=5333195
This, in turn, led me to javax.mail.internet.ContentDisposition and more
importantly javax.mail.internet.HeaderTokenizer. We should be able to use
these fairly easily to implement the aforementioned APIs.

  
Ugh ... that (parsing MIME headers) is something else that I didn't have to worry about when I was "constructively lazy" and depended on JavaMail for parsing.  We'd definitely need to build something on top of the MultipartStream from Commons FileUpload, since that only gives you body parts as a whole, and doesn't deal with the headers.
Craig McClanahan wrote:
  
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.

    

Why do you need to support non-servlet deployments?

  
Two reasons:

* On the server side, JAX-RS explicitly declares support for non-servlet deployments
  (although lots of details are left as "exercise for the implementor" in the 1.0 spec).
  There is no a priori reason I can think of that we should restrict multipart support to
  only work for servlet based server side deployments.

* A lot of my use cases involve *client* applications using RESTful web services.
  Jersey has a very nice client API that can leverage much of the JAX-RS infrastructure
  on the client side as well, and it would be really weird to "import javax.servlet.*" into
  an applet, or a RIA app based on Swing or Java FX.

Craig McClanahan wrote:
  
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.

    

As soon as we start talking about class loaders things get ugly real quick.
Ideally I'd like to cheat by simplifing the requirements if possible ;)


  
I know that pain :-), having been pretty heavily involved in the servlet container inside Tomcat from version 4.x -- the basic architecture today is pretty similar although quite refined.  But let me state a requirement I think we must satisfy in a different way:  it must be possible to deploy two or more different server apps, each using jersey-multipart, in the same instance of a servlet container, with different configuration settings for things like the threshold size before a body part gets spooled to a disk file.

The algorithm I described above is quite typical of the way your average web framework (including Struts) locates application specific resources.  Fortunately, we can make it work transparently in a non-servlet world too, by using things like ClassLoader.getResourceAsStream() instead of ServletContext.getResourceAsStream().
Craig McClanahan wrote:
  
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.

    

FileUpload has a background thread that monitors when its File instances get
garbage-collected (Guice does something similar) and when this happens it
goes and deletes the files off the hard-drive automatically. It also uses a
Servlet Filter to hook servlet shutdown to force immediate deletion of all
outstanding files. At least, that's what I understood from their Javadoc.

  
I'm not usually a fan of "background thread cleanup" type solutions because they are (a) asynchronous, and therefore consume resources for longer than they should be consumed, and (b) they encourage sloppy coding -- "someone else will clean up my mess, so I don't have to worry about it."  It might not be quite so bad in this use case, although we'd need to again deal separately with the servlet versus non-servlet case (servlet based temp files should be cleaned up when the app is undeployed, not just when the container is shut down), but that might be feasible.

Unless performance considerations dictate otherwise, I'm much more into synchronously cleaning up after myself after each request.  But it's definitely not optimal to make the app developer responsible for ensuring that this happens.
Craig McClanahan wrote:
  
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).

    

The only reason I suggested looking at FileUpload is I thought it was an
active project that it handles temporary files and configuration under the
hood for us. Now I'm not so sure anymore. If you believe that adding their
configuration on top of Javamail isn't much hassle I think I'd prefer that
than relying on their code. I say this because I noticed that Javamail has
been open-sourced and is now part of Glassfish and I trust those guys a heck
of a lot more than I do your average Apache project.

  
I'm afraid I'm biased both ways so can't help you much in determining who to trust more.

I work for Sun (originally in the J2EE (now Java EE) group where Glassfish now comes from), and have also been heavily involved in many Apache projects -- most particularly Tomcat and Struts, the latter being where many of the Commons projects got a lot of initial developers and initial codebases.

I trust them both :-).
My main concern now is stability and simplicity. I guess I no longer care
about the underlying JAR size ;)

  
+1 for stability and simplicity.  But this is a case where the "reuse" argument might tends towards fork-and-copy-a-few-classes (with appropriate attributions, of course, to satisfy the relevant open source licenses), rather than importing reasonably large packages when we're only using a few classes from them.

On the other hand, the JavaMail based support for parsing multipart messages "just works" ...
Gili
  
Craig

Craig McClanahan

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
In reply to this post by Robertson, Jeff
Some javascript/style in this post has been disabled (why?)
Robertson, Jeff wrote:
Re: [Jersey] Hello World! and Welcome to jersey-multipart

Isn't  javamail required to be provided by a JEE server? That could be a reason to use it instead of something you'd always have to bundle with the app.

JavaMail is indeed required to be available for a J2EE (older) or Java EE (newer) app server.

Of course that only helps people deploying in a full blown server.

That's only true for an out-of-the-box deployment of something like Tomcat or Jetty.

Every servlet container I'm aware of provides a mechanism for you to add JAR files to the set of libraries that are made available to *all* webapps that are deployed on that container.  For example, on Tomcat 4.x/5.x, just put javamail.jar and jaf.jar into "$TOMCAT_HOME/common/lib".  On Tomcat 6.x, use "$TOMCAT_HOME/lib" instead.  You'll find some similar capability for all the other containers too.

Craig

Paul Sandoz

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
In reply to this post by Craig McClanahan
Some javascript/style in this post has been disabled (why?)

On Nov 4, 2008, at 9:06 AM, Craig McClanahan wrote:
BTW, I just discovered this related discussion:
http://forums.sun.com/thread.jspa?threadID=5333195
This, in turn, led me to javax.mail.internet.ContentDisposition and more
importantly javax.mail.internet.HeaderTokenizer. We should be able to use
these fairly easily to implement the aforementioned APIs.

  
Ugh ... that (parsing MIME headers) is something else that I didn't have to worry about when I was "constructively lazy" and depended on JavaMail for parsing.  We'd definitely need to build something on top of the MultipartStream from Commons FileUpload, since that only gives you body parts as a whole, and doesn't deal with the headers.

Yes, for the @FormParam multipart support in Jersey (which we really need to clean up and defer to the mail contribs support) i had to parse the content disposition:

    private static Map<String, BodyPart> getFormData(MimeMultipart mm) throws Exception {
        Map<String, BodyPart> m = new HashMap<String, BodyPart>();
        
        for (int i = 0; i < mm.getCount(); i++) {
            BodyPart b = mm.getBodyPart(i);
            if (b.getDisposition() != null && 
                    b.getDisposition().equalsIgnoreCase("form-data")) {
                String name = getName(b.getHeader("content-disposition")[0]);
                if (name != null)
                    m.put(name, b);
            }
        }
        return m;
    }

    private static String getName(String disposition) throws ParseException {
        HttpHeaderReader reader = new HttpHeaderReaderImpl(disposition);
        // Skip any white space
        reader.hasNext();

        // Get the "form-data"
        reader.nextToken();

        while (reader.hasNext()) {
            reader.nextSeparator(';');
            
            // Ignore a ';' with no parameters
            if (!reader.hasNext())
                break;
            
            // Get the parameter name
            String name = reader.nextToken();
            reader.nextSeparator('=');
            // Get the parameter value
            String value = reader.nextTokenOrQuotedString();
            if (name.equalsIgnoreCase("name")) {
                return value;
            }
        }
        return null;
    }

I am reusing the HTTP header parsing code for parsing the MIME header. There are some close similarities between HTTP headers and MIME headers but there may also be some subtle differences that results failure for edge cases.



Craig McClanahan wrote:
  
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.

    
Why do you need to support non-servlet deployments?

  
Two reasons:

* On the server side, JAX-RS explicitly declares support for non-servlet deployments
  (although lots of details are left as "exercise for the implementor" in the 1.0 spec).
  There is no a priori reason I can think of that we should restrict multipart support to
  only work for servlet based server side deployments.

* A lot of my use cases involve *client* applications using RESTful web services.
  Jersey has a very nice client API that can leverage much of the JAX-RS infrastructure
  on the client side as well, and it would be really weird to "import javax.servlet.*" into
  an applet, or a RIA app based on Swing or Java FX.


We could do the following:

1) Have a class in jersey core called FeatureAndProperties;

2) The ClientConfig and ResourceConfig extend this class.

3) FeaturesAndProperties can be injected.

Then message body readers/writers requiring access to config information are independent of client/server.


Craig McClanahan wrote:
  
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.

    
As soon as we start talking about class loaders things get ugly real quick.
Ideally I'd like to cheat by simplifing the requirements if possible ;)


  
I know that pain :-), having been pretty heavily involved in the servlet container inside Tomcat from version 4.x -- the basic architecture today is pretty similar although quite refined.  But let me state a requirement I think we must satisfy in a different way:  it must be possible to deploy two or more different server apps, each using jersey-multipart, in the same instance of a servlet container, with different configuration settings for things like the threshold size before a body part gets spooled to a disk file.

The algorithm I described above is quite typical of the way your average web framework (including Struts) locates application specific resources.  Fortunately, we can make it work transparently in a non-servlet world too, by using things like ClassLoader.getResourceAsStream() instead of ServletContext.getResourceAsStream().
Craig McClanahan wrote:
  
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.

    
FileUpload has a background thread that monitors when its File instances get
garbage-collected (Guice does something similar) and when this happens it
goes and deletes the files off the hard-drive automatically. It also uses a
Servlet Filter to hook servlet shutdown to force immediate deletion of all
outstanding files. At least, that's what I understood from their Javadoc.

  
I'm not usually a fan of "background thread cleanup" type solutions because they are (a) asynchronous, and therefore consume resources for longer than they should be consumed, and (b) they encourage sloppy coding -- "someone else will clean up my mess, so I don't have to worry about it."  It might not be quite so bad in this use case, although we'd need to again deal separately with the servlet versus non-servlet case (servlet based temp files should be cleaned up when the app is undeployed, not just when the container is shut down), but that might be feasible.

Unless performance considerations dictate otherwise, I'm much more into synchronously cleaning up after myself after each request.  But it's definitely not optimal to make the app developer responsible for ensuring that this happens.


I have made changes to the life-cycle management of Jersey to support @PostConstruct and @PreDestroy of resource classes (plan to do the same for providers). What we could support is a way for the mail support to register the multi-part instance it creates for clean up when the request/response is done. One way to do this is to support a @PerInstance life-cycle and the mail support asks Jersey to create an instance of the MultiPart which will get destroyed when the request goes out of scope. It should also work for Jersey clients working within the server environment.

There is an issue with isolated clients though, and may be that is where a thread clean up solution is appropriate and we may be able to hide that functionality through different life-cycle implementation support.

This does of course mean that the mail support is dependent on Jersey specific APIs.



Craig McClanahan wrote:
  
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).

    
The only reason I suggested looking at FileUpload is I thought it was an
active project that it handles temporary files and configuration under the
hood for us. Now I'm not so sure anymore. If you believe that adding their
configuration on top of Javamail isn't much hassle I think I'd prefer that
than relying on their code. I say this because I noticed that Javamail has
been open-sourced and is now part of Glassfish and I trust those guys a heck
of a lot more than I do your average Apache project.

  
I'm afraid I'm biased both ways so can't help you much in determining who to trust more.

I work for Sun (originally in the J2EE (now Java EE) group where Glassfish now comes from), and have also been heavily involved in many Apache projects -- most particularly Tomcat and Struts, the latter being where many of the Commons projects got a lot of initial developers and initial codebases.

I trust them both :-).
My main concern now is stability and simplicity. I guess I no longer care
about the underlying JAR size ;)

  
+1 for stability and simplicity.  But this is a case where the "reuse" argument might tends towards fork-and-copy-a-few-classes (with appropriate attributions, of course, to satisfy the relevant open source licenses), rather than importing reasonably large packages when we're only using a few classes from them.

On the other hand, the JavaMail based support for parsing multipart messages "just works" ...

I would be inclined to build on JavaMail and take the fork-and-copy approach. It should be possible to extend JavaMail for multipart/form-data then the Jersey layers above that becomes easier to support.

Paul.
Gili

Re: Hello World! and Welcome to jersey-multipart

Reply Threaded More More options
Print post
Permalink
In reply to this post by Craig McClanahan
Craig McClanahan wrote:
Ugh ... that (parsing MIME headers) is something else that I didn't have
to worry about when I was "constructively lazy" and depended on JavaMail
for parsing.  We'd definitely need to build something on top of the
MultipartStream from Commons FileUpload, since that only gives you body
parts as a whole, and doesn't deal with the headers.
Correct me if I'm wrong, but it doesn't look like parsing the headers is difficult at all. I believe that HeaderTokenizer takes care of the quotes and internal whitespace for you. All that's left for you to do is tell it to scan for semi-colons and equal-signs, take the parser output and pass it into HeaderType.

Gili
1 2 3