JAX-RS Multipart support with RESTEasy

7 Comments

I just finished implementing some “multipart/*” and multipart/form-data support within RESTEasy.  It will be out with the next release.  Highlights?

A JAX-RS compatable API

Our current support for multipart is through the MimeMultipart classes within the Java Mail library.  They are limited because they do not use the MessageBodyReader/Writers available in JAX-RS.  I have written two parallel APIs that provide multipart support in a JAX-RS way:

package org.jboss.resteasy.plugins.providers.multipart;

public interface MultipartInput {
   List<InputPart> getParts();
   String getPreamble();
}

public interface InputPart {
   MultivaluedMap<String, String> getHeaders();
   String getBodyAsString();
   <T> T getBody(Class<T> type, Type genericType) throws IOException;
   <T> T getBody(org.jboss.resteasy.util.GenericType<T> type) throws IOException;
   MediaType getMediaType();
}

MultipartInput is a simple interface that allows you to get access to each part of the multipart message. Each part is represented by an InputPart interface. Each part has a set of headers associated with it.  You can unmarshall the part by calling one of the getBody() methods. The Type genericType parameter can be null, but the Class type parameter must be set. Resteasy will find a MessageBodyReader based on the media type of the part as well as the type information you pass in. The following piece of code is unmarshalling parts which are XML into a JAXB annotated class called Customer.

   @Path("/multipart")
   public class MyService   {
      @PUT
      @Consumes("multipart/mixed")
      public void put(MultipartInput input)
      {
         List<Customer> customers = new ArrayList...;
         for (InputPart part : input.getParts())
         {
            Customer cust = part.getBody(Customer.class, null);
            customers.add(cust);
         }
      }
   }

There’s a similar API for outputing multipart.

Multipart with vanilla Lists and Maps

Another feature I’ve added is the ability to use regular instances of java.util.List(any multipart format) or Map (form-data only) to represent multipart data.  Its only usable when your parts are uniform though.

Here’s an example of using it:

parameter of the List type declaration. Here’s an example again of unmarshalling a list of customers.

   @Path("/multipart")
   public class MyService
   {
      @PUT
      @Consumes("multipart/mixed")
      public void put(List<Customer> customers)
      {
         ...
      }
   }

That’s using input.  The problem with output is that RESTEasy has no idea what mime type you want to marshal or List or Map into.  So, the @PartType annotation is used.  Here’s an example of outputing multipart/form-data with a map:

@Path("/multipart")
   public class MyService
   {
      @GET
      @Produces("multipart/form-data")
      @PartType("application/xml")
      public Map<String, Customer> get()
      {
         ...
      }

Multipart-Formdata with POJOs

Finally, I added the ability to map POJO form classes to multipart/form-data.  You re-use @FormParam on fields and/or properties of a user-provided POJO.  Here’s an example:

   public class CustomerProblemForm {
      @FormData("customer")
      @PartType("application/xml")
      private Customer customer;

      @FormData("problem")
      @PartType("text/plain")
      private String problem;

      public Customer getCustomer() { return customer; }
      public void setCustomer(Customer cust) { this.customer = cust; }
      public String getProblem() { return problem; }
      public void setProblem(String problem) { this.problem = problem; }
   }

After defining your POJO class you can then use it to represent multipart/form-data. Here’s an example of sending a CustomerProblemForm using the RESTEasy client framework

   @Path("portal")
   public interface CustomerPortal {

      @Path("issues/{id}")
      @Consumes("multipart/form-data")
      @PUT
      public void putProblem(@MultipartForm CustomerProblemForm,
                             @PathParam("id") int id);
   }

   {
       CustomerPortal portal = ProxyFactory.create(CustomerPortal.class, "http://example.com");
       CustomerProblemForm form = new CustomerProblemForm();
       form.setCustomer(...);
       form.setProblem(...);

       portal.putProblem(form, 333);
   }

You see that the @MultipartForm annotation was used to tell RESTEasy that the object has @FormParam and that it should be marshalled from that. You can also use the same object to receive multipart data. Here is an example of the server side counterpart of our customer portal.

   @Path("portal")
   public class CustomerPortalServer {

      @Path("issues/{id})
      @Consumes("multipart/form-data")
      @PUT
      public void putIssue(@MultipartForm CustoemrProblemForm,
                           @PathParm("id") int id) {
         ... write to database...
      }
   }

JAX-RS + Asynch HTTP

10 Comments

A RESTEasy user (and JBoss customer) asked about 6 weeks ago if we were planning on supporting Resteasy with Asynchronous HTTP.  For those of you not familiar with Asynchronous HTTP, take a look at Servlet 3.0, Tomcat Comet APIs, or Jetty’s continuations.  I decided to add some limited support after doing some research.

The primary (and IMO, the only) usecase for Asynchronous HTTP is in the case where the client is polling the server for a delayed response.  The usual example is an AJAX chat client where you want to push/pull from both the client and the server.  These scenarios have the client blocking a long time on the server’s socket waiting for a new message.  What happens in synchronous HTTP is that you end up having a thread consumed per client connection.  This eats up memory and valuable thread resources.  No such a big deal in 90% of applications, but when you start getting a lot of concurrent clients that are blocking like this, there’s a lot of wasted resources.

As for Resteasy and asynchronous HTTP, I don’t want to create a COMET server.  Maybe I’m totally wrong, but it seems that the idea of COMET is to use HTTP solely for the purpose of initiating a dedicated socket connection to your server.  After the initial connection is made, the client and server just tunnel their own message protocol across the socket for any number of requests.

Now, this isn’t necessarily a bad idea.  Its actually a good one for performance reasons.  As a JBossian David Lloyd explained to me in private email, what this API allows you to do is create a direct connection to your service so that you don’t have to redispatch with every message that is exchange from the client and server.  Sure, HTTP Keep Alive allows you to avoid re-establishing the socket connection, but you don’t have to worry about all the other path routing logic to get the message to the Java object that services the request.

Still, this is something I don’t want or need to provide through RESTEasy.  Its really using HTTP to tunnel a new protocol and something that is very unRESTful to me.  Basically it requires that the client knows how to communicate the COMET protocol, which IMO, makes ubiquity very hard.  Besidesk, Tomcat, JBoss Web, and Jetty will probably do a fine job here.  There are also already a number of COMET servers available on the web.  No, I will focus on giving asynchronous HTTP features to a pure HTTP and JAX-RS request.

What I will initially provide through RESTEasy is a very simple callback API.

@Path("/myresource")
@GET
public void getMyResource(@Suspend AsynchronousResponse response) {

   ... hand of response to another thread ...
}

public interface AsynchronousResponse {

   void setResponse(javax.ws.rs.core.Response response);

}

The @Suspend annotation tells Resteasy that the HTTP request/response should be detached from the currently executing thread and that the current thread should not try to automaticaly process the response.  The AsynchronousResponse is the callback object.  It is injected into the method by Resteasy.  Application code hands off the AsynchronousResponse to a different thread for processing.  The act of calling setResponse() will cause a response to be sent back to the client and will also terminate the HTTP request.

Servlet 3.0 has asynchronou APIs.  When you suspend a request, the request may be redelivered when a timeout occurs.  I don’t want to have a Resteasy replacement for these APIs.  What I want is something that complements these APIs and makes a specific usecase easier to write.  The use case I want to solve is detaching the processing of an HTTP response from the current thread of execution so that another thread can do the work when ready.  That’s it.  So, there will be no redelivery of a request.

Initially I plan to work with the asynchronous apis within the Tomcat 6.0 NIO transport as this is what is distributed with JBoss 4.2.3.  Next I plan to work with the JBoss Web asynchronous apis, then Jetty 6, then finally with a Servlet 3.0 implementation.

If you have any ideas on this subject, please let me know.  I can work them into the prototype I’m developing.

RESTEasy DZone articles and Beta 8

6 Comments

If you’re wondering why I haven’t done a signficant blog since June, wonder no more.  Instead of creating content for my blog, I’ve written a couple of articles for DZone.com:

Also, today I put out RESTEasy 1.0 Beta 8.  I small tiny bug was found by the community which I rolled up quick into a release.  Follow the downloads link at http://jboss.org/resteasy to check it out.

Caching, Parallelism, and Scalability

Leave a comment

Manik Surtani has written a great article on Caching, Parallelism, and Scalability.  Check it out.

RESTEasy 1.0-beta-7: quick bug fixes

2 Comments

A quick release from our beta-6 to fix a few quick bugs that users found.  See our main project page at:

http://jboss.org/resteasy

to download and browse docs.  Have fun.

RESTEasy 1.0 Beta 5 Released

1 Comment

Customer bug fixes and new features in this release. We’ve implemented some JSON support after some users requested this feature. Using the Jettison Framework, you can use JAXB annotated classes and marshal them to and from JSON. A pretty cool framework and easily integrated into RESTEasy JAX-RS. The RESTEasy JAX-RS documentation has also been updated to reflect changes and functionality updates.

Please see our main project page to learn how to download, post questions, etc.

Release Notes – Resteasy – Version Beta 5

Bug

  • [RESTEASY-47] – Autoscan thinks a Subresource is a root resource, result is error
  • [RESTEASY-50] – Client framework throws exception if anything other than OK is returned as a response code
  • [RESTEASY-53] – DELETE/POST with no entity returns 200 should return 204 (NO_CONTENT)

Feature Request

  • [RESTEASY-52] – JSON/JAXB and raw JSON @Provider support
  • [RESTEASY-54] – ClientResponse object for ClientFramework
  • [RESTEASY-55] – Allow return of Response.Status in client proxy framework
  • [RESTEASY-56] – Allow turning off of default built-in @Provider classes

Task

  • [RESTEASY-49] – Support ExceptionHandlers
  • [RESTEASY-57] – resteasy.providers should not turn off builtin

Speaking Thursday, June 12th NEJUG: REST and JAX-RS

Leave a comment

I’ll be speaking this Thursday, June 12th at the Boston area JUG (NEJUG) at the Sun campus in Burlington at 6:00 pm. My talk will be on on REST and how the new JAX-RS specification makes it easier for you to write RESTFul Web Services in Java. Please stop by, at least to say hi. Hecklers welcome too!

Resteasy JAX-RS Beta 4 Released

1 Comment

I’m trying to release much earlier and more often. This release brings RESTEasy JAX-RS up to the current public draft of JAX-RS, JSR-311, minus a few methods that are being debated upon within specification. I didn’t want to implement something where the behavior is going to change. The RESTEasy JAX-RS documentation has also been updated to reflect changes and functionality updates.

Please see our main project page to learn how to download, post questions, etc.

Release Notes – Resteasy – Version Beta4

Bug

  • [RESTEASY-28] – if a class or super class is annotated, then ignore any interface’s annotations
  • [RESTEASY-34] – Locator method and resource method with same URI does not dispatch
  • [RESTEASY-45] – You should be able to use multiple path params per uri segment
  • [RESTEASY-46] – Subresource class annotated via its interface does not work

Task

  • [RESTEASY-3] – Implement UriInfo.getAncestorResources and UriInfo.getAncestorResourcesURIs
  • [RESTEASY-7] – Implement MessageBodyWorkers
  • [RESTEASY-21] – Support for @Path.encoded
  • [RESTEASY-22] – CacheControl marshalling/delegate
  • [RESTEASY-30] – Implement encoding in UriBuilder
  • [RESTEASY-32] – Implement @Encoded support
  • [RESTEASY-35] – Support for javax.security annotations
  • [RESTEASY-37] – Update API to Public Draft
  • [RESTEASY-38] – Implement UriBuilder.extension()
  • [RESTEASY-41] – Implement Httpheaders.getAcceptableLanguages() and getRequestHeader()
  • [RESTEASY-42] – Support for MediaType and Language file suffix extension mappings
  • [RESTEASY-43] – Test servlet init-param parsing of map extensions
  • [RESTEASY-44] – Support for ApplicationConfig

Resteasy Project: JAX-RS Restful Web Services implementation

12 Comments

I’m pleased to announce the first beta release of the JBoss Resteasy JAX-RS implementation!

After about 4 months of on and off development, I finally have a working version of the new JSR-311, JAX-RS specification. For those of you who don’t know, JSR-311 is a RESTFul Web Services implementation for Java and is slated to be included within Java EE 6. The spec is still a bit of a moving target, but its still pretty useful. Let me know what you think!

Features:

  • JAX-RS implementation (almost full, still have a few minor things here and there)
  • Portable to any app-server/Tomcat that runs on JDK 5 or higher
  • EJB 3.0 and Spring integration
  • Client framework to make writing HTTP clients easy (JAX-RS only define server bindings)

Where can I find out more?

All information, including where to download it, is available on our Resteasy WIKI.

Scannotation fix for /WEB-INF/classes

1 Comment

I have a confession.  I didn’t really test the code that allowed you to scan /WEB-INF/classes for my Scannotation project.  In the old code, if you run within Tomcat, you’ll get a “jndi:” protocol based URL that my code doesn’t understand yet how to scan

Instead, I obtain the URL by doing a ServletContext.getRealPath(“/WEB-INF/classes”).  Unfortunately, this is not guaranteed to work by the specification, so, if you run into this problem, you’re gonna have to find another way to scan this directory portably.

I’ve released a version 1.02 of Scannotation that has this RealPath fix along with a few other minor bug fixes I found while using the library.

Older Entries Newer Entries