Monday, February 2, 2009

Multiparts in CXF JAXRS

We've just had CXF JAXRS updated to deal with multiparts. Multipart/related, multipart/alternative, multipart/mixed and multipart/form-data are supported at the moment.

The reason I've decided to blog about it is that I was fascinated how little work I had actually to do to make it all happen. CXF JAXRS sits on top of the robust CXF runtime core component, which, among other things, has a great support for (de)serializing multipart requests, with the ability for attachments exceeding a given configurable memory threshold be saved into a configurable directory.

A number of options is available for developers wishing to handle multipart requests. First we tried to make sure that JAXWS users trying JAXRS can reuse the same code they use when dealing with attachments in JAXWS. Particularly, one can get a root attachment bound to a method parameter, while the subordinate ones can later be retrieved for further processing :


public class Resource {
@Context
private MessageContext cxfJaxrsContext;

@POST
@Path("/")
public void handle(StreamSource root) {
// check the root one...
// get to the rest of them
Map<String, DataHandler> parts =
AttachmentUtils.getChildAttachmentsAsMap(mc);
handleParts(parts);
}
}


The root attachment can be bound to any type which can be supported by registered providers :


public class Resource {
@Context
private MessageContext cxfJaxrsContext;

@POST
@Path("/")
public void handle(JaxbBook book) {
// book represents the root one
List<Attachment> parts = AttachmentUtils.getChildAttachments(mc);
}

@POST
@Path("/")
public void handle2(JSONBook book) {
// book represents the root one
List<Attachment> parts = AttachmentUtils.getChildAttachments(mc);
}

@POST
@Path("/")
public void handle3(MultipartBody body) {
Attachment a = body.getRootAttachment();
List<Attachment> parts = body.getChildAttachments();
}

@POST
@Path("/")
public void handle4(List<InputStream> all) {
}

@POST
@Path("/")
public void handle5() {
Attachment a = AttachmentUtils.getRootAttachment(mc);
List<Attachment> parts = AttachmentUtils.getChildAttachments(mc);
AttachmentUtils.getAllAttachments(mc);
}
}


One can address individual parts like this :


public class Resource {
@Context
private MessageContext cxfJaxrsContext;

@POST
@Path("/")
public void handle(@Multipart(value="id1",
type="application/json") JSONBook book,
@Multipart(value="id2") JaxbBook book) {

}
}


For multipart/form-data, FormParams can be used to bind to individual parts representing form fields. MultipartMap is supported. One limitation at the moment is that with multipart/form-data, parts can only be bound to Strings (which can further be processed as per the JAXRS parameter handling rules if FormParam is used, with CXF JAXRS ParameterHandler being of likely help here). Thus if one wants to upload files then at the moment one needs to use MultipartBody and do some custom processing :


public class Resource {

@POST
@Path("/")
@Consumes("multipart/form-data")
public void handle(MultipartBody body) {
// presumes the part has a content id
Attachment a = body.getAttachmentById("file");
ContentDisposition cd = a.getContentDisposition();
InputStream is = a.getDataHandler().getInputStream();
// proceed
}
}


Configuring the attachments directories and memory thresholds can be done on a per jaxrs:endpoint from Spring, setting corresponding bean properties on a CXF JAXRS provider dealing with multiparts. Or you can do it from your code if you wish :


public class Resource {

@POST
@Path("/")
public void handle() {
List<Attachment> all =
AttachmentUtils.getAllAttachments(mc,
"/tmp", "589678")
}
}


Outbound attachments will also be supported. Give it a go and let us know what you think.

6 comments:

Arul said...

Sergey,

Great to see multipart support in CXF JAXRS. It works really cool. I just started playing with it and blogged about my first experience with multiparts in CXF (http://aruld.info/handling-multiparts-in-restful-applications-using-cxf/).

-Arul

Sergey Beryozkin said...

Hi Arul

Very appreciated - thanks for giving it a try ...

Cheers, Sergey

Anonymous said...

I am using CXF 2.1.x in production and need support to handle multipart posts. Whats the best way to emulate that in 2.1.x?

Neale said...

Just been looking around multipart support for JAX-RS, and it seems that it'd be rather good to get some heads together and get support included as part of the standard. There's some great ideas out there, but... they're different :)

Anonymous said...

Why is multipart support not part of the JAX-RS specification?

dmanohar said...

Hi,

The post is very good, I have a scenario trying to see how to handle it.
I have a html page, and trying to upload a file whatever user selects.
Can you give me some pointers on how to handle this.

the server is cxf Jax-rs service.