| <?xml version="1.0"?> |
| <!-- |
| |
| Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. |
| |
| This program and the accompanying materials are made available under the |
| terms of the Eclipse Public License v. 2.0, which is available at |
| http://www.eclipse.org/legal/epl-2.0. |
| |
| This Source Code may also be made available under the following Secondary |
| Licenses when the conditions for such availability set forth in the |
| Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| version 2 with the GNU Classpath Exception, which is available at |
| https://www.gnu.org/software/classpath/license.html. |
| |
| SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| |
| --> |
| |
| <!DOCTYPE chapter [<!ENTITY % ents SYSTEM "jersey.ent" > %ents;]> |
| <chapter xmlns="http://docbook.org/ns/docbook" |
| version="5.0" |
| xml:lang="en" |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| xmlns:xi="http://www.w3.org/2001/XInclude" |
| xmlns:xlink="http://www.w3.org/1999/xlink" |
| xsi:schemaLocation="http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd |
| http://www.w3.org/1999/xlink http://www.w3.org/1999/xlink.xsd" |
| xml:id="message-body-workers"> |
| <title>JAX-RS Entity Providers</title> |
| <section> |
| <title>Introduction</title> |
| <para> |
| Entity payload, if present in an received HTTP message, is passed to Jersey from an I/O container as an input stream. |
| The stream may, for example, contain data represented as a plain text, XML or JSON document. However, in many JAX-RS |
| components that process these inbound data, such as resource methods or client responses, the JAX-RS API user can |
| access the inbound entity as an arbitrary Java object that is created from the content of the input stream based on |
| the representation type information. For example, an entity created from an input stream that contains data |
| represented as a XML document, can be converted to a custom JAXB bean. |
| Similar concept is supported for the outbound entities. An entity returned from the resource method in the form |
| of an arbitrary Java object can be serialized by Jersey into a container output stream as a specified representation. |
| Of course, while JAX-RS implementations do provide default support for most common combinations of Java type and it's |
| respective on-the-wire representation formats, JAX-RS implementations do not support the conversion described above |
| for any arbitrary Java type and any arbitrary representation format by default. Instead, a generic extension concept |
| is exposed in JAX-RS API to allow application-level customizations of this JAX-RS runtime to support for entity |
| conversions. The JAX-RS extension API components that provide the user-level extensibility are typically referred to |
| by several terms with the same meaning, such as <emphasis>entity providers</emphasis>, |
| <emphasis>message body providers</emphasis>, <emphasis>message body workers</emphasis> or |
| <emphasis>message body readers and writers</emphasis>. You may find all these terms used interchangeably throughout |
| the user guide and they all refer to the same concept. |
| </para> |
| <para> |
| In JAX-RS extension API (or SPI - service provider interface, if you like) the concept is captured in 2 interfaces. |
| One for handling inbound entity representation-to-Java de-serialization - &jaxrs.ext.MessageBodyReader; and the other |
| one for handling the outbound entity Java-to-representation serialization - &jaxrs.ext.MessageBodyWriter;. |
| A &lit.jaxrs.ext.MessageBodyReader;, as the name suggests, is an extension that supports reading the message body |
| representation from an input stream and converting the data into an instance of a specific Java type. |
| A &lit.jaxrs.ext.MessageBodyWriter; is then responsible for converting a message payload from an instance of a |
| specific Java type into a specific representation format that is sent over the wire to the other party as part of an |
| HTTP message exchange. |
| Both of these providers can be used to provide message payload serialization and de-serialization support on the |
| server as well as the client side. A message body reader or writer is always used whenever a HTTP request or |
| response contains an entity and the entity is either requested by the application code (e.g. injected as a parameter |
| of JAX-RS resource method or a response entity read on the client from a &jaxrs.core.Response;) or has to be |
| serialized and sent to the other party (e.g. an instance returned from a JAX-RS resource method or a request |
| entity sent by a JAX-RS client). |
| </para> |
| </section> |
| <section> |
| <title>How to Write Custom Entity Providers</title> |
| <para> |
| A best way how to learn about entity providers is to walk through an example of writing one. Therefore we will |
| describe here the process of implementing a custom &jaxrs.ext.MessageBodyWriter; and |
| &jaxrs.ext.MessageBodyReader; using a practical example. Let's first setup the stage by defining a JAX-RS |
| resource class for the server side story of our application. |
| |
| <example> |
| <title>Example resource class</title> |
| <programlisting language="java" linenumbering="numbered">@Path("resource") |
| public class MyResource { |
| @GET |
| @Produces("application/xml") |
| public MyBean getMyBean() { |
| return new MyBean("Hello World!", 42); |
| } |
| |
| @POST |
| @Consumes("application/xml") |
| public String postMyBean(MyBean myBean) { |
| return myBean.anyString; |
| } |
| }</programlisting> |
| </example> |
| |
| The resource class defines &lit.http.GET; and &lit.http.POST; resource methods. Both methods work with an entity |
| that is an instance of <literal>MyBean</literal>. |
| </para> |
| <para> |
| The <literal>MyBean</literal> class is defined in the next example: |
| |
| <example> |
| <title>MyBean entity class</title> |
| <programlisting language="java" linenumbering="numbered">@XmlRootElement |
| public class MyBean { |
| @XmlElement |
| public String anyString; |
| @XmlElement |
| public int anyNumber; |
| |
| public MyBean(String anyString, int anyNumber) { |
| this.anyString = anyString; |
| this.anyNumber = anyNumber; |
| } |
| |
| // empty constructor needed for deserialization by JAXB |
| public MyBean() { |
| } |
| |
| @Override |
| public String toString() { |
| return "MyBean{" + |
| "anyString='" + anyString + '\'' + |
| ", anyNumber=" + anyNumber + |
| '}'; |
| } |
| }</programlisting> |
| </example> |
| </para> |
| <section> |
| <title>MessageBodyWriter</title> |
| <para> |
| The <literal>MyBean</literal> is a JAXB-annotated POJO. In &lit.http.GET; resource method we return |
| the instance of MyBean and we would like Jersey runtime to serialize it into XML and write |
| it as an entity body to the response output stream. We design a custom &lit.jaxrs.ext.MessageBodyWriter; |
| that can serialize this POJO into XML. See the following code sample: |
| |
| <note> |
| <para> |
| Please note, that this is only a demonstration of how to write a custom entity provider. |
| Jersey already contains default support for entity providers that can serialize JAXB beans |
| into XML. |
| </para> |
| </note> |
| |
| <example> |
| <title>MessageBodyWriter example</title> |
| <programlisting language="java" linenumbering="numbered"><![CDATA[@Produces("application/xml") |
| public class MyBeanMessageBodyWriter implements MessageBodyWriter<MyBean> { |
| |
| @Override |
| public boolean isWriteable(Class<?> type, Type genericType, |
| Annotation[] annotations, MediaType mediaType) { |
| return type == MyBean.class; |
| } |
| |
| @Override |
| public long getSize(MyBean myBean, Class<?> type, Type genericType, |
| Annotation[] annotations, MediaType mediaType) { |
| // deprecated by JAX-RS 2.0 and ignored by Jersey runtime |
| return -1; |
| } |
| |
| @Override |
| public void writeTo(MyBean myBean, |
| Class<?> type, |
| Type genericType, |
| Annotation[] annotations, |
| MediaType mediaType, |
| MultivaluedMap<String, Object> httpHeaders, |
| OutputStream entityStream) |
| throws IOException, WebApplicationException { |
| |
| try { |
| JAXBContext jaxbContext = JAXBContext.newInstance(MyBean.class); |
| |
| // serialize the entity myBean to the entity output stream |
| jaxbContext.createMarshaller().marshal(myBean, entityStream); |
| } catch (JAXBException jaxbException) { |
| throw new ProcessingException( |
| "Error serializing a MyBean to the output stream", jaxbException); |
| } |
| } |
| }]]></programlisting> |
| </example> |
| </para> |
| <para> |
| The <literal>MyBeanMessageBodyWriter</literal> implements the &lit.jaxrs.ext.MessageBodyWriter; |
| interface that contains three methods. In the next sections we'll explore these methods more closely. |
| </para> |
| <section> |
| <title> |
| <literal>MessageBodyWriter.isWriteable</literal> |
| </title> |
| <para> |
| A method <literal>isWriteable</literal> |
| should return true if the &lit.jaxrs.ext.MessageBodyWriter; is able to write the given type. Method |
| does not decide only based on the Java type of the entity but also on annotations attached to the entity |
| and the requested representation media type. |
| </para> |
| <para> |
| Parameters <literal>type</literal> and <literal>genericType</literal> both define the entity, |
| where <literal>type</literal> is a raw Java type |
| (for example, a <literal>java.util.List</literal> class) and <literal>genericType</literal> is a |
| &jdk6.ParameterizedType; including generic information (for example <literal>List<String></literal>). |
| </para> |
| <para> |
| Parameter <literal>annotations</literal> contains annotations that are either attached to the resource |
| method and/or annotations that are attached to the entity by building response like in the following piece |
| of code: |
| |
| <example> |
| <title>Example of assignment of annotations to a response entity</title> |
| <programlisting language="java" linenumbering="numbered">@Path("resource") |
| public static class AnnotatedResource { |
| |
| @GET |
| public Response get() { |
| Annotation annotation = AnnotatedResource.class |
| .getAnnotation(Path.class); |
| return Response.ok() |
| .entity("Entity", new Annotation[] {annotation}).build(); |
| } |
| }</programlisting> |
| </example> |
| |
| In the example above, the &lit.jaxrs.ext.MessageBodyWriter; would get |
| <literal>annotations</literal> parameter containing a JAX-RS <literal>@GET</literal> annotation |
| as it annotates the resource method and also a <literal>@Path</literal> annotation as it |
| is passed in the response (but not because it annotates the resource; only resource |
| method annotations are included). In the case of <literal>MyResource</literal> |
| and method <literal>getMyBean</literal> the annotations would contain the |
| &jaxrs.GET; and the &jaxrs.Produces; annotation. |
| </para> |
| <para> |
| The last parameter of the <literal>isWriteable</literal> method is the <literal>mediaType</literal> |
| which is the media type attached to the response entity by annotating the resource method with a |
| &lit.jaxrs.Produces; annotation or the request media type specified in the JAX-RS Client API. |
| In our example, the media type passed to providers for the resource <literal>MyResource</literal> and method |
| <literal>getMyBean</literal> would be <literal>"application/xml"</literal>. |
| </para> |
| <para> |
| In our implementation of the <literal>isWriteable</literal> method, we |
| just check that the type is <literal>MyBean</literal>. Please note, that |
| this method might be executed multiple times by Jersey runtime as Jersey needs to check |
| whether this provider can be used for a particular combination of entity Java type, media type, and attached |
| annotations, which may be potentially a performance hog. You can limit the number of execution by |
| properly defining the &lit.jaxrs.Produces; annotation on the &lit.jaxrs.ext.MessageBodyWriter;. |
| In our case thanks to &lit.jaxrs.Produces; annotation, the provider will be considered |
| as writeable (and the method <literal>isWriteable</literal> might be executed) only if the |
| media type of the outbound message is <literal>"application/xml"</literal>. Additionally, the provider |
| will only be considered as possible candidate and its <literal>isWriteable</literal> method will |
| be executed, if the generic type of the provider is either a sub class or super class of |
| <literal>type</literal> parameter. |
| </para> |
| </section> |
| <section> |
| <title> |
| <literal>MessageBodyWriter.writeTo</literal> |
| </title> |
| <para> |
| Once a message body writer is selected as the most appropriate (see the <xref linkend="providers-selection" /> |
| for more details on entity provider selection), its <literal>writeTo</literal> method is invoked. This method |
| receives parameters with the same meaning as in <literal>isWriteable</literal> as well as a few additional ones. |
| </para> |
| <para> |
| In addition to the parameters already introduced, the <literal>writeTo</literal> method defies also |
| <literal>httpHeaders</literal> parameter, that contains HTTP headers associated with the |
| outbound message. |
| |
| <note> |
| <para> |
| When a &lit.jaxrs.ext.MessageBodyWriter; is invoked, the headers still can be modified in this point |
| and any modification will be reflected in the outbound HTTP message being sent. The modification of |
| headers must however happen before a first byte is written to the supplied output stream. |
| </para> |
| </note> |
| </para> |
| <para> |
| Another new parameter, <literal>myBean</literal>, contains the entity instance to be serialized (the type of |
| entity corresponds to generic type of &lit.jaxrs.ext.MessageBodyWriter;). Related parameter |
| <literal>entityStream</literal> contains the entity output stream to which the method should serialize the entity. |
| In our case we use JAXB to marshall the entity into the <literal>entityStream</literal>. Note, that the |
| <literal>entityStream</literal> is not closed at the end of method; the stream will be closed by Jersey. |
| <important> |
| <para> |
| Do not close the entity output stream in the <literal>writeTo</literal> method of your |
| &lit.jaxrs.ext.MessageBodyWriter; implementation. |
| </para> |
| </important> |
| </para> |
| </section> |
| |
| <section> |
| <title> |
| <literal>MessageBodyWriter.getSize</literal> |
| </title> |
| <para> |
| The method is deprecated since JAX-RS 2.0 and Jersey 2 ignores the return value. In JAX-RS 1.0 the |
| method could return the size of the entity that would be then used for "Content-Length" response |
| header. In Jersey 2.0 the "Content-Length" parameter is computed automatically using an internal |
| outbound entity buffering. For details about configuration options of outbound entity buffering see the javadoc |
| of &jersey.message.MessageProperties;, property <literal>OUTBOUND_CONTENT_LENGTH_BUFFER</literal> |
| which configures the size of the buffer. |
| <note> |
| <para> |
| You can disable the Jersey outbound entity buffering by setting the buffer size to 0. |
| </para> |
| </note> |
| </para> |
| </section> |
| <section> |
| <title>Testing a &lit.jaxrs.ext.MessageBodyWriter;</title> |
| <para> |
| Before testing the <literal>MyBeanMessageBodyWriter</literal>, the writer must |
| be registered as a custom JAX-RS extension provider. It should either be added to your application |
| &jersey.server.ResourceConfig;, or returned from your custom &jaxrs.core.Application; sub-class, or |
| annotated with &jaxrs.ext.Provider; annotation to leverage JAX-RS provider auto-discovery feature. |
| </para> |
| <para> |
| After registering the <literal>MyBeanMessageBodyWriter</literal> and <literal>MyResource</literal> class |
| in our application, the request can be initiated (in this example from Client API). |
| |
| <example xml:id="client-get-call"> |
| <title>Client code testing MyBeanMessageBodyWriter</title> |
| <programlisting language="java" linenumbering="numbered" >WebTarget webTarget = // initialize web target to the context root |
| // of example application |
| Response response = webTarget.path("resource") |
| .request(MediaType.APPLICATION_XML).get(); |
| System.out.println(response.getStatus()); |
| String myBeanXml = response.readEntity(String.class); |
| System.out.println(myBeanXml);</programlisting> |
| </example> |
| |
| The client code initiates the &lit.http.GET; which will be matched to the resource method |
| <literal>MyResource.getMyBean()</literal>. The response entity is de-serialized as a <literal>String</literal>. |
| </para> |
| <para> |
| The result of console output is: |
| |
| <example> |
| <title>Result of MyBeanMessageBodyWriter test</title> |
| <screen language="text" linenumbering="unnumbered"><![CDATA[200 |
| <?xml version="1.0" encoding="UTF-8" standalone="yes"?><myBean> |
| <anyString>Hello World!</anyString><anyNumber>42</anyNumber></myBean>]]></screen> |
| </example> |
| |
| The returned status is 200 and the entity is stored in the response in a <literal>XML</literal> format. |
| Next, we will look at how the Jersey de-serializes this XML document into a <literal>MyBean</literal> consumed by |
| our &lit.http.POST; resource method. |
| </para> |
| </section> |
| </section> |
| <section> |
| <title>MessageBodyReader</title> |
| <para> |
| In order to de-serialize the entity of <literal>MyBean</literal> on the server or the client, we need to implement |
| a custom &jaxrs.ext.MessageBodyReader;. |
| <note> |
| <para> |
| Please note, that this is only a demonstration of how to write a custom entity provider. |
| Jersey already contains default support for entity providers that can serialize JAXB beans |
| into XML. |
| </para> |
| </note> |
| </para> |
| <para> |
| Our &lit.jaxrs.ext.MessageBodyReader; implementation is listed in <xref linkend="mbw.ex.mbr" />. |
| |
| <example xml:id="mbw.ex.mbr"> |
| <title>MessageBodyReader example</title> |
| <programlisting language="java" linenumbering="numbered"><![CDATA[public static class MyBeanMessageBodyReader |
| implements MessageBodyReader<MyBean> { |
| |
| @Override |
| public boolean isReadable(Class<?> type, Type genericType, |
| Annotation[] annotations, MediaType mediaType) { |
| return type == MyBean.class; |
| } |
| |
| @Override |
| public MyBean readFrom(Class<MyBean> type, |
| Type genericType, |
| Annotation[] annotations, MediaType mediaType, |
| MultivaluedMap<String, String> httpHeaders, |
| InputStream entityStream) |
| throws IOException, WebApplicationException { |
| |
| try { |
| JAXBContext jaxbContext = JAXBContext.newInstance(MyBean.class); |
| MyBean myBean = (MyBean) jaxbContext.createUnmarshaller() |
| .unmarshal(entityStream); |
| return myBean; |
| } catch (JAXBException jaxbException) { |
| throw new ProcessingException("Error deserializing a MyBean.", |
| jaxbException); |
| } |
| } |
| }]]></programlisting> |
| </example> |
| |
| It is obvious that the &lit.jaxrs.ext.MessageBodyReader; interface is similar to &lit.jaxrs.ext.MessageBodyWriter;. |
| In the next couple of sections we will explore it's API methods. |
| </para> |
| <section> |
| <title><literal>MessageBodyReader.isReadable</literal></title> |
| <para> |
| It defines the method <literal>isReadable()</literal> which has a very similar meaning as method |
| <literal>isWriteable()</literal> in &lit.jaxrs.ext.MessageBodyWriter;. The method returns &lit.true; |
| if it is able to de-serialize the given type. The <literal>annotations</literal> parameter contains annotations |
| that are attached to the entity parameter in the resource method. In our &lit.http.POST; resource |
| method <literal>postMyBean</literal> the entity parameter <literal>myBean</literal> is not |
| annotated, therefore no annotation will be passed to the isReadable. The <literal>mediaType</literal> |
| parameter contains the entity media type. The media type, in our case, must be consumable by the &lit.http.POST; |
| resource method, which is specified by placing a JAX-RS &jaxrs.Consumes; annotation to the method. |
| The resource method <literal>postMyBean()</literal> is annotated with |
| <literal>@Consumes("application/xml")</literal>, |
| therefore for purpose of de-serialization of entity for the <literal>postMyBean()</literal> method, |
| only requests with entities represented as <literal>"application/xml"</literal> |
| media type will match the method. However, this method might be executed for entity types that are sub classes |
| or super classes of the declared generic type on the &lit.jaxrs.ext.MessageBodyReader; will be also considered. |
| It is a responsibility of the <literal>isReadable</literal> method to decide whether it is able |
| to de-serialize the entity and type comparison is one of the basic decision steps. |
| <tip> |
| <para> |
| In order to reduce number of <literal>isReadable</literal> executions, always define correctly the consumable |
| media type(s) with the &lit.jaxrs.Consumes; annotation on your custom &lit.jaxrs.ext.MessageBodyReader;. |
| </para> |
| </tip> |
| </para> |
| </section> |
| <section> |
| <title><literal>MessageBodyReader.readFrom</literal></title> |
| <para> |
| The <literal>readForm()</literal> method gets the parameters with the same meaning as in |
| <literal>isReadable()</literal>. The additional <literal>entityStream</literal> parameter provides a handle |
| to the entity input stream from which the entity bytes should be read and de-serialized into a Java entity which |
| is then returned from the method. Our <literal>MyBeanMessageBodyReader</literal> de-serializes the incoming |
| XML data into an instance of <literal>MyBean</literal> using JAXB. |
| |
| <important> |
| <para> |
| Do not close the entity input stream in your &lit.jaxrs.ext.MessageBodyReader; implementation. The stream |
| will be automatically closed by Jersey runtime. |
| </para> |
| </important> |
| </para> |
| </section> |
| <section> |
| <title>Testing a &lit.jaxrs.ext.MessageBodyWriter;</title> |
| |
| <para> |
| Now let's send a test request using the JAX-RS Client API. |
| |
| <example> |
| <title>Testing MyBeanMessageBodyReader</title> |
| <programlisting language="java" linenumbering="numbered"><![CDATA[final MyBean myBean = new MyBean("posted MyBean", 11); |
| Response response = webTarget.path("resource").request("application/xml") |
| .post(Entity.entity(myBean, "application/xml")); |
| |
| System.out.println(response.getStatus()); |
| final String responseEntity = response.readEntity(String.class); |
| System.out.println(responseEntity); |
| ]]></programlisting> |
| </example> |
| </para> |
| <para> |
| The console output is: |
| |
| <example> |
| <title>Result of testing MyBeanMessageBodyReader</title> |
| <screen language="text" linenumbering="unnumbered"><![CDATA[200 |
| posted MyBean]]></screen> |
| </example> |
| </para> |
| </section> |
| <section> |
| <title>Using Entity Providers with JAX-RS Client API</title> |
| <para> |
| Both, &lit.jaxrs.ext.MessageBodyReader; and &lit.jaxrs.ext.MessageBodyWriter; can be registered in a |
| configuration of JAX-RS Client API components typically without any need to change their code. The example |
| <xref linkend="mbw.ex.client.mbr.reg" /> is a variation on the <xref linkend="client-get-call" /> |
| listed in one of the previous sections. |
| |
| <example xml:id="mbw.ex.client.mbr.reg"> |
| <title>MessageBodyReader registered on a JAX-RS client</title> |
| |
| <programlisting language="java" linenumbering="numbered"><![CDATA[Client client = ClientBuilder.newBuilder() |
| .register(MyBeanMessageBodyReader.class).build(); |
| |
| Response response = client.target("http://example/comm/resource") |
| .request(MediaType.APPLICATION_XML).get(); |
| System.out.println(response.getStatus()); |
| MyBean myBean = response.readEntity(MyBean.class); |
| System.out.println(myBean);]]></programlisting> |
| </example> |
| </para> |
| <para> |
| The code above registers <literal>MyBeanMessageBodyReader</literal> to the &jaxrs.client.Client; configuration |
| using a &jaxrs.client.ClientBuilder; which means that the provider will be used for any &jaxrs.client.WebTarget; |
| produced by the <literal>client</literal> instance. |
| <note> |
| <para> |
| You could also register the JAX-RS entity (and any other) providers to individual |
| &lit.jaxrs.client.WebTarget; instances produced by the client. |
| </para> |
| </note> |
| |
| Then, using the fluent chain of method invocations, a resource target pointing to our |
| <literal>MyResource</literal> is defined, a HTTP &lit.http.GET; request is invoked. |
| The response entity is then read as an instance of a <literal>MyBean</literal> type by invoking the |
| <literal>response.readEntity</literal> method, that internally locates the registered |
| <literal>MyBeanMessageBodyReader</literal> and uses it for entity de-serialization. |
| </para> |
| <para> |
| The console output for the example is: |
| <example> |
| <title>Result of client code execution</title> |
| <screen language="text" linenumbering="unnumbered"><![CDATA[200 |
| MyBean{anyString='Hello World!', anyNumber=42}]]></screen> |
| </example> |
| </para> |
| </section> |
| </section> |
| </section> |
| |
| <section xml:id="providers-selection"> |
| <title>Entity Provider Selection</title> |
| |
| <para> |
| Usually there are many entity providers registered on the server or client side (be default there must be |
| at least providers mandated by the JAX-RS specification, such as providers for primitive types, byte array, |
| JAXB beans, etc.). |
| JAX-RS defines an algorithm for selecting the most suitable provider for entity processing. This algorithm |
| works with information such as entity Java type and on-the-wire media type representation of entity, and searches |
| for the most suitable entity provider from the list of available providers based on the supported media type |
| declared on each provider (defined by &lit.jaxrs.Produces; or &lit.jaxrs.Consumes; on the provider class) |
| as well as based on the generic type declaration of the available providers. When a list of suitable candidate |
| entity providers is selected and sorted based on the rules defined in JAX-RS specification, a JAX-RS runtime |
| then it invokes <literal>isReadable</literal> or <literal>isWriteable</literal> method respectively on each |
| provider in the list until a first provider is found that returns &lit.true;. This provider is then used to |
| process the entity. |
| </para> |
| <para> |
| The following steps describe the algorithm for selecting a &lit.jaxrs.ext.MessageBodyWriter; (extracted |
| from JAX-RS with little modifications). The steps refer to the previously discussed example application. |
| The &lit.jaxrs.ext.MessageBodyWriter; is searched for purpose of deserialization of <literal>MyBean</literal> |
| entity returned from the method <literal>getMyBean</literal>. So, <emphasis>type is <literal>MyBean</literal> |
| and media type <literal>"application/xml"</literal></emphasis>. Let's assume the runtime contains also |
| registered providers, namely: |
| |
| <simplelist> |
| <member> |
| <literal>A</literal>: <literal>@Produces("application/*")</literal> with generic type |
| <literal><Object></literal> |
| </member> |
| <member> |
| <literal>B</literal>: <literal>@Produces("*/*")</literal> with generic type <literal><MyBean></literal> |
| </member> |
| <member> |
| <literal>C</literal>: <literal>@Produces("text/plain")</literal> with generic type |
| <literal><MyBean></literal> |
| </member> |
| <member> |
| <literal>D</literal>: <literal>@Produces("application/xml")</literal> with generic type |
| <literal><Object></literal> |
| </member> |
| <member> |
| <literal>MyBeanMessageBodyWriter</literal>: <literal>@Produces("application/xml")</literal> with generic |
| type <literal><MyBean></literal> |
| </member> |
| </simplelist> |
| </para> |
| <para> |
| The algorithm executed by a JAX-RS runtime to select a proper &lit.jaxrs.ext.MessageBodyWriter; implementation |
| is illustrated in <xref linkend="mbw.writer.selection.algorithm" />. |
| </para> |
| <procedure xml:id="mbw.writer.selection.algorithm"> |
| <title>&lit.jaxrs.ext.MessageBodyWriter; Selection Algorithm</title> |
| <step> |
| <para> |
| Obtain the object that will be mapped to the message entity body. For a return type of Response |
| or subclasses, the object is the value of the entity property, for other return types it is the returned |
| object. |
| </para> |
| <para> |
| So in our case, for the resource method <literal>getMyBean</literal> the type will |
| be <literal>MyBean</literal>. |
| </para> |
| </step> |
| <step> |
| <para> |
| Determine the media type of the response. |
| </para> |
| <para> |
| In our case, for resource method <literal>getMyBean</literal> |
| annotated with <literal>@Produces("application/xml")</literal>, the media type will be |
| <literal>"application/xml"</literal>. |
| </para> |
| </step> |
| <step> |
| <para> |
| Select the set of MessageBodyWriter providers that support the object and media |
| type of the message entity body. |
| </para> |
| <para> |
| In our case, for entity media type <literal>"application/xml"</literal> |
| and type <literal>MyBean</literal>, the appropriate &lit.jaxrs.ext.MessageBodyWriter; will |
| be the <literal>A</literal>, <literal>B</literal>, <literal>D</literal> |
| and <literal>MyBeanMessageBodyWriter</literal>. The provider <literal>C</literal> does |
| not define the appropriate |
| media type. <literal>A</literal> and <literal>B</literal> are fine as |
| their type is more generic and compatible with <literal>"application/xml"</literal>. |
| </para> |
| </step> |
| <step xml:id="mbw.writer.selection.algorithm.sortStep"> |
| <para> |
| Sort the selected MessageBodyWriter providers with a primary key of generic type where providers |
| whose generic type is the nearest superclass of the object class are sorted first and a secondary key of |
| media type. Additionally, JAX-RS specification mandates that custom, user registered providers have to |
| be sorted ahead of default providers provided by JAX-RS implementation. This is used as a tertiary |
| comparison key. User providers are places prior to Jersey internal providers in to the final ordered list. |
| </para> |
| <para> |
| The sorted providers will be: <literal>MyBeanMessageBodyWriter</literal>, |
| <literal>B</literal>. <literal>D</literal>, <literal>A</literal>. |
| </para> |
| </step> |
| <step> |
| <para> |
| Iterate through the sorted &lit.jaxrs.ext.MessageBodyWriter; providers and, utilizing the |
| <literal>isWriteable</literal> method of each until you find a &lit.jaxrs.ext.MessageBodyWriter; that |
| returns &lit.true;. |
| </para> |
| <para> |
| The first provider in the list - our <literal>MyBeanMessageBodyWriter</literal> returns &lit.true; as |
| it compares types and the types matches. If it would return &lit.false;, the next provider |
| <literal>B</literal> would by check by invoking its <literal>isWriteable</literal> method. |
| </para> |
| </step> |
| <step> |
| <para> |
| If step 5 locates a suitable &lit.jaxrs.ext.MessageBodyWriter; then use its writeTo method to map the |
| object to the entity body. |
| </para> |
| <para> |
| <literal>MyBeanMessageBodyWriter.writeTo</literal> will be executed and it will serialize the |
| entity. |
| </para> |
| <stepalternatives> |
| <step> |
| <para> |
| Otherwise, the server runtime MUST generate an |
| <literal>InternalServerErrorException</literal>, a subclass of |
| <literal>WebApplicationException</literal> with its status set to 500, and no entity and the client |
| runtime MUST generate a <literal>ProcessingException</literal>. |
| </para> |
| <para> |
| We have successfully found a provider, thus no exception is generated. |
| </para> |
| </step> |
| </stepalternatives> |
| </step> |
| </procedure> |
| <note> |
| <para> |
| JAX-RS 3.x/2.x is incompatible with JAX-RS 1.x in one step of the entity provider selection algorithm. |
| JAX-RS 1.x defines sorting keys priorities in the <xref linkend="mbw.writer.selection.algorithm.sortStep" /> |
| in exactly opposite order. So, in JAX-RS 1.x the keys are defined in the order: primary media type, |
| secondary type declaration distance where custom providers have always precedence to internal providers. |
| If you want to force Jersey to use the algorithm compatible with JAX-RS 1.x, setup the property |
| (to &jersey.server.ResourceConfig; or return from &jaxrs.core.Application; from its |
| <literal>getProperties</literal> method): |
| |
| <programlisting language="java" linenumbering="unnumbered"><![CDATA[jersey.config.workers.legacyOrdering=true]]></programlisting> |
| |
| Documentation of this property can be found in the javadoc of &jersey.message.MessageProperties;. |
| </para> |
| </note> |
| |
| <para> |
| The algorithm for selection of &lit.jaxrs.ext.MessageBodyReader; is similar, including the incompatibility |
| between JAX-RS 3.x/2.x and JAX-RS 1.x and the property to workaround it. The algorithm is defined as follows: |
| </para> |
| <procedure xml:id="mbw.reader.selection.algorithm"> |
| <title>&lit.jaxrs.ext.MessageBodyReader; Selection Algorithm</title> |
| <step> |
| <para> |
| Obtain the media type of the request. If the request does not contain a <literal>Content-Type</literal> |
| header then use <literal>application/octet-stream</literal> media type. |
| </para> |
| </step> |
| <step> |
| <para> |
| Identify the Java type of the parameter whose value will be mapped from the entity body. The |
| Java type on the server is the type of the entity parameter of the resource method. On the client |
| it is the <literal>Class</literal> passed to <literal>readFrom</literal> method. |
| </para> |
| </step> |
| <step> |
| <para> |
| Select the set of available &lit.jaxrs.ext.MessageBodyReader; providers that support the media type |
| of the request. |
| </para> |
| </step> |
| <step xml:id="mbw.reader.selection.algorithm.selectStep"> |
| <para> |
| Iterate through the selected &lit.jaxrs.ext.MessageBodyReader; classes and, utilizing their |
| <literal>isReadable</literal> method, choose the first &lit.jaxrs.ext.MessageBodyReader; provider that |
| supports the desired combination of Java type/media type/annotations parameters. |
| </para> |
| </step> |
| <step> |
| <para> |
| If <xref linkend="mbw.reader.selection.algorithm.selectStep" /> locates a suitable |
| &lit.jaxrs.ext.MessageBodyReader;, then use its <literal>readFrom</literal> method to map the entity |
| body to the desired Java type. |
| </para> |
| <stepalternatives> |
| <step> |
| <para> |
| Otherwise, the server runtime MUST generate a <literal>NotSupportedException</literal> |
| (HTTP 415 status code) and no entity and the client runtime MUST generate an instance |
| of <literal>ProcessingException</literal>. |
| </para> |
| </step> |
| </stepalternatives> |
| </step> |
| </procedure> |
| </section> |
| |
| <section> |
| <title>Jersey &lit.jersey.message.MessageBodyWorkers; API</title> |
| <para> |
| In case you need to directly work with JAX-RS entity providers, for example to serialize an entity in your resource |
| method, filter or in a composite entity provider, you would need to perform quite a lot of steps. |
| You would need to choose the appropriate &lit.jaxrs.ext.MessageBodyWriter; based on the type, media type and |
| other parameters. Then you would need to instantiate it, check it by <literal>isWriteable</literal> method and |
| basically perform all the steps that are normally performed by Jersey |
| (see <xref linkend="mbw.reader.selection.algorithm" />). |
| </para> |
| <para> |
| To remove this burden from developers, Jersey exposes a proprietary public API that simplifies the manipulation |
| of entity providers. The API is defined by &jersey.message.MessageBodyWorkers; interface and Jersey provides an |
| implementation that can be injected using the &jaxrs.core.Context; injection annotation. The interface declares |
| methods for selection of most appropriate &lit.jaxrs.ext.MessageBodyReader; and &lit.jaxrs.ext.MessageBodyWriter; |
| based on the rules defined in JAX-RS spec, methods for writing and reading entity that ensure proper and timely |
| invocation of interceptors and other useful methods. |
| </para> |
| <para> |
| See the following example of usage of &lit.jersey.message.MessageBodyWorkers;. |
| |
| <example> |
| <title>Usage of MessageBodyWorkers interface</title> |
| <programlisting language="java" linenumbering="numbered"><![CDATA[@Path("workers") |
| public static class WorkersResource { |
| |
| @Context |
| private MessageBodyWorkers workers; |
| |
| @GET |
| @Produces("application/xml") |
| public String getMyBeanAsString() { |
| |
| final MyBean myBean = new MyBean("Hello World!", 42); |
| |
| // buffer into which myBean will be serialized |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| |
| // get most appropriate MBW |
| final MessageBodyWriter<MyBean> messageBodyWriter = |
| workers.getMessageBodyWriter(MyBean.class, MyBean.class, |
| new Annotation[]{}, MediaType.APPLICATION_XML_TYPE); |
| |
| try { |
| // use the MBW to serialize myBean into baos |
| messageBodyWriter.writeTo(myBean, |
| MyBean.class, MyBean.class, new Annotation[] {}, |
| MediaType.APPLICATION_XML_TYPE, new MultivaluedHashMap<String, Object>(), |
| baos); |
| } catch (IOException e) { |
| throw new RuntimeException( |
| "Error while serializing MyBean.", e); |
| } |
| |
| final String stringXmlOutput = baos.toString(); |
| // stringXmlOutput now contains XML representation: |
| // "<?xml version="1.0" encoding="UTF-8" standalone="yes"?> |
| // <myBean><anyString>Hello World!</anyString> |
| // <anyNumber>42</anyNumber></myBean>" |
| |
| return stringXmlOutput; |
| } |
| }]]></programlisting> |
| </example> |
| </para> |
| <para> |
| In the example a resource injects &lit.jersey.message.MessageBodyWorkers; and uses it for selection |
| of the most appropriate &lit.jaxrs.ext.MessageBodyWriter;. Then the writer is utilized to serialize the entity |
| into the buffer as XML document. The <literal>String</literal> content of the buffer is then returned. |
| This will cause that Jersey will not use <literal>MyBeanMessageBodyWriter</literal> |
| to serialize the entity as it is already in the <literal>String</literal> type |
| (<literal>MyBeanMessageBodyWriter</literal> does not support <literal>String</literal>). Instead, a simple |
| <literal>String</literal>-based &lit.jaxrs.ext.MessageBodyWriter; will be chosen and it will only serialize the |
| <literal>String</literal> with XML to the output entity stream by writing out the bytes of the |
| <literal>String</literal>. |
| </para> |
| <para> |
| Of course, the code in the example does not bring any benefit as the entity could |
| have been serialized by <literal>MyBeanMessageBodyWriter</literal> by Jersey as in previous examples; |
| the purpose of the example was to show how to use &lit.jersey.message.MessageBodyWorkers; in a resource method. |
| </para> |
| </section> |
| |
| <section> |
| <title>Default Jersey Entity Providers</title> |
| |
| <para> |
| Jersey internally contains entity providers for these types with combination of media types (in brackets): |
| </para> |
| <simplelist> |
| <member> |
| <literal>byte[]</literal> (<literal>*/*</literal>) |
| </member> |
| <member> |
| &jdk6.String; (<literal>*/*</literal>) |
| </member> |
| <member> |
| &jdk6.InputStream; (<literal>*/*</literal>) |
| </member> |
| <member> |
| &jdk6.Reader; (<literal>*/*</literal>) |
| </member> |
| <member> |
| &jdk6.File; (<literal>*/*</literal>) |
| </member> |
| <member> |
| &jee9.DataSource; (<literal>*/*</literal>) |
| </member> |
| <member> |
| &jdk6.Source; (<literal>text/xml</literal>, <literal>application/xml</literal> and media types of the form |
| <literal>application/*+xml</literal>) |
| </member> |
| <member> |
| &jdk6.JAXBElement; (<literal>text/xml</literal>, <literal>application/xml</literal> and media types of the form |
| <literal>application/*+xml</literal>) |
| </member> |
| <member> |
| &jaxrs.core.MultivaluedMap; (<literal>application/x-www-form-urlencoded</literal>) |
| </member> |
| <member> |
| &jaxrs.core.Form; (<literal>application/x-www-form-urlencoded</literal>) |
| </member> |
| <member> |
| &jaxrs.core.StreamingOutput; ((<literal>*/*</literal>)) - this class can be used as an lightweight |
| &lit.jaxrs.ext.MessageBodyWriter; that can be returned from a resource method |
| </member> |
| <member> |
| &jdk6.Boolean;, &jdk6.Character; and &jdk6.Number; (<literal>text/plain</literal>) - corresponding |
| primitive types supported via boxing/unboxing conversion |
| </member> |
| </simplelist> |
| <para> |
| For other media type supported in jersey please see the <xref linkend="media" /> which describes |
| additional Jersey entity provider extensions for serialization to JSON, XML, serialization of collections, |
| &jersey.media.multipart; and others. |
| </para> |
| </section> |
| </chapter> |