The runtime relies on JAX-RS MessageBodyReader
and
MessageBodyWriter
implementations to serialize and de-serialize
data between the HTTP messages and their Java representations. The readers and writers can restrict
the MIME types they are capable of processing.
The runtime provides readers and writers for a number of common mappings. If an application
requires more advanced mappings, a developer can provide custom implementations of the
MessageBodyReader
interface and/or the
MessageBodyWriter
interface. Custom readers and writers are
registered with the runtime when the application is started.
Table 7.1 lists the entity mappings provided by Apache CXF out of the box.
Table 7.1. Natively supported entity mappings
Java Type | MIME Type |
---|---|
primitive types | text/plain |
java.lang.Number | text/plain |
byte[] | */* |
java.lang.String | */* |
java.io.InputStream | */* |
java.io.Reader | */* |
java.io.File | */* |
javax.activation.DataSource | */* |
javax.xml.transform.Source | text/xml , application/xml ,
application/*+xml |
javax.xml.bind.JAXBElement | text/xml , application/xml ,
application/*+xml |
JAXB annotated objects | text/xml , application/xml ,
application/*+xml |
javax.ws.rs.core.MultivaluedMap<String, String> | application/x-www-form-urlencoded
[a] |
javax.ws.rs.core.StreamingOutput | */*
[b] |
[a] This mapping is used for handling HTML form data. [b] This mapping is only supported for returning data to a consumer. |
Custom entity readers are responsible for mapping incoming HTTP requests into a Java type that a
service's implementation can manipulate. They implement the
javax.ws.rs.ext.MessageBodyReader
interface.
The interface, shown in Example 7.1, has two methods that need implementing:
Example 7.1. Message reader interface
package javax.ws.rs.ext; public interface MessageBodyReader<T> { public boolean isReadable(java.lang.Class<?> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, javax.ws.rs.core.MediaType mediaType); public T readFrom(java.lang.Class<T> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, javax.ws.rs.core.MediaType mediaType, javax.ws.rs.core.MultivaluedMap<String, String> httpHeaders, java.io.InputStream entityStream) throws java.io.IOException, WebApplicationException; }
isReadable()
The
isReadable()
method determines if the reader is capable of reading the data stream and creating the proper type of entity representation. If the reader can create the proper type of entity the method returnstrue
.Table 7.2 describes the
isReadable()
method's parameters.Table 7.2. Parameters used to determine if a reader can produce an entity
Parameter Type Description type
Class<T>
Specifies the actual Java class of the object used to store the entity. genericType
Type
Specifies the Java type of the object used to store the entity. For example, if the message body is to be converted into a method parameter, the value will be the type of the method parameter as returned by the Method.getGenericParameterTypes()
method.annotations
Annotation[]
Specifies the list of annotations on the declaration of the object created to store the entity. For example if the message body is to be converted into a method parameter, this will be the annotations on that parameter returned by the Method.getParameterAnnotations()
method.mediaType
MediatType
Specifies the MIME type of the HTTP entity. readFrom()
The
readFrom()
method reads the HTTP entity and coverts it into the desired Java object. If the reading is successful the method returns the created Java object containing the entity. If an error occurs when reading the input stream the method should throw anIOException
exception. If an error occurs that requires an HTTP error response, anWebApplicationException
with the HTTP response should be thrown.Table 7.3 describes the
readFrom()
method's parameters.Table 7.3. Parameters used to read an entity
Parameter Type Description type
Class<T>
Specifies the actual Java class of the object used to store the entity. genericType
Type
Specifies the Java type of the object used to store the entity. For example, if the message body is to be converted into a method parameter, the value will be the type of the method parameter as returned by the Method.getGenericParameterTypes()
method.annotations
Annotation[]
Specifies the list of annotations on the declaration of the object created to store the entity. For example if the message body is to be converted into a method parameter, this will be the annotations on that parameter returned by the Method.getParameterAnnotations()
method.mediaType
MediatType
Specifies the MIME type of the HTTP entity. httpHeaders
MultivaluedMap<String, String>
Specifies the HTTP message headers associated with the entity. entityStream
InputStream
Specifies the input stream containing the HTTP entity. Important This method should not close the input stream.
Before an MessageBodyReader
implementation can be used as an
entity reader, it must be decorated with the javax.ws.rs.ext.Provider
annotation. The @Provider
annotation alerts the runtime that the
supplied implementation provides additional functionality. The implementation must also be registered
with the runtime as described in Registering readers and writers.
By default a custom entity provider handles all MIME types. You can limit the MIME types that a
custom entity reader will handle using the javax.ws.rs.Consumes
annotation. The @Consumes
annotation specifies a comma separated
list of MIME types that the custom entity provider reads. If an entity is not of a specified
MIME type, the entity provider will not be selected as a possible reader.
Example 7.2 shows an entity reader the consumes
XML entities and stores them in a Source
object.
Example 7.2. XML source entity reader
import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.Provider; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import org.w3c.dom.Document; import org.apache.cxf.jaxrs.ext.xml.XMLSource; @Provider @Consumes({"application/xml", "application/*+xml", "text/xml", "text/html" }) public class SourceProvider implements MessageBodyReader<Object> { public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) { return Source.class.isAssignableFrom(type) || XMLSource.class.isAssignableFrom(type); } public Object readFrom(Class<Object> source, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream is) throws IOException { if (DOMSource.class.isAssignableFrom(source)) { Document doc = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder; try { builder = factory.newDocumentBuilder(); doc = builder.parse(is); } catch (Exception e) { IOException ioex = new IOException("Problem creating a Source object"); ioex.setStackTrace(e.getStackTrace()); throw ioex; } return new DOMSource(doc); } else if (StreamSource.class.isAssignableFrom(source) || Source.class.isAssignableFrom(source)) { return new StreamSource(is); } else if (XMLSource.class.isAssignableFrom(source)) { return new XMLSource(is); } throw new IOException("Unrecognized source"); } }
Custom entity writers are responsible for mapping Java types into HTTP entities. They implement the
javax.ws.rs.ext.MessageBodyWriter
interface.
The interface, shown in Example 7.3, has three methods that need implementing:
Example 7.3. Message writer interface
package javax.ws.rs.ext; public interface MessageBodyWriter<T> { public boolean isWriteable(java.lang.Class<?> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, javax.ws.rs.core.MediaType mediaType); public long getSize(T t, java.lang.Class<?> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, javax.ws.rs.core.MediaType mediaType); public void writeTo(T t, java.lang.Class<?> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, javax.ws.rs.core.MediaType mediaType, javax.ws.rs.core.MultivaluedMap<String, Object> httpHeaders, java.io.OutputStream entityStream) throws java.io.IOException, WebApplicationException; }
isWriteable()
The
isWriteable()
method determines if the entity writer can map the Java type to the proper entity type. If the writer can do the mapping, the method returnstrue
.Table 7.4 describes the
isWritable()
method's parameters.Table 7.4. Parameters used to read an entity
Parameter Type Description type
Class<T>
Specifies the Java class of the object being written. genericType
Type
Specifies the Java type of object to be written, obtained either by reflection of a resource method return type or via inspection of the returned instance. The GenericEntity
class, described in Returning entities with generic type information, provides support for controlling this value.annotations
Annotation[]
Specifies the list of annotations on the method returning the entity. mediaType
MediatType
Specifies the MIME type of the HTTP entity. getSize()
The
getSize()
method is called before thewriteTo()
. It returns the length, in bytes, of the entity being written. If a positive value is returned the value is written into the HTTP message'sContent-Length
header.Table 7.5 describes the
getSize()
method's parameters.Table 7.5. Parameters used to read an entity
Parameter Type Description t
generic Specifies the instance being written. type
Class<T>
Specifies the Java class of the object being written. genericType
Type
Specifies the Java type of object to be written, obtained either by reflection of a resource method return type or via inspection of the returned instance. The GenericEntity
class, described in Returning entities with generic type information, provides support for controlling this value.annotations
Annotation[]
Specifies the list of annotations on the method returning the entity. mediaType
MediatType
Specifies the MIME type of the HTTP entity. writeTo()
The
writeTo()
method converts a Java object into the desired entity type and writes the entity to the output stream. If an error occurs when writing the entity to the output stream the method should throw anIOException
exception. If an error occurs that requires an HTTP error response, anWebApplicationException
with the HTTP response should be thrown.Table 7.6 describes the
writeTo()
method's parameters.Table 7.6. Parameters used to read an entity
Parameter Type Description t
generic Specifies the instance being written. type
Class<T>
Specifies the Java class of the object being written. genericType
Type
Specifies the Java type of object to be written, obtained either by reflection of a resource method return type or via inspection of the returned instance. The GenericEntity
class, described in Returning entities with generic type information, provides support for controlling this value.annotations
Annotation[]
Specifies the list of annotations on the method returning the entity. mediaType
MediatType
Specifies the MIME type of the HTTP entity. httpHeaders
MultivaluedMap<String, Object>
Specifies the HTTP response headers associated with the entity. entityStream
OutputStream
Specifies the output stream into which the entity is written.
Before a MessageBodyWriter
implementation can be used as an
entity writer, it must be decorated with the javax.ws.rs.ext.Provider
annotation. The @Provider
annotation alerts the runtime that the
supplied implementation provides additional functionality. The implementation must also be registered
with the runtime as described in Registering readers and writers.
By default a custom entity provider handles all MIME types. You can limit the MIME types that a
custom entity writer will handle using the javax.ws.rs.Produces
annotation. The @Produces
annotation specifies a comma separated
list of MIME types that the custom entity provider generates. If an entity is not of a specified
MIME type, the entity provider will not be selected as a possible writer.
Example 7.4 shows an entity writer that takes
Source
objects and produces XML entities.
Example 7.4. XML source entity writer
import java.io.IOException; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.apache.cxf.jaxrs.ext.xml.XMLSource; @Provider @Produces({"application/xml", "application/*+xml", "text/xml" }) public class SourceProvider implements MessageBodyWriter<Source> { public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) { return Source.class.isAssignableFrom(type); } public void writeTo(Source source, Class<?> clazz, Type genericType, Annotation[] annotations, MediaType mediatype, MultivaluedMap<String, Object> httpHeaders, OutputStream os) throws IOException { StreamResult result = new StreamResult(os); TransformerFactory tf = TransformerFactory.newInstance(); try { Transformer t = tf.newTransformer(); t.transform(source, result); } catch (TransformerException te) { te.printStackTrace(); throw new WebApplicationException(te); } } public long getSize(Source source, Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) { return -1; } }
Before a JAX-RS application can use any custom entity providers, the custom
providers must be registered with the runtime. Providers are registered with the
runtime using either the jaxrs:providers
element in the application's
configuration file or using the JAXRSServerFactoryBean
class.
The jaxrs:providers
element is a child of
the jaxrs:server
element and contains a list of
bean
elements. Each bean
element defines one entity provider.
Example 7.5 show a JAX-RS server configured to use a set of custom entity providers.
Example 7.5. Registering entity providers with the runtime
<beans ...> <jaxrs:server id="customerService" address="/"> ... <jaxrs:providers> <bean id="isProvider" class="com.bar.providers.InputStreamProvider"/> <bean id="longProvider" class="com.bar.providers.LongProvider"/> </jaxrs:providers> </jaxrs:server> </beans>
The JAXRSServerFactoryBean
class is a Apache CXF extension that provides
access to the configuration APIs. It has a setProvider()
method that
allows you to add instantiated entity providers to an application.
Example 7.6 shows code for registering an entity provider
programmatically.
Example 7.6. Programmatically registering an entity provider
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; ... JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); ... SourceProvider provider = new SourceProvider(); sf.setProvider(provider); ...