添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Search

RESTEasy Reference Guide

download PDF
JBoss Enterprise Application Platform 5

for use with JBoss Enterprise Application Platform 5

Edition 5.2.0

Eva Kopalova

Petr Penicka

Russell Dickenson

Scott Mumford

Abstract

A reference guide to RESTEasy for use with JBoss Enterprise Application Platform 5 and its patch releases.

Chapter 1. Overview

JSR-000311 JAX-RS is a Java Community Process (JCP) specification that provides a Java API for RESTful Web Services over the HTTP protocol. RESTEasy is a portable implementation of the JAX-RS specification that can run in any Servlet container, and integrates tightly with the JBoss Enterprise Application Platform (EAP) to provide improved user experience in the EAP environment. Where JAX-RS is a server-side only specification, RESTEasy brings JAX-RS functions to the client side through the RESTEasy JAX-RS Client Framework, allowing you to map outgoing HTTP requests to remote servers with JAX-RS and interface proxies. RESTEasy includes the JAX-RS implementation. RESTEasy is portable to any Tomcat or Application Server that runs on Java Development Kit 5.0 or higher. The RESTEasy server implementation can be used with Embedded JBoss for junit testing. RESTEasy integrates easily with Enterprise JavaBeans (EJB) and the Spring Framework. RESTEasy includes a client framework to simplify the process of writing HTTP clients, giving you the opportunity to define more than server bindings.

Chapter 2. Installation/Configuration

2.1. Using RESTEasy in your Application

