The Providers Sample demonstrates how to create custom MessageBodyReaders and MessageBodyWriters to support additional media types not supported by default by the IBM JAX-RS runtime.
Before looking at this sample specifically, we'll take a look at the API which allow the JAX-RS runtime to be extended to support various media types.
We'll then examine the components of the Providers Sample to demonstrate how to extend the JAX-RS runtime in this manner.
In JAX-RS, entity providers provide mappings between represenations and their Java types. When a request is made to a resource implementation, entity providers are used to marshall/unmarshall the message body to and from the corresponding Java type.
There are two type of providers, MessageBodyReaders and MessageBodyWriters. Both interfaces can be implemented to create custom providers for any media type. Extending the runtime in this manner is the focus of this sample.
Implementations implement the MessgeBodyReader and/or MessageBodyWriter interfaces and are annotatd with the @Provider annotation.
MessageBodyReader implentations are used to unmarshall represenations to a Java type. The following methods define the interface:
|
The isReadable method determines if the reader is able to process the message body.
The readFrom method does the work to convert the message body to the corresponding Java type T via the provided InputStream.
MessageBodyWriter implementations are used to marshall Java Objects to a representation which is put on the response message body. The following methods define the interface:
|
The getSize method determines the number of bytes to be written to the provided OutputStream in the writeTo method.
The isWriteable method determines whether or not the writer is able to process the Java type and convert an instance to the corresponding representation.
The writeTo method does the work to convert the Java Object of type T to the representation that is put on the response message body via the provided OutputStream.
As mentioned earlier, entity provider implementations must be annotated with the @Provider annotation. Additionally the @Consumes and @Produces annotations can be used to explicitly declare a supported media type(s) and to more efficiently determine if a provider implementation should be used.
MessageBodyReaders annotated with the @Consumes annotation will allow the runtime to quickly eliminate candidate providers based on the annotation value. For example, if the annotation value on ReaderA is "text/xml", @Consumes(value="text/xml"), and the Content-Type header value of an incoming request is "text/plain," the runtime can immediately determine that ReaderA is not a candidate to unmarshall the message body on the request.
Likewise, the @Produces annotation can be used to narrow the list of candidate MessageBodyWriters that can be used to marshall Java Objects for an outgoing response.
Before the runtime will process custom entity providers, they must be returned by an instance of javax.ws.rs.core.Application. The Application class returns resource and provider classes and instances for use within applications.
Lastly, before we look at the Provider Sample implementations it helps to understand the process flow that takes place when a request is received and the repsonse is sent out, and where the entity providers are invoked in that process.
In the Providers Sample, the runtime is being extended to support the intger/text and integer/int media types. These are not standard media types.
IntegerResource is a simple resource class that wraps an Integer Object. The root path for the resource class is /integer.
|
There are two primary resource methods to note. getInteger and setInteger are the GET and POST methods and produce/accept both the integer/text and integer/int media types.
|
IntegerTextProvider implements both the MessageBodyReader and MessageBodyWriter interfaces. It consumes and produces the integer/text media type. By implementing the reader and writer interfaces, the integer/text media type is now supported.
|
The isReadable method is implemented so that true is returned for the Integer type.
|
When an @POST request is made to the IntegerResource.setInteger(Integer) method, the runtime must decide which MessageBodyReader will be used to unmarshall the Integer from the request body. Since IntegerTextProvider and IntegerIntProvider both implement the MessageBodyReader interface, and both have isReadable methods that return true for the Integer type, the Content-Type is used to determine that IntegerTextProvider should be used for the integer/text media type.
The readFrom method is implemented to read the body and convert it to an Integer which is then passed to the setInteger method of the IntegerResource class.
|
Like isReadable, isWritable is implemented so that true is returned for the Integer type.
|
When an @GET request is made to the IntegerResource.getInteger() method, the runtime must decide which MessageBodyWriter will be used to marshall the Integer for the response. Since IntegerTextProvider and IntegerIntProvider both implement the MessageBodyWriter interface, and both have isWritable methods that return true for the Integer type, the Accpet header is used to determine that IntegerTextProvider should be used for the integer/text media type.
NOTE: If no Content-Type is specified on the request for a POST or no Accept is specified on the request for a GET, or if the media type is ambiguous (ex: */*), the runtime may choose any supporting implementation based on the isReadable and isWritable return values.
The writeTo method is implemented to convert an Integer to a text String.
|
IntegerIntProvider is very similar to IntegerTextProvider except that it produces and consumes the integer/int media type. Rather than converting Strings to Integer and vice versa, the provider converts Integers to and from their binary representations. Implementing the reader and writer interfaces in this manner adds support for the integer/int media type.
|
The ProvidersApplication class declares the sample resource and provider classes via the getClasses() and getSingletons() methods. In this case, the IntegerResource class is returned in getClasses() meaning it's scope is limited to single requests. The custom entity providers are returned via the getSingletons() method meaning a single instance is used to unmarshall/marshall data for all requests.
|