Professional Documents
Culture Documents
2 CustomContentHandlers
2 CustomContentHandlers
@Path("/idea")
public class IdeaProvider {
@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/recharge")
public Receipt recharge(Subscriber subscriber) {
Receipt receipt = null;
return receipt;
}
}
To know how to write your own handlers, we’re going to pretend/assume that there
is no built-in JAX-RS JAXB support and instead write one our-selves Custom
Handlers using JAX-RS APIs which will converts into an obj format.
So we need write our Custom handler which read the XML data from the req body
and converts into an obj format for this JAX-RS has provided MessageBodyReder
and if we are returing an obj response then we need to write Custom handler which
will converts obj to XML format for this JAX-RS has provided MessageBodyWriter,
But JAX-RS Runtime will not knows what are Reader and Writers that's where we
need to register CustomMessageBodyReaders and MessageBodyWriters with the
JAX-RS runtime with help of @Provider annotation so that it will takes care of
identifying the Readers and Writers by introspecting which are annotated with
@Provider at the start-up of the application and whenever the req comes it will
takes care of converting obj to xml and xml to obj and if we use any other formats
like JSON then it will takes care of converting JSON into java obj and java obj to
JSON by the help of Readers and Writers that we have register with JAX-RS
Runtime.
That means for every data format we need to write one Reader and one Writer, that
means if we wanted convert xml to obj then we need to write JAXB Custom Reader
and JAXB Custom Writer, Similarly if we wanted to convert JSON to java obj then we
need to JSON Custom Reader and JSON Custom Writer
In order to convert xml to obj we need to write Binding classes or generate the
Binding classes using "xjc tool" by passing XSD as input and the we need to write
logic to using JAXB with help of these Binding classes for unmarshall/marshall.
Writing Custom Handlers:
MessageBodyReader (Custom Unmarshaller):
Let’s write an unmarshaller (converts XML to obj) that knows how to convert HTTP
XML request bodies into a Java object. To do this, we need to use the
javax.ws.rs.ext.MessageBodyReader interface.
T readFrom(Class<T> type,
Type genericType,
Annotation annotations[],
MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream entityStream)
throws IOException, WebApplicationException;
}
As we are not specifying in which raw format we reading the req data in method
param of the Resource class method, we need to specify this raw type info in the
Custom MessageBodyReader so that JAX-RS will takes this raw format and with the
help of Custom MessageBodyReader and Binding classes it will converts xml into obj
of data.
As we are registering the Readers, Writers with the help of @Provider annotation so
the JAX-RS Runtime will reads the Readers and Writers creates obj for Readers and
Writes for only once and uses it for every req.
@Provider
@Consumes(MediaType.APPLICATION_XML)
public class JAXBMessageBodyReader implements MessageBodyReader<Object> {
@Context
private Providers providers;
@Override
public boolean isReadable(Class<?> type,
Type genericType,
Annotation annotations[],
MediaType mediaType) {
if (classType.isAnnotationPresent(XmlRootElement.class)) {
return true;
}
return false;
}
@Override
public Object readFrom(Class<Object>,
Type genericType,
Annotation annotations[],
MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream entityStream)
throws IOException, WebApplicationException {
try {
contextResolver = providers.getContextResolver(JAXBContext.class,
MediaType.APPLICATION_XML_TYPE);
jContext = JAXBContext.newInstance(classType);
unmarshaller = jContext.createUnmarshaller();
obj = unmarshaller.unmarshal(is);
} catch (JAXBException e) {
e.printStackTrace();
throw new WebApplicationException(e);
}
return obj;
}
}
Our JAXBUnmarshaller or Custom Handler or JAXBCustom Handler or Custom
JAXBMessageBodyReader class is annotated with @Provider and @Consumes.
The @Consumes annotation tells the JAX-RS runtime which media types it can
handle. The matching rules for finding a MessageBodyReader are the same as the
rules for matching MessageBodyWriter.
The readFrom() method gives us access to the HTTP headers of the incoming
request as well as a java.io.InputStream that represents the request message body.
Here, we just create a JAXBContext based on the Java type we want to create and
use a javax.xml.bind.Unmarshaller to extract it from the stream.
@Override
public long getSize(Object object,
Class<?> classType,
Type rawType,
Annotation[] annotations,
MediaType mediaType) {
return -1;
}// getSize()
@Override
public boolean isWriteable(Class<?> classType,
Type rawType,
Annotation[] annotations,
MediaType mediaType) {
if (classType.isAnnotationPresent(XmlRootElement.class)) {
return true;
}
return false;
}// isWriteable()
@Override
public void writeTo(Object object,
Class<?> classType,
Type rawType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String,
Object> responseHeaders,
OutputStream os)
throws IOException, WebApplicationException {
try {
jContext = JAXBContext.newInstance(classType);
marshaller = jContext.createMarshaller();
marshaller.marshal(object, os);
} catch (JAXBException e) {
e.printStackTrace();
throw new WebApplicationException(e);
} finally {
os.close();
}
}// writeTo()
}
In the Writer class we annoated with the @javax.ws.rs.ext.Provider annotation, this
tells JAX-RS that this is a deployable JAX-RS component. We must also annotate it
with @Produces to tell JAX-RS which media types this MessageBodyWriter supports.
Here, we’re saying that our JAXBMarshaller class supports application/xml.
Purpose of isWriteable() method and its params:
The isWriteable() method is a callback method that tells the JAX-RS runtime whether
or not the class can handle writing out this type. JAX-RS follows this algorithm to
find an appropriate MessageBodyWriter to write out a Java object into the HTTP
response.
1. First, JAX-RS calculates a list of MessageBodyWriters by looking at each writer’s
@Produces annotation to see if it supports the media type that the JAX-RS resource
method wants to output.
2. This list is sorted, with the best match for the desired media type coming first. In
other words, if our JAX-RS resource method wants to output application/xml and we
have three MessageBodyWriters (one produces application/*, one supports anything
*/*, and the last supports application/xml), the one producing application/xml will
come first.
3. Once this list is calculated, the JAX-RS implementation iterates through the list in
order, calling the MessageBodyWriter.isWriteable() method. If the invocation on
isWritable() returns true, that MessageBodyWriter is used to output the data.
The isWriteable() method takes four parameters.
Purpose isWriteable() method params:
The first one is a java.lang.Class that is the type of the object that is being
marshalled. We determine the type by calling the getClass() method of the object.
In our example, we use this parameter to find out if our object’s class is annotated
with the @XmlRootElement annotation.
The second parameter is a java.lang.reflect.Type. This is generic type information
about the object being marshalled. We determine it by introspecting the return type
of the JAX-RS resource method. We don’t use this parameter in our
JAXBMarshaller.isWriteable() implementation. This parameter would be useful, for
example, if we wanted to know the type parameter of a java.util.List generic type.
The third parameter is an array of java.lang.annotation.Annotation objects. These
annotations are applied to the JAX-RS resource method we are marshalling the
response for. Some MessageBodyWriters may be triggered by JAX-RS resource
method annotations rather than class annotations. In our JAXBMarshaller class, we
do not use this parameter in our isWriteable() implementation.
The fourth parameter is the media type that our JAX-RS resource method wants to
produce. The MediaType obj purpose given below.
Send Req-2
isReadable(..)
readFrom(..)
jCOntext hashCode: 390184556
isWritable(..)
isWritable(..)
writeTo(..)
jContext hashCode: 791996013
If we observe here JAXBContext obj has been created for every Reader and Writer of
each and every req which will kilss the JVM memory which leads to performance
issues.
So inorder to avoid this we need to go for Custom ContextResolvers that has been
provided by the JAX-RS API so that it will creates only one obj irrespective of
whether it is a Reader or Writer and irrespective of request so that JVM memory will
not be wasted so that performance issues will not raise.