RESTEasy is deployed as a WAR archive, so it must be deployed inside a Servlet container. You can obtain the resteasy-jaxrs-war from the enterprise code source. The file is located in the /resteasy folder of the source code archive. Download the archive from the Red Hat Customer Support Portal. Place your JAX-RS annotated class resources and providers in one or more JAR s within /WEB-INF/lib . Alternatively, place your raw class files within /WEB-INF/classes . By default, RESTEasy is configured to scan JAR s and classes within these directories for JAX-RS annotated classes, and to deploy and register them within the system. RESTEasy is implemented as a ServletContextListener and a Servlet, and deployed within a WAR file. The WEB-INF/web.xml file contains the following:
<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <!--Set this if you want Resteasy to scan for JAX-RS classes
    <context-param>
        <param-name>resteasy.scan</param-name>
        <param-value>true</param-value>
    </context-param>
    <!-- set this if you map the Resteasy servlet to something other than /*
    <context-param>
       <param-name>resteasy.servlet.mapping.prefix</param-name>
       <param-value>/resteasy</param-value>
    </context-param>
    <!-- to turn on security
    <context-param>
        <param-name>resteasy.role.based.security</param-name>
        <param-value>true</param-value>
    </context-param>
    <listener>
        <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
    </listener>
    <servlet>
        <servlet-name>Resteasy</servlet-name>
        <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Resteasy</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>
			The ResteasyBootstrap listener initializes some of RESTEasy's basic components and scans for annotation classes that exist in your WAR file. It also receives configuration options from <context-param> elements.
			These configuration options must be set if the <servlet-mapping> for the RESTEasy Servlet has a URL pattern other than /*. For example, if the URL pattern is:
		
<servlet-mapping>
  <servlet-name>Resteasy</servlet-name>
  <url-pattern>/restful-services/*</url-pattern>
</servlet-mapping>
Then the value of resteasy-servlet.mapping.prefix must be:
<context-param>
  <param-name>resteasy.servlet.mapping.prefix</param-name>
  <param-value>/restful-services</param-value>
</context-param>
The available <param-name> values are outlined below with the <param-value> defaults (or expected patterns) listed for reference.

Available <context-param> Parameter Names

resteasy.servlet.mapping.prefix
Defines the URL pattern for the RESTEasy servlet-mapping , if not /* .
resteasy.scan.providers
Scans for @Provider classes and registers them. The default value is false .
resteasy.scan.resources
Scans for JAX-RS resource classes. The default value is false .
resteasy.scan
Scans for both @Provider and JAX-RS resource classes ( @Path , @GET , @POST , etc.) and registers them.
resteasy.providers
A comma-delimited list of fully-qualified @Provider class names you want to register.
resteasy.use.builtin.providers
Determines whether default, built-in @Provider classes are registered. The default value is true .
resteasy.resources
Comma-delimited list of fully-qualified JAX-RS resource class names you want to register.
resteasy.jndi.resources
A comma-delimited list of JNDI names referencing the objects that you want to register as JAX-RS resources.
javax.ws.rs.core.Application
Fully-qualified name of the Application class to bootstrap in a spec-portable way. The ResteasyBootstrap listener configures an instance of the ResteasyProviderFactory and Registry . You can obtain ResteasyProviderFactory and Registry instances from the ServletContext attributes org.jboss.resteasy.spi.ResteasyProviderFactory and org.jboss.resteasy.spi.Registry .

2.2. javax.ws.rs.core.Application

javax.ws.rs.core.Application is a standard JAX-RS class that can be implemented to provide information about your deployment. It is a class that lists all JAX-RS root resources and providers. * Defines the components of a JAX-RS application and supplies additional * metadata. A JAX-RS application or implementation supplies a concrete * subclass of this abstract class. public abstract class Application private static final Set<Object> emptySet = Collections.emptySet(); * Get a set of root resource and provider classes. The default lifecycle * for resource class instances is per-request. The default lifecycle for * providers is singleton. * <p>Implementations should warn about and ignore classes that do not * conform to the requirements of root resource or provider classes. * Implementations should warn about and ignore classes for which * {@link #getSingletons()} returns an instance. Implementations MUST * NOT modify the returned set.</p> * @return a set of root resource and provider classes. Returning null * is equivalent to returning an empty set. public abstract Set<Class<?>> getClasses(); * Get a set of root resource and provider instances. Fields and properties * of returned instances are injected with their declared dependencies * (see {@link Context}) by the runtime prior to use. * <p>Implementations should warn about and ignore classes that do not * conform to the requirements of root resource or provider classes. * Implementations should flag an error if the returned set includes * more than one instance of the same class. Implementations MUST * NOT modify the returned set.</p> * <p>The default implementation returns an empty set.</p> * @return a set of root resource and provider instances. Returning null * is equivalent to returning an empty set. public Set<Object> getSingletons() return emptySet; To use Application you must set the Servlet context-param , javax.ws.rs.core.Application , with a fully-qualified class that implements Application. For example: <context-param> <param-name>javax.ws.rs.core.Application</param-name> <param-value>com.mycom.MyApplicationConfig</param-value> </context-param> If you have this set, you should probably turn off automatic scanning as this will probably result in duplicate classes being registered.

2.3. RESTEasyLogging

RESTEasy logs various events using slf4j . The slf4j API is intended to serve as a simple facade for various logging APIs, allowing you to plug in the desired implementation at deployment time. By default, RESTEasy is configured to use Apache log4j , but you can use any logging provider supported by slf4j. The initial set of logging categories defined in the framework is listed below. Further logging categories are being added, but these should make it easier to troubleshoot issues.
Table 2.1. Logging Categories
Category Function
org.jboss.resteasy.core Logs all activity by the core RESTEasy implementation.
org.jboss.resteasy.plugins.providers Logs all activity by RESTEasy entity providers.
org.jboss.resteasy.plugins.server Logs all activity by the RESTEasy server implementation.
org.jboss.resteasy.specimpl Logs all activity by JAX-RS implementing classes.
org.jboss.resteasy.mock Logs all activity by the RESTEasy mock framework.
If you are developing RESTEasy code, the LoggerCategories class provides easy access to category names and the various loggers.

Chapter 3. Using @Path and @GET, @POST, etc.

@Path("/library") public class Library { @Path("/books") public String getBooks() {...} @Path("/book/{isbn}") public String getBook(@PathParam("isbn") String id) { // search my database and get a string representation and return it @Path("/book/{isbn}") public void addBook(@PathParam("isbn") String id, @QueryParam("name") String name) {...} @DELETE @Path("/book/{id}") public void removeBook(@PathParam("id") String id {...} If you have the RESTEasy Servlet configured and reachable at a root path of http://myhost.com/services , the requests would be handled by the Library class: GET http://myhost.com/services/library/books GET http://myhost.com/services/library/book/333 PUT http://myhost.com/services/library/book/333 DELETE http://myhost.com/services/library/book/333 The @javax.ws.rs.Path annotation must exist on either the class or a resource method, or both. If it exists on both the class and method, the relative path to the resource method is a concatenation of the class and method. The @javax.ws.rs package contains annotations for each HTTP method. @GET , @POST , @PUT , @DELETE , and @HEAD . Place these annotations on public methods that you want to map to the annotation's HTTP method. If a @Path annotation exists on the class, you do not need to annotate the method you wish to map with @Path . Multiple HTTP methods can be used, as long as they can be distinguished from other methods. When a method is annotated with @Path without a HTTP method being applied, the annotated method is referred to as a JAXRSResourceLocator .

3.1. @Path and regular expression mappings

The @Path annotation is not limited to simple path expressions. You can also insert regular expressions into the value of @Path . For example: @Path("/resources) public class MyResource { @Path("{var:.*}/stuff") public String get() {...} The following GETs will route to the getResource() method: GET /resources/stuff GET /resources/foo/stuff GET /resources/on/and/on/stuff The format of the expression is: "{" variable-name [ ":" regular-expression ] "}" Here, regular-expression is optional. Where this is not provided, the expression defaults to a wildcard matching of one particular segment, like so: "([]*)" For example: @Path("/resources/{var}/stuff") will match these: GET /resources/foo/stuff GET /resources/bar/stuff but will not match: GET /resources/a/bunch/of/stuff

Chapter 4. @PathParam

@PathParam is a parameter annotation which allows you to map variable URI path fragments into your method call. @Path("/library") public class Library { @Path("/book/{isbn}") public String getBook(@PathParam("isbn") String id) { // search my database and get a string representation and return it This lets you embed variable identification in the URIs of your resources. The previous example shows an isbn URI parameter passing information about a particular book we want to access. You can inject into any primitive parameter type, a String, any Java object that takes a String parameter, or a static valueOf method that takes a String parameter. For example, if we wanted isbn to be a real object, we could write: @Path("/book/{isbn}") public String getBook(@PathParam("isbn") ISBN id) {...} public class ISBN { public ISBN(String str) {...} Or instead of a public String constructor, we could have a valueOf method: public class ISBN { public static ISBN valueOf(String isbn) {...}

4.1. Advanced @PathParam and Regular Expressions

There are several more complicated uses of @PathParam s. You are allowed to specify one or more @PathParam s embedded in one URI segment. For example: 1. @Path("/aaa{param}bbb") 2. @Path("/{name}-{zip}") 3. @Path("/foo{name}-{zip}bar") So, a URI of the form "/aaa111bbb" would match the first specified parameter. "/bill-02115" would match the second, and "foobill-02115bar" would match the third. In Section 3.1, “@Path and regular expression mappings” , we mentioned that regular expressions can be used within @Path values, like so: @Path("/aaa{param:b+}/{many:.*}/stuff") public String getIt(@PathParam("param") String bs, @PathParam("many") String many) {...} With the @Path defined here, the request GET /aaabb/some/stuff would have a "param" value of bb , and a "many" value of some . The request GET /aaab/a/lot/of/stuff would have a "param" value of b , and a "many" value of a/lot/of .

4.2. @PathParam and PathSegment

The specification has a very simple abstraction for examining a fragment of the URI path being invoked on javax.ws.rs.core.PathSegment : public interface PathSegment { * Get the path segment. * @return the path segment String getPath(); * Get a map of the matrix parameters associated with the path segment * @return the map of matrix parameters MultivaluedMap<String, String> getMatrixParameters(); RESTEasy can inject a PathSegment instead of a value with your @PathParam . @Path("/book/{id}") public String getBook(@PathParam("id") PathSegment id) {...} This is particularly useful when you have multiple @PathParam s that use matrix parameters . Matrix parameters are an arbitrary set of name-value pairs embedded in a URI path segment. The PathSegment object fives you access to these parameters. See Chapter 7, @MatrixParam for further information. An example of a matrix parameter: GET http://host.com/library/book;name=EJB 3.0;author=Bill Burke A matrix parameter represents a resource that can be addressed by its attributes as well as its raw ID.

Chapter 5. @QueryParam

The @QueryParam annotation allows you to map a URI query string parameter or URL form-encoded parameter onto your method invocation. GET /books?num=5 public String getBooks(@QueryParam("num") int num) { Since RESTEasy is built on a Servlet, it cannot distinguish between URL query strings and URL form-encoded parameters. As with @PathParam , the type of parameter can be a primitive, a String, or a class with a String constructor or static valueOf() method.

Chapter 6. @HeaderParam

The @HeaderParam annotation allows you to map a request HTTP header onto your method invocation. GET /books?num=5 public String getBooks(@HeaderParam("From") String from) { As with @PathParam , the type of parameter can be a primitive, a String, or a class with a String constructor or static valueOf() method. For example, MediaType has a valueOf() method, so you could do the following: public void put(@HeaderParam("Content-Type") MediaType contentType, ...)

Chapter 7. @MatrixParam

Matrix parameters are an arbitrary set of name-value pairs embedded in a URI path segment. An example of a matrix parameter is: GET http://host.com/library/book;name=EJB 3.0;author=Bill Burke Matrix parameters represent resources that can be addressed by their attributes as well as their raw ID. The @MatrixParam annotation lets you injext URI matrix parameters into your method invocation. public String getBook(@MatrixParam("name") String name, @MatrixParam("author") String author) {...} One limitation with @MatrixParam is that the current version of the specification does not resolve. If, for example, the same MatrixParam exists simultaneously in different path segments, at present we recommend using PathParam combined with PathSegment .

Chapter 8. @CookieParam

The @CookieParam annotation allows you to inject the value of a cookie, or an object representation of an HTTP request cookie, into your method invocation. GET /books?num=5 public String getBooks(@CookieParam("sessionid") int id) { public String getBooks(@CookieParam("sessionid") javax.ws.rs.core.Cookie id) {...} As with @PathParam , the type of parameter can be a primitive, a String, or a class with a String constructor or static valueOf() method. You can also get an object representation of the cookie via the javax.ws.rs.core.Cookie class.

Chapter 9. @FormParam

When the input request body is of the type application/x-www-form-urlencoded (that is, an HTML form), you can inject individual form parameters from the request body into method parameter values. <form method="POST" action="/resources/service"> First name: <input type="text" name="firstname"> Last name: <input type="text" name="lastname"> </form> If you post using this form, the service would look as follows: @Path("/") public class NameRegistry { @Path("/resources/service") @POST public void addName(@FormParam("firstname") String first, @FormParam("lastname") String last) {...} You cannot combine @FormParam with the default application/x-www-form-urlencoded that unmarshalls to a MultivaluedMap<String, String> That is, the following would be illegal: @Path("/") public class NameRegistry { @Path("/resources/service") @POST @Consumes("application/x-www-form-urlencoded") public void addName(@FormParam("firstname") String first, MultivaluedMap<String, String> form) {...}

Chapter 10. @Form

This is a RESTEasy-specific annotation that allows you to reuse any @ * Param annotation within an injected class. RESTEasy instantiates the class and injects values into any annotated @ * Param or @Context property. This is useful if you have many parameters on your method and you want to condense them into a value object. public class MyForm { @FormParam("stuff") private int stuff; @HeaderParam("myHeader") private String header; @PathParam("foo") public void setFoo(String foo) {...} @POST @Path("/myservice") public void post(@Form MyForm form) {...} When someone posts to /myservice , RESTEasy instantiates an instance of MyForm and injects the form parameter stuff into the stuff field, the header myheader into the header field, and call the setFoo method with the @PathParam variable of foo .

Chapter 11. @DefaultValue

@DefaultValue is a parameter annotation that can be combined with any other @ * Param annotations to define a default value where the HTTP request item does not exist. public String getBooks(@QueryParam("num") @DefaultValue("10") int num) {...}

Chapter 12. @Encoded and encoding

JAX-RS allows you to get encoded or decoded @ * Param s and specify path definitions and parameter names using encoded or decoded strings. The @javax.ws.rs.Encoded annotation can be used on a class, method, or parameter. By default, injected @PathParam and @QueryParam are decoded. Adding the @Encoded annotation means that the value of these parameters will be provided in encoded form. @Path("/") public class MyResource { @Path("/{param}") public String get(@PathParam("param") @Encoded String param) {...} In the previous example, the value of the @PathParam injected into the param of the get() method will be URL encoded. Adding the @Encoded annotation as a parameter annotation triggers this effect. You can also use the @Encoded annotation on the entire method and any combination of @QueryParam or @PathParam 's values will be encoded. @Path("/") public class MyResource { @Path("/{param}") @Encoded public String get(@QueryParam("foo") String foo, @PathParam("param") String param) {} In this example, the values of the foo query parameter and the param path parameter will be injected as encoded values. You can also set the default to be encoded for the entire class. @Path("/") @Encoded public class ClassEncoded { public String get(@QueryParam("foo") String foo) {} The @Path annotation has an attribute called encode . This controls whether the literal part of the value supplied (that is, the characters that are not part of a template variable) are URL-encoded. If true , any characters in the URI template that are not valid will be automatically encoded. If false , then all characters must be valid URI characters. By default, the encode attribute is set to true . (You can also encode the characters by hand.) @Path(value="hello%20world", encode=false) As with @Path.encode() , this controls whether the specified query parameter name should be encoded by the container before it tries to find the query parameter in the request. @QueryParam(value="hello%20world", encode=false)

Chapter 13. @Context

The @Context annotation allows you to inject instances of javax.ws.rs.core.HttpHeaders , javax.ws.rs.core.UriInfo , javax.ws.rs.core.Request , javax.servlet.HttpServletRequest , javax.servlet.HttpServletResponse , javax.servlet.ServletConfig , javax.servlet.ServletContext , and javax.ws.rs.core.SecurityContext objects.

Chapter 14. JAX-RS Resource Locators and Sub Resources

Resource classes can partially process a request and then provide another sub-resource object to process the remainder of the request. For example: @Path("/") public class ShoppingStore { @Path("/customers/{id}") public Customer getCustomer(@PathParam("id") int id) { Customer cust = ...; // Find a customer object return cust; public class Customer { public String get() {...} @Path("/address") public String getAddress() {...} Resource methods with a @Path annotation and no HTTP method are considered sub-resource locators . They provide an object that can process the request. In the previous example code, ShoppingStore is a root resource because its class is annotated with @Path . The getCustomer() is a sub-resource locator method. If the client invoked the following:
GET /customer/123
Then the ShoppingStore.getCustomer() method would be invoked first. This method provides a Customer object that can service the request. The HTTP request will be dispatched to the Customer.get() method. Another example is:
GET /customer/123/address
In this request, again, first the ShoppingStore.getCustomer() method is invoked. A Customer object is returned, and the rest of the request is dispatched to the Customer.getAddress() method. Another interesting feature of sub-resource locators is that the locator method result is dynamically processed at runtime in order to determine how the request should be dispatched. This means that the ShoppingStore.getCustomer() method does not have to declare any specific type. @Path("/") public class ShoppingStore { @Path("/customers/{id}") public java.lang.Object getCustomer(@PathParam("id") int id) { Customer cust = ...; // Find a customer object return cust; public class Customer { public String get() {...} @Path("/address") public String getAddress() {...} In the previous example, getCustomer() returns a java.lang.Object . Per request, at runtime, the JAX-RS server will determine how to dispatch the request based on the object returned by getCustomer() . This can be useful in certain situations. For example, say you have a class hierarchy for your customers. Customer is the abstract base, and CorporateCustomer and IndividualCustomer are subclasses. In this case, your getCustomer() method might perform a Hibernate polymorphic query without requiring any understanding of the concrete class it queries, or the content returned. @Path("/") public class ShoppingStore { @Path("/customers/{id}") public java.lang.Object getCustomer(@PathParam("id") int id) { Customer cust = entityManager.find(Customer.class, id); return cust; public class Customer { public String get() {...} @Path("/address") public String getAddress() {...} public class CorporateCustomer extendsCustomer { @Path("/businessAddress") public String getAddress() {...}

Chapter 15. JAX-RS Content Negotiation

The HTTP protocol has built-in content negotiation headers that allow the client and server to specify the type of content that they transfer, and the type of content they prefer to receive. The server declares content preferences via the @Produces and @Consumes headers. @Consumes is an array of media types that a particular resource or resource method consumes. For example: @Consumes("text/*") @Path("/library") public class Library { @POST public String stringBook(String book) {...} @Consumes("text/xml") @POST public String jaxbBook(Book book) {...} When a client makes a request, JAX-RS first locates all methods that match the path. It then sorts objects based on the content-type header sent by the client. If a client sends the following: POST /library content-type: text/plain this is a nice book The stringBook() method is invoked, because it matches the default text/* media type. If the client sends XML, like so: POST /library content-type: text/xml <book name="EJB 3.0" author="Bill Burke"/> Then the jaxbBook() method is invoked. @Produces is used to map a client request and match it with the client's Accept header. The Accept HTTP header is sent by the client, and defines the media types that the client prefers to receive from the server. @Produces("text/*") @Path("/library") public class Library { @Produces("application/json") public String getJSON() {...} public String get() {...} So, if the client sends: GET /library Accept: application/json The getJSON() method would be invoked. @Consumes and @Produces can support multiple media types by presenting them in a list. The client's Accept header can also list multiple media types to receive. More specific media types are selected first. The Accept header (or @Produces or @Consumes ) can also specify weighted preferences that will match requests with resource methods. (This is best explained in Section 14.1 of RFC 2616.) RESTEasy provides support for this more complex method of content negotiation. An alternative method used by JAX-RS is a combination of media-type, content-language, and content encoding, in addition to etags, last modified headers, and other pre-conditions. This is a more complex form of content negotiation, performed programmatically by the application developer via the javax.ws.rs.Variant , VarianListBuilder , and Request objects. Request is injected using the @Context annotation. (For more information, read the JavaDoc.)

Chapter 16. Content Marshalling/Providers

16.1. Default Providers and default JAX-RS Content Marshalling

RESTEasy can automatically marshal and unmarshal several different message body types.
Table 16.1. Message Body Types
Media Types Java Type
application/*+xml, text/*+xml, application/*+json, application/*+fastinfoset, application/atom+* JaxB annotated classes
*/* java.lang.String
*/* java.io.InputStream
text/plain primitives, java.lang.String, or any type that has a String constructor, or static valueOf(String) method for input, toString() for output
*/* javax.activation.DataSource
*/* java.io.File
*/* byte[]
application/x-www-form-urlencoded javax.ws.rs.core.MultivaluedMap

16.2. Content Marshalling with @Provider classes

The JAX-RS specification lets you plug in your own readers and writers for request or response bodies. To do so, annotate a class with @Provider and specify the @Produces types for a reader. You must also implement a MessageBodyReader and a MessageBodyWriter interface.

16.3. Providers Utility Class

javax.ws.rs.ext.Providers is a simple injectable interface that lets you locate MessageBodyReader s, MessageBodyWriter s, ContextResolver s and ExceptionMapper s. It also lets you implement multi-part providers (content types that embed other content types). public interface Providers * Get a message body reader that matches a set of criteria. The set of * readers is first filtered by comparing the supplied value of * {@code mediaType} with the value of each reader's * {@link javax.ws.rs.Consumes}, ensuring the supplied value of * {@code type} is assignable to the generic type of the reader, and * eliminating those that do not match. * The list of matching readers is then ordered with those with the best * matching values of {@link javax.ws.rs.Consumes} (x/y > x/* > */*) * sorted first. Finally, the * {@link MessageBodyReader#isReadable} * method is called on each reader in order using the supplied criteria and * the first reader that returns {@code true} is selected and returned. * @param type the class of object that is to be written. * @param mediaType the media type of the data that will be read. * @param genericType the type of object to be produced. E.g. if the * message body is to be converted into a method parameter, this will be * the formal type of the method parameter as returned by * <code>Class.getGenericParameterTypes</code>. * @param annotations an array of the annotations on the declaration of the * artifact that will be initialized with the produced instance. E.g. if the * message body is to be converted into a method parameter, this will be * the annotations on that parameter returned by * <code>Class.getParameterAnnotations</code>. * @return a MessageBodyReader that matches the supplied criteria or null * if none is found. <T> MessageBodyReader<T> getMessageBodyReader(Class<T> type, Type genericType, Annotation annotations[], MediaType mediaType); * Get a message body writer that matches a set of criteria. The set of * writers is first filtered by comparing the supplied value of * {@code mediaType} with the value of each writer's * {@link javax.ws.rs.Produces}, ensuring the supplied value of * {@code type} is assignable to the generic type of the reader, and * eliminating those that do not match. * The list of matching writers is then ordered with those with the best * matching values of {@link javax.ws.rs.Produces} (x/y > x/* > */*) * sorted first. Finally, the * {@link MessageBodyWriter#isWriteable} * method is called on each writer in order using the supplied criteria and * the first writer that returns {@code true} is selected and returned. * @param mediaType the media type of the data that will be written. * @param type the class of object that is to be written. * @param genericType the type of object to be written. E.g. if the * message body is to be produced from a field, this will be * the declared type of the field as returned by * <code>Field.getGenericType</code>. * @param annotations an array of the annotations on the declaration of the * artifact that will be written. E.g. if the * message body is to be produced from a field, this will be * the annotations on that field returned by * <code>Field.getDeclaredAnnotations</code>. * @return a MessageBodyReader that matches the supplied criteria or null * if none is found. <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> type, Type genericType, Annotation annotations[], MediaType mediaType); * Get an exception mapping provider for a particular class of exception. * Returns the provider whose generic type is the nearest superclass of * {@code type}. * @param type the class of exception * @return an {@link ExceptionMapper} for the supplied type or null if none * is found. <T extends Throwable> ExceptionMapper<T> getExceptionMapper(Class<T> type); * Get a context resolver for a particular type of context and media type. * The set of resolvers is first filtered by comparing the supplied value of * {@code mediaType} with the value of each resolver's * {@link javax.ws.rs.Produces}, ensuring the generic type of the context * resolver is assignable to the supplied value of {@code contextType}, and * eliminating those that do not match. If only one resolver matches the * criteria then it is returned. If more than one resolver matches then the * list of matching resolvers is ordered with those with the best * matching values of {@link javax.ws.rs.Produces} (x/y > x/* > */*) * sorted first. A proxy is returned that delegates calls to * {@link ContextResolver#getContext(java.lang.Class)} to each matching context * resolver in order and returns the first non-null value it obtains or null * if all matching context resolvers return null. * @param contextType the class of context desired * @param mediaType the media type of data for which a context is required. * @return a matching context resolver instance or null if no matching * context providers are found. <T> ContextResolver<T> getContextResolver(Class<T> contextType, MediaType mediaType); You can inject an instance of Providers into MessageBodyReader or MessageBodyWriter like so: @Provider @Consumes("multipart/fixed") public class MultipartProvider implements MessageBodyReader { private @Context Providers providers;

Chapter 17. JAXB Providers

RESTEasy includes support for marshaling and unmarshaling JAXB annotated classes. Multiple JAXB Providers are included with RESTEasy to address the subtle differences between classes generated by XJC and classes that are annotated with @XmlRootElement , or work with JAXBElement classes directly. When using the JAX-RS API in development, the provider to be invoked is selected transparently. This chapter describes the providers best-suited for a variety of configurations if you want to access the providers directly . RESTEasy selects a JAXB Provider when a parameter type (return type) is an object annotated with JAXB annotations (for example, @XmlRootEntity or @XmlType ), or a JAXBElement . The resource class (resource method) will be annotated with either @Consumes or @Produces , and contain one or more of the following values: text/*+xml application/*+xml application/*+fastinfoset application/*+json RESTEasy selects different providers based on the return type used in the resource. This section describes the workings of the selection process. Classes annotated with @XmlRootElement are handled with the JAXBXmlRootElementProvider . This provider handles basic marshaling and unmarshaling of custom JAXB entities. Classes that are generated by XJC do not usually contain an @XmlRootElement annotation. To be marshaled, they must be wrapped in an instance of JAXBElement . This usually involves invoking a method named ObjectFactory on the class, which serves as the XmlRegistry . The JAXBXmlTypeProvider provider is selected when the class is annotated with an XmlType annotation and not an XmlRootElement annotation. The provider attempts to locate the XmlRegistry for the target class. By default, a JAXB implementation creates a class called ObjectFactory and is located in the same package as the target class. ObjectFactory contains a create method that takes the object instance as a parameter. For example, if the target type is called Contact , then the ObjectFactory class will have a method: public JAXBElement createContact(Contact value) {.. If your resource works with the JAXBElement class directly, the RESTEasy runtime will select the JAXBElementProvider . This provider examines the ParameterizedType value of the JAXBElement in order to select the appropriate JAXBContext .

17.1. JAXB Decorators

RESTEasy's JAXB providers can decorate marshaler and Unmarshaler instances. Add an annotation that triggers the decoration marshaler or Unmarshaler . The decorators can perform tasks such as setting marshaler or Unmarshaler properties and setting up validation. As an example, say you want to create an annotation that will trigger pretty-printing of an XML document. In raw JAXB, we would set a property on the marshaler of marshaler.JAXB_FORMATTED_OUTPUT . Instead, let us write a marshaler decorator . First, define an annotation: import org.jboss.resteasy.annotations.Decorator; @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Decorator(processor = PrettyProcessor.class, target = marshaler.class) public @interface Pretty {} For this to work, you must annotate the @Pretty annotation with a meta-annotation named @Decorator . The target() attribute must be the JAXB marshaler class. Next, we will write the processor() attribute class. import org.jboss.resteasy.core.interception.DecoratorProcessor; import org.jboss.resteasy.annotations.DecorateTypes; import javax.xml.bind.marshaler; import javax.xml.bind.PropertyException; import javax.ws.rs.core.MediaType; import javax.ws.rs.Produces; import java.lang.annotation.Annotation; * @author <a href="mailto:[email protected]">Bill Burke</a> * @version $Revision: 1 $ @DecorateTypes({"text/*+xml", "application/*+xml"}) public class PrettyProcessor implements DecoratorProcessor<marshaler, Pretty> public marshaler decorate(marshaler target, Pretty annotation, Class type, Annotation[] annotations, MediaType mediaType) target.setProperty(marshaler.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); The processor implementation must implement the DecoratorProcessor interface, and should also be annotated with @DecorateTypes . This annotation specifies the media types that the processor can work with. Now that we have defined our annotation and our Processor , we can use it on our JAX-RS resource methods or JAXB types like so: @Pretty @Produces("application/xml") public SomeJAXBObject get() {...} If this is confusing, check the RESTEasy source code for information about implementing @XmlHeader .

17.2. Pluggable JAXBContext s with ContextResolvers

We do not recommend using this feature unless you are familiar with the principles involved. By default, RESTEasy creates and caches JAXBContext instances per class type depending on the class you are marshaling or unmarshaling. If you do not want RESTEasy to create JAXBContext s, you can plug in your own by implementing an instance of javax.ws.rs.ext.ContextResolver . public interface ContextResolver<T> T getContext(Class<?> type); @Provider @Produces("application/xml") public class MyJAXBContextResolver implements ContextResolver<JAXBContext> JAXBContext getContext(Class<?> type) if (type.equals(WhateverClassIsOverridedFor.class)) return JAXBContext.newInstance()...; You must provide a @Produces annotation to specify the types of media intended for the context. You must also implement ContextResolver<JAXBContext> . This helps the runtime match the correct context resolver. You must also annotate the ContextResolver class with @Provider . There are several ways to make this ContextResolver available. return it as a class or instance from a javax.ws.rs.core.Application implementation. list it as a provider with resteasy.providers . let RESTEasy automatically scan for it within your WAR file. (See the Configuration Guide for more details.) add it manually via ResteasyProviderFactory.getInstance().registerProvider(Class) or registerProviderInstance(Object) .

17.3. JAXB and XML provider

RESTEasy provides the required JAXB provider support for XML. It has several additional annotations to make application coding simpler.

17.3.1. @XmlHeader and @Stylesheet

To set an XML header when you output XML documents, use the @org.jboss.resteasy.annotations.providers.jaxb.XmlHeader annotation. @XmlRootElement public static class Thing private String name; public String getName() return name; public void setName(String name) this.name = name; @Path("/test") public static class TestService @Path("/header") @Produces("application/xml") @XmlHeader("<?xml-stylesheet type='text/xsl' href='${baseuri}foo.xsl' ?>") public Thing get() Thing thing = new Thing(); thing.setName("bill"); return thing; Here, the @XmlHeader forces an xml-stylesheet header on the XML output. The same result can be obtained by placing the header on the Thing class. Read the JavaDocs for further information regarding the substitution values provided by RESTEasy. RESTEasy also has a convenient annotation for stylesheet headers. For example: @XmlRootElement public static class Thing private String name; public String getName() return name; public void setName(String name) this.name = name; @Path("/test") public static class TestService @Path("/stylesheet") @Produces("application/xml") @Stylesheet(type="text/css", href="${basepath}foo.xsl") @Junk public Thing getStyle() Thing thing = new Thing(); thing.setName("bill"); return thing;

17.4. JAXB and JSON provider

RESTEasy lets you marshal JAXB annotated POJOs to and from JSON with the Jettison JSON library. You can find more information about Jettison at http://jettison.codehaus.org/ . Jettison has two mapping formats: the default Jettison Mapped Convention format, and BadgerFish. For example, consider this JAXB class:
@XmlRootElement(name = "book")
public class Book {
   private String author;
   private String ISBN;
   private String title;
   public Book() {
   public Book(String author, String ISBN, String title) {
      this.author = author;
      this.ISBN = ISBN;
      this.title = title;
   @XmlElement
   public String getAuthor() {
      return author;
   public void setAuthor(String author) {
      this.author = author;
   @XmlElement
   public String getISBN() {
      return ISBN;
   public void setISBN(String ISBN) {
      this.ISBN = ISBN;
   @XmlAttribute
   public String getTitle() {
      return title;
   public void setTitle(String title) {
      this.title = title;
			The JAXB Book class would be marshaled to JSON using the BadgerFish Convention:
 {"book":
       "@title":"EJB 3.0",
       "author":{"$":"Bill Burke"},
       "ISBN":{"$":"596529260"}
			Element values are associated with a map. To find the value of the element, you must access the $ variable. You could access the book like this, in JavaScript:
 var data = eval("(" + xhr.responseText + ")");
 document.getElementById("zone").innerHTML = data.book.@title;
 document.getElementById("zone").innerHTML += data.book.author.$;
			To use the BadgerFish Convention you must use the @org.jboss.resteasy.annotations.providers.jaxb.json.BadgerFish annotation either on the JAXB class you are marshaling or unmarshaling, or on the JAX-RS resource method or parameter:
 @BadgerFish
 @XmlRootElement(name = "book")
 public class Book {...}
			To return a book on the JAX-RS method without polluting your JAXB classes with RESTEasy annotations, you can add the annotation to the JAX-RS method instead:
 @BadgerFish
 public Book getBook(...) {...}
			If your input is a Book, place it on the parameter:
 @POST
 public void newBook(@BadgerFish Book book) {...}
			The default Jettison Mapped Convention returns the following JSON:
 { "book" :
         "@title":"EJB 3.0",
         "author":"Bill Burke",
         "ISBN":596529260
			Note that title is prefixed with the @ character. Unlike the BadgerFish convention, this does not represent the value of element text, which makes it simpler (and a sensible default). To access this in JavaScript:
 var data = eval("(" + xhr.responseText + ")");
 document.getElementById("zone").innerHTML = data.book.@title;
 document.getElementById("zone").innerHTML += data.book.author;
			The Mapped Convention lets you adjust the JAXB mapping with the @org.jboss.resteasy.annotations.providers.jaxb.json.Mapped annotation. With this, you can provide an XML namespace to JSON namespace mapping. For example, if you define your JAXB namespace within your package-info.java class like so:
 @javax.xml.bind.annotation.XmlSchema(namespace="http://jboss.org/books")
 package org.jboss.resteasy.test.books;
			You must define a JSON-to-XML namespace mapping, or you will receive an exception:
java.lang.IllegalStateException: Invalid JSON namespace:
  http://jboss.org/books
at org.codehaus.jettison.mapped.MappedNamespaceConvention
  .getJSONNamespace(MappedNamespaceConvention.java:151)
at org.codehaus.jettison.mapped.MappedNamespaceConvention
  .createKey(MappedNamespaceConvention.java:158)
at org.codehaus.jettison.mapped.MappedXMLStreamWriter
  .writeStartElement(MappedXMLStreamWriter.java:241)
			The @Mapped annotation fixes this problem. Place the @Mapped annotation on your JAXB classes, your JAX-RS resource method, or on the parameter that you are unmarshaling.
 import org.jboss.resteasy.annotations.providers.jaxb.json.Mapped;
 import org.jboss.resteasy.annotations.providers.jaxb.json.XmlNsMap;
@Produces("application/json")
@Mapped(namespaceMap = {
        @XmlNsMap(namespace = "http://jboss.org/books", jsonName = "books")
public Book get() {...}
			You can also force @XmlAttributes to be marshaled as XMLElements.
            @Mapped(attributeAsElements={"title"})
            @XmlRootElement(name = "book")
            public class Book {...}
			To return a book on the JAX-RS method without polluting your JAXB classes with RESTEasy annotations, add the annotation to the JAX-RS method:
            @Mapped(attributeAsElements={"title"})
            public Book getBook(...) {...}
			If your input is a Book, place it on the parameter:
 @POST
 public void newBook(@Mapped(attributeAsElements={"title"}) Book book) {...}

17.5. JAXB and FastinfoSet provider

RESTEasy supports the Fastinfoset MIME type through the use of JAXB annotated classes. Fastinfoset documents serialize and parse more quickly, and are smaller in size, than logically-equivalent XML documents, so they can be used where size and processing time of XML documents is problematic. It is configured in the same way as the XML JAXB provider.

17.6. Arrays and Collections of JAXB Objects

RESTEasy automatically marshals arrays, java.util.Set s, and java.util.List s of JAXB objects to and from XML, JSON, Fastinfoset , and other RESTEasy JAXB mappers. @XmlRootElement(name = "customer") @XmlAccessorType(XmlAccessType.FIELD) public class Customer @XmlElement private String name; public Customer() public Customer(String name) this.name = name; public String getName() return name; @Path("/") public class MyResource @Path("array") @Consumes("application/xml") public void putCustomers(Customer[] customers) Assert.assertEquals("bill", customers[0].getName()); Assert.assertEquals("monica", customers[1].getName()); @Path("set") @Produces("application/xml") public Set<Customer> getCustomerSet() HashSet<Customer> set = new HashSet<Customer>(); set.add(new Customer("bill")); set.add(new Customer("monica")); return set; @Path("list") @Consumes("application/xml") public void putCustomers(List<Customer> customers) Assert.assertEquals("bill", customers.get(0).getName()); Assert.assertEquals("monica", customers.get(1).getName()); The resource above publishes and receives JAXB objects. We assume that these are wrapped in a collection element like the following: <collection> <customer><name>bill</name></customer> <customer><name>monica</name></customer> <collection> You can change the namespace URI, namespace tag, and collection element name by using the @org.jboss.resteasy.annotations.providers.jaxb.Wrapped annotation on a parameter or method: @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Wrapped String element() default "collection"; String namespace() default "http://jboss.org/resteasy"; String prefix() default "resteasy"; So, if we wanted to output the following XML: <foo:list xmlns:foo="http://foo.org"> <customer><name>bill</name></customer> <customer><name>monica</name></customer> </foo:list> We would use the @Wrapped annotation as follows: @Path("list") @Produces("application/xml") @Wrapped(element="list", namespace="http://foo.org", prefix="foo") public List<Customer> getCustomerSet() List<Customer> list = new ArrayList<Customer>(); list.add(new Customer("bill")); list.add(new Customer("monica")); return list;

17.6.1. JSON and JAXB Collections/Arrays

RESTEasy supports using collections with JSON. It encloses lists, sets, or arrays of returned JAXB objects in a simple JSON array. For example: @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public static class Foo @XmlAttribute private String test; public Foo() public Foo(String test) this.test = test; public String getTest() return test; public void setTest(String test) this.test = test; A List or Array of the Foo class would be represented in JSON like so: [{"foo":{"@test":"bill"}},{"foo":{"@test":"monica}"}}] It would also expect this format when receiving input.

17.7. Maps of JAXB Objects

RESTEasy automatically marshals maps of JAXB objects to and from XML, JSON, Fastinfoset , and other JAXB mappers. Your parameter or method return type must be generic, with a String as the key and the JAXB object's type. @XmlRootElement(namespace = "http://foo.com") public static class Foo @XmlAttribute private String name; public Foo() public Foo(String name) this.name = name; public String getName() return name; @Path("/map") public static class MyResource @POST @Produces("application/xml") @Consumes("application/xml") public Map<String, Foo> post(Map<String, Foo> map) Assert.assertEquals(2, map.size()); Assert.assertNotNull(map.get("bill")); Assert.assertNotNull(map.get("monica")); Assert.assertEquals(map.get("bill").getName(), "bill"); Assert.assertEquals(map.get("monica").getName(), "monica"); return map; This resource publishes and receives JAXB objects within a map. By default, they are wrapped in a map element in the default namespace. Each map element has zero or more entry elements with a key attribute. <entry key="bill" xmlns="http://foo.com"> <foo name="bill"/> </entry> <entry key="monica" xmlns="http://foo.com"> <foo name="monica"/> </entry> You can change the namespace URI, namespace prefix and map, entry, and key element and attribute names by using the @org.jboss.resteasy.annotations.providers.jaxb.WrappedMap annotation on a parameter or method. @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface WrappedMap * map element name String map() default "map"; * entry element name * String entry() default "entry"; * entry's key attribute name String key() default "key"; String namespace() default ""; String prefix() default ""; So, to output the following XML: <hashmap> <hashentry hashkey="bill" xmlns:foo="http://foo.com"> <foo:foo name="bill"/> </hashentry> We would use the @WrappedMap annotation as follows: @Path("/map") public static class MyResource @Produces("application/xml") @WrappedMap(map="hashmap", entry="hashentry", key="hashkey") public Map<String, Foo> get() return map;

17.7.1. JSON and JAXB maps

RESTEasy supports the use of maps with JSON. It encloses returned JAXB objects within simple JSON maps. For example: @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public static class Foo @XmlAttribute private String test; public Foo() public Foo(String test) this.test = test; public String getTest() return test; public void setTest(String test) this.test = test; This a List or array of this Foo class would be represented in JSON like this: { "entry1" : {"foo":{"@test":"bill"}}, "entry2" : {"foo":{"@test":"monica}"}}} It also expects this format for input

17.7.2. Possible Problems with Jettison Provider

If you have the resteasy-jackson-provider-xxx.jar in your classpath, the Jackson JSON provider will be triggered. This is problematic for code that depends upon the Jettison JAXB or JSON provider. To correct this, you must either remove Jackson from your WEB-INF/lib or classpath, or use the @NoJackson annotation on your JAXB classes.

17.8. Interfaces, Abstract Classes, and JAXB

Some object models use abstract classes and interfaces heavily. JAXB does not work with interfaces that are root elements, and RESTEasy cannot unmarshal parameters that are interfaces or raw abstract classes because it lacks the information required to create a JAXBContext . For example: public interface IFoo {} @XmlRootElement public class RealFoo implements IFoo {} @Path("/jaxb") public class MyResource { @Consumes("application/xml") public void put(IFoo foo) {...} In this example, RESTEasy would display an error ("Cannot find MessageBodyReader for..." or similar) because RESTEasy does not know that implementations of IFoo are JAXB classes, so it cannot create a JAXBContext for IFoo . As a workaround, you can annotate the interface with @XmlSeeAlso to correct the issue. This will not work with manual, hand-coded JAXB. @XmlSeeAlso(RealFoo.class) public interface IFoo {} The extra @XmlSeeAlso on IFoo allows RESTEasy to create a JAXBContext that knows how to unmarshal RealFoo instances.

Chapter 18. RESTEasy Atom Support

Atom is an XML-based document format that compiles lists of related information, known as feeds . Feeds are composed of a number of items, known as entries , each of which includes an extensible set of metadata (a title, for example). Atom is primarily used to syndicate web content (such as weblogs and news headlines) to websites, and directly to user agents. Atom is the RSS feed of the next generation. Although used primarily to syndicate weblogs and news, the format is starting to be used as the envelope for Web services such as distributed notifications and job queues, or simply to send or receive data in bulk to or from a service.

18.1. RESTEasy Atom API and Provider

RESTEasy has defined a simple object model to represent Atom in Java, and uses JAXB to marshal and unmarshal it. The org.jboss.resteasy.plugins.providers.atom package contains the main classes: Feed , Entry , Content , and Link . Each class is annotated with JAXB annotations. The distribution also contains the JavaDocs for this project, which are very useful in learning the model. The following code is a simple example of sending an Atom feed with the RESTEasy API: import org.jboss.resteasy.plugins.providers.atom.Content; import org.jboss.resteasy.plugins.providers.atom.Entry; import org.jboss.resteasy.plugins.providers.atom.Feed; import org.jboss.resteasy.plugins.providers.atom.Link; import org.jboss.resteasy.plugins.providers.atom.Person; @Path("atom") public class MyAtomService @Path("feed") @Produces("application/atom+xml") public Feed getFeed() throws URISyntaxException Feed feed = new Feed(); feed.setId(new URI("http://example.com/42")); feed.setTitle("My Feed"); feed.setUpdated(new Date()); Link link = new Link(); link.setHref(new URI("http://localhost")); link.setRel("edit"); feed.getLinks().add(link); feed.getAuthors().add(new Person("Bill Burke")); Entry entry = new Entry(); entry.setTitle("Hello World"); Content content = new Content(); content.setType(MediaType.TEXT_HTML_TYPE); content.setText("Nothing much"); entry.setContent(content); feed.getEntries().add(entry); return feed; RESTEasy's Atom provider is JAXB-based, so you are not limited to sending Atom objects with XML. You can automatically re-use RESTEasy's other JAXB providers (JSON and FastinfoSet). All you need to do is add +atom in front of the main subtype (that is, @Produces("application/atom+json") or @Consumes("application/atom+fastinfoset") .

18.2. Using JAXB with the Atom Provider

The org.jboss.resteasy.plugins.providers.atom.Content class lets you marshal and unmarshal JAXB-annotated objects that form the body of an entry's content. The following code is an example of sending an Entry with a Customer object attached as the body of the entry's content.
@XmlRootElement(namespace = "http://jboss.org/Customer")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer
   @XmlElement
   private String name;
   public Customer()
   public Customer(String name)
      this.name = name;
   public String getName()
      return name;
@Path("atom")
public static class AtomServer
   @Path("entry")
   @Produces("application/atom+xml")
   public Entry getEntry()
      Entry entry = new Entry();
      entry.setTitle("Hello World");
      Content content = new Content();
      content.setJAXBObject(new Customer("bill"));
      entry.setContent(content);
      return entry;
			The Content.setJAXBObject() method tells the content object that you are returning a Java JAXB object to be marshaled. If you use a base format other than XML (for example, application/atom+json), the attached JAXB object will be marshaled into that format.
			If your input is an Atom document, you can also extract JAXB objects from Content by using Content.getJAXBObject(Class class). The code that follows is an example of extracting a Customer object from the Content:
		
@Path("atom")
public static class AtomServer
   @Path("entry")
   @Produces("application/atom+xml")
   public void putCustomer(Entry entry)
      Content content = entry.getContent();
      Customer cust = content.getJAXBObject(Customer.class);
}

18.3. Atom support through Apache Abdera

RESTEasy supports Apache Abdera, an implementation of the Atom protocol and data format. You can find Abdera at the Apache web site . Abdera is a fully-fledged Atom server, but RESTEasy only supports integration with JAX-RS for marshaling and unmarshaling the Atom data format to and from the Feed and Entry interface types in Abdera.

18.3.1. Abdera and Maven

The Abdera provider is not included with the RESTEasy distribution. To include the Abdera provider in your WAR archive's pom files, add the following. Remember to change the version in the code to the version of RESTEasy that you are working with.

Warning

RESTEasy may not pick up the latest version of Abdera.
<repository>
      <id>jboss</id>
      <url>http://repository.jboss.org/maven2</url>
   </repository>
   <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>abdera-atom-provider</artifactId>
      <version>...version...</version>
   </dependency>

18.3.2. Using the Abdera Provider

import org.apache.abdera.Abdera;
import org.apache.abdera.factory.Factory;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.jboss.resteasy.plugins.providers.atom.AbderaEntryProvider;
import org.jboss.resteasy.plugins.providers.atom.AbderaFeedProvider;
import org.jboss.resteasy.test.BaseResourceTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBContext;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Date;
 * @author <a href="mailto:[email protected]">Bill Burke</a>
 * @version $Revision: 1 $
public class AbderaTest extends BaseResourceTest
   @Path("atom")
   public static class MyResource
      private static final Abdera abdera = new Abdera();
      @Path("feed")
      @Produces(MediaType.APPLICATION_ATOM_XML)
      public Feed getFeed(@Context UriInfo uri) throws Exception
         Factory factory = abdera.getFactory();
         Assert.assertNotNull(factory);
         Feed feed = abdera.getFactory().newFeed();
         feed.setId("tag:example.org,2007:/foo");
         feed.setTitle("Test Feed");
         feed.setSubtitle("Feed subtitle");
         feed.setUpdated(new Date());
         feed.addAuthor("James Snell");
         feed.addLink("http://example.com");
         Entry entry = feed.addEntry();
         entry.setId("tag:example.org,2007:/foo/entries/1");
         entry.setTitle("Entry title");
         entry.setUpdated(new Date());
         entry.setPublished(new Date());
         entry.addLink(uri.getRequestUri().toString());
         Customer cust = new Customer("bill");
         JAXBContext ctx = JAXBContext.newInstance(Customer.class);
         StringWriter writer = new StringWriter();
         ctx.createmarshaler().marshal(cust, writer);
         entry.setContent(writer.toString(), "application/xml");
         return feed;
      @Path("feed")
      @Consumes(MediaType.APPLICATION_ATOM_XML)
      public void putFeed(Feed feed) throws Exception
         String content = feed.getEntries().get(0).getContent();
         JAXBContext ctx = JAXBContext.newInstance(Customer.class);
         Customer cust = (Customer) ctx.createUnmarshaler().unmarshal(new StringReader(content));
         Assert.assertEquals("bill", cust.getName());
      @Path("entry")
      @Produces(MediaType.APPLICATION_ATOM_XML)
      public Entry getEntry(@Context UriInfo uri) throws Exception
         Entry entry = abdera.getFactory().newEntry();
         entry.setId("tag:example.org,2007:/foo/entries/1");
         entry.setTitle("Entry title");
         entry.setUpdated(new Date());
         entry.setPublished(new Date());
         entry.addLink(uri.getRequestUri().toString());
         Customer cust = new Customer("bill");
         JAXBContext ctx = JAXBContext.newInstance(Customer.class);
         StringWriter writer = new StringWriter();
         ctx.createmarshaler().marshal(cust, writer);
         entry.setContent(writer.toString(), "application/xml");
         return entry;
      @Path("entry")
      @Consumes(MediaType.APPLICATION_ATOM_XML)
      public void putFeed(Entry entry) throws Exception
         String content = entry.getContent();
         JAXBContext ctx = JAXBContext.newInstance(Customer.class);
         Customer cust = (Customer) ctx.createUnmarshaler().unmarshal(new StringReader(content));
         Assert.assertEquals("bill", cust.getName());
   @Before
   public void setUp() throws Exception
      dispatcher.getProviderFactory().registerProvider(AbderaFeedProvider.class);
      dispatcher.getProviderFactory().registerProvider(AbderaEntryProvider.class);
      dispatcher.getRegistry().addPerRequestResource(MyResource.class);
   @Test
   public void testAbderaFeed() throws Exception
      HttpClient client = new HttpClient();
      GetMethod method = new GetMethod("http://localhost:8081/atom/feed");
      int status = client.executeMethod(method);
      Assert.assertEquals(200, status);
      String str = method.getResponseBodyAsString();
      PutMethod put = new PutMethod("http://localhost:8081/atom/feed");
      put.setRequestEntity(new StringRequestEntity(str, MediaType.APPLICATION_ATOM_XML, null));
      status = client.executeMethod(put);
      Assert.assertEquals(200, status);
   @Test
   public void testAbderaEntry() throws Exception
      HttpClient client = new HttpClient();
      GetMethod method = new GetMethod("http://localhost:8081/atom/entry");
      int status = client.executeMethod(method);
      Assert.assertEquals(200, status);
      String str = method.getResponseBodyAsString();
      PutMethod put = new PutMethod("http://localhost:8081/atom/entry");
      put.setRequestEntity(new StringRequestEntity(str, MediaType.APPLICATION_ATOM_XML, null));
      status = client.executeMethod(put);
      Assert.assertEquals(200, status);

Chapter 19. JSON Support via Jackson

Apart from the Jettison JAXB adapter for JSON, RESTEasy also supports integration with the Jackson project. Many users find Jackson's output format more intuitive than the format provided by either BadgerFish or Jettison. Jackson is available from http://jackson.codehaus.org . It lets you easily marshal Java objects to and from JSON. Jackson has a JavaBean-based model and JAXB-like APIs. RESTEasy integrates with the JavaBean model as described in the Jackson Tutorial . To include Jackson in your project, add the following Maven dependency to your build: <repository> <id>jboss</id> <url>http://repository.jboss.org/maven2</url> </repository> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-jackson-provider</artifactId> <version>1.1.GA</version> </dependency> RESTEasy expands the JAX-RS integration built into Jackson in several ways. The first expansion provided support for application/*+json . Previously, Jackson accepted only application/json and text/json as valid media types. application/*+json support lets you marshal your JSON-based media types with Jackson. For example: @Path("/customers") public class MyService { @Produces("application/vnd.customer+json") public Customer[] getCustomers() {} Using RESTEasy JAXB providers alongside Jackson is also problematic. Rather than use Jackson to output your JSON, you can use Jettison and JAXB. To do so, you must either not install the Jackson provider, or use the @org.jboss.resteasy.annotations.providers.NoJackson annotation on your JAXB annotated classes, like so: @XmlRootElement @NoJackson public class Customer {...} @Path("/customers") public class MyService { @Produces("application/vnd.customer+json") public Customer[] getCustomers() {} If you cannot annotate the JAXB class with @NoJackson , then you can annotate a method parameter instead: @XmlRootElement public class Customer {...} @Path("/customers") public class MyService { @Produces("application/vnd.customer+json") @NoJackson public Customer[] getCustomers() {} @POST @Consumes("application/vnd.customer+json") public void createCustomer(@NoJackson Customer[] customers) {...}

19.1. Possible Conflict With JAXB Provider

If your Jackson classes are annotated with JAXB annotations and the resteasy-jaxb-provider is on your classpath, you can trigger the Jettison JAXB marshalling code. To disable the JAXB JSON Marshaller, annotate your classes with @org.jboss.resteasy.annotations.providers.jaxb.IgnoreMediaTypes("application/*+json") .

Chapter 20. Multipart Providers

RESTEasy has rich support for the multipart/* and multipart/form-data MIME (Multipurpose Internet Mail Extension) types. The multipart MIME format passes lists of content bodies. Multiple content bodies are embedded in the one message. multipart/form-data is often found in web application HTML Form documents, and is generally used to upload files. The form-data format works like other multipart formats, except that each inlined piece of content has a name associated with it. RESTEasy provides a custom API for reading and writing multipart types, as well as marshaling arbitrary List (for any multipart type) and Map ( multipart/form-data only) objects.

20.1. Input with multipart/mixed

When you write a JAX-RS service, RESTEasy provides an interface to let you read any multipart MIME type: org.jboss.resteasy.plugins.providers.multipart.MultipartInput . 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 lets you access each part of the multipart message. Each part is represented by an InputPart interface, and is associated with a set of headers. You can unmarshal a part by calling one of the getBody() methods. The Type genericType parameter can be null, but the Class type parameter must be set. RESTEasy locates a MessageBodyReader based on the media type of the part, and the type information you pass in. The following piece of code unmarshals XML parts into a JAXB annotated class called Customer . @Path("/multipart") public class MyService @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); If you want to unmarshal a body part that is sensitive to generic type metadata, you can use the org.jboss.resteasy.util.GenericType class, like so: @Path("/multipart") public class MyService @Consumes("multipart/mixed") public void put(MultipartInput input) for (InputPart part : input.getParts()) List<Customer> cust = part.getBody(new GenericType>List>Customer<<() {}); GenericType is required here because it is the only way to obtain generic type information at runtime.

20.2. java.util.List with multipart data

If the body parts are uniform, you can provide a java.util.List as your input parameter and avoid unmarshaling each part manually. As you can see in the example code below, this must include the type being unmarshaled with the generic parameter of the List type declaration. @Path("/multipart") public class MyService @Consumes("multipart/mixed") public void put(List<Customer> customers)

20.3. Input with multipart/form-data

When you write a JAX-RS service, RESTEasy provides an interface that lets you read the multipart/form-data MIME type. multipart/form-data is often found in web application HTML Form documents, and is generally used to upload files. The form-data format is like other multipart formats except that each inlined piece of content is associated with a name. The interface for form-data input is org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput . public interface MultipartFormDataInput extends MultipartInput @Deprecated Map<String, InputPart> getFormData(); Map<String, List<InputPart>> getFormDataMap(); <T> T getFormDataPart(String key, Class<T> rawType, Type genericType) throws IOException; <T> T getFormDataPart(String key, GenericType<T> type) throws IOException; This works similarly to MultipartInput , as described earlier in this chapter.

20.4. java.util.Map with multipart/form-data

With form-data , if the body parts are uniform, you can provide a java.util.Map as your input parameter and avoid unmarshaling each part manually. As you can see in the example code below, this must include the type being unmarshaled with the generic parameter of the List type declaration. @Path("/multipart") public class MyService @Consumes("multipart/form-data") public void put(Map<String, Customer> customers)

20.5. Input with multipart/related

When you write a JAX-RS service, RESTEasy provides an interface that lets you read the multipart/related MIME type. multipart/related indicates that message parts should be considered as parts of a whole, and not individually. You can use multipart/related to perform tasks like sending a web page complete with images in a single message. Every multipart/related message has a root/start part that references other parts of the message. Parts are identified by their Content-ID headers. multipart/related is defined by RFC 2387. The interface for related input is org.jboss.resteasy.plugins.providers.multipart.MultipartRelatedInput . public interface MultipartRelatedInput extends MultipartInput String getType(); String getStart(); String getStartInfo(); InputPart getRootPart(); Map<String, InputPart> getRelatedMap(); It works similarly to MultipartInput , as described earlier in this chapter.

20.6. Output with multipart

RESTEasy provides a simple API to output multipart data. package org.jboss.resteasy.plugins.providers.multipart; public class MultipartOutput public OutputPart addPart(Object entity, MediaType mediaType) public OutputPart addPart(Object entity, GenericType type, MediaType mediaType) public OutputPart addPart(Object entity, Class type, Type genericType, MediaType mediaType) public List<OutputPart> getParts() public String getBoundary() public void setBoundary(String boundary) public class OutputPart public MultivaluedMap<String, Object> getHeaders() public Object getEntity() public Class getType() public Type getGenericType() public MediaType getMediaType() To output multipart data, create a MultipartOutput object and call addPart() methods. RESTEasy automatically finds a MessageBodyWriter to marshal your entity objects. As with MultipartInput , your marshaling may be sensitive to generic type metadata. In this case, use GenericType . The following example returns a multipart/mixed format to the calling client. The parts are JAXB-annotated Customer objects that will marshal into application/xml . @Path("/multipart") public class MyService @Produces("multipart/mixed") public MultipartOutput get() MultipartOutput output = new MultipartOutput(); output.addPart(new Customer("bill"), MediaType.APPLICATION_XML_TYPE); output.addPart(new Customer("monica"), MediaType.APPLICATION_XML_TYPE); return output;

20.7. Multipart Output with java.util.List

If the body parts are uniform, you can provide a java.util.Map as your input parameter and avoid unmarshaling each part manually or using a MultipartOutput object. As you can see in the example code below, this must include the type being unmarshaled with the generic parameter of the List type declaration. You must also annotate the method with @PartType to specify each part's media type. The following example returns a customer list to a client, where each customer is a JAXB object: @Path("/multipart") public class MyService @Produces("multipart/mixed") @PartType("application/xml") public List<Customer> get()

20.8. Output with multipart/form-data

RESTEasy provides a simple API to output multipart/form-data. package org.jboss.resteasy.plugins.providers.multipart; public class MultipartFormDataOutput extends MultipartOutput public OutputPart addFormData(String key, Object entity, MediaType mediaType) public OutputPart addFormData(String key, Object entity, GenericType type, MediaType mediaType) public OutputPart addFormData(String key, Object entity, Class type, Type genericType, MediaType mediaType) public Map<String, OutputPart> getFormData() To output multipart/form-data , create a MultipartFormDataOutput object and call addFormData() methods. RESTEasy automatically locates a MessageBodyWriter to marshal your entity objects. As with MultipartInput , your marshaling may be sensitive to generic type metadata. In this case, use GenericType . The example below returns a multipart/form-data format to a calling client. The parts are JAXB-annotated Customer objects, which will be marshaled into application/xml . @Path("/form") public class MyService @Produces("multipart/form-data") public MultipartFormDataOutput get() MultipartFormDataOutput output = new MultipartFormDataOutput(); output.addPart("bill", new Customer("bill"), MediaType.APPLICATION_XML_TYPE); output.addPart("monica", new Customer("monica"), MediaType.APPLICATION_XML_TYPE); return output;

20.9. Multipart FormData Output with java.util.Map

If the body parts are uniform, you can provide a java.util.Map as your input parameter and avoid unmarshaling each part manually or using a MultipartFormDataOutput object. As you can see in the example code below, this must include the type being unmarshaled with the generic parameter of the List type declaration. You must also annotate the method with @PartType to specify each part's media type. This example returns a customer list to a client, where each customer is a JAXB object. @Path("/multipart") public class MyService @Produces("multipart/form-data") @PartType("application/xml") public Map<String, Customer> get()