Beruflich Dokumente
Kultur Dokumente
@Provider
class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext jContext;
public JAXBContextResolver() {
try {
jContext = JAXBContext.newInstance(Subscriber.class, Receipt.class);
} catch (JAXBException e) {
e.printStackTrace();
throw new WebApplicationException(e);
}
}
@Override
public JAXBContext getContext(Class<?> classType) {
if (classType.isAssignableFrom(Subscriber.class)
|| classType.isAssignableFrom(Receipt.class)) {
return jContext;
}
return null;
}
}
The ContextResolver will contains JAXBContext obj which inturn contains all Binding
classes as meta data. So the JAX-RS Runtime will creates ContextResolver which is
one per application so that all the meta data will be available at one sigle shot.
Writing MessageBodyReader with the help of ContextResolver:
@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 = contextResolver.getContext(classType);
unmarshaller = jContext.createUnmarshaller();
obj = unmarshaller.unmarshal(is);
} catch (JAXBException e) {
e.printStackTrace();
throw new WebApplicationException(e);
}
return obj;
}
}
@Override
public long getSize(Object object,
Class<?> classType,
Type rawType,
Annotation[] annotations,
MediaType mediaType) {
return 0;
}// 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 {
ContextResolver<JAXBContext> contextResolver=
providers.getContextResolver(JAXBContext.class,
MediaType.APPLICATION_XML_TYPE);
jContext = contextResolver.getContext(classType);
marshaller = jContext.createMarshaller();
marshaller.marshal(object, os);
} catch (JAXBException e) {
e.printStackTrace();
throw new WebApplicationException(e);
} finally {
os.close();
}
}// writeTo()
}
return receipt;
}
}
Since the writeTo() method of our MessageBodyWriter has access to the recharge()
method’s annotations, we can implement this easily. Let’s modify our JAXBMarshaller
class writeTo() as follows.
@Provider
@Produces(MediaType.APPLICATION_XML)
class JAXBMessageBodyWriter implements MessageBodyWriter<Object> {
@Context
private Providers providers;
@Override
public long getSize(...) {}
@Override
public boolean 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 {
ContextResolver<JAXBContext> contextResolver =
providers.getContextResolver(JAXBContext.class,
MediaType.APPLICATION_XML_TYPE);
jContext = contextResolver.getContext(classType);
marshaller = jContext.createMarshaller();
if (pretty) {
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
}
// (or) We can direclty write as follows
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(object, os);
} catch (JAXBException e) {
e.printStackTrace();
throw new WebApplicationException(e);
} finally {
os.close();
}
}// writeTo()
}
Here, we iterate over the annotations parameter to see if any of them are the @Pretty
annotation. If
@Pretty has been set, we set the JAXB_FORMATTED_OUTPUT property on the
Marshaller so that it will format the XML with line breaks and indentation strings.
public IdeaApplication() {
// register singletons
singletons.add(new JAXBMessageBodyReader());
singletons.add(new JAXBMessageBodyWriter());
singletons.add(new JAXBContextResolver());
// register non-singletons
classes.add(IdeaProvider.class);
}
@Override
public Set<Class<?>> getClasses() {
return classes;
}
@Override
public Set<Object> getSingletons() {
return singletons;
}
}
If we didn't register also by default, only one instance of each MessageBodyReader,
MessageBodyWriter, or ContextResolver is created per application by the Jax-RS
Runtime bcz these are contractual classes hence it will creates only one obj but if we
want to make any class that is not a contractual with JAX-RS then we need to make
it as singleton if we want by registering as singletons using Application class.
If JAX-RS is allocating instances of these components the classes of these components
must provide a public default constructor or public single param constructor for which
the JAX-RS runtime can provide all the parameter values. A public constructor may
only include parameters annotated with the @Context annotation.
Possible and recommended places we can inject Providers interface reference using
@Context annotation:
We can inject the Providers reference in class at 3-places in which we are going to
inject the Providers interface reference to get the inbuilt obj of JAX-RS.
1. Class level as Attribute:
If the class is singleton then we can inject Providers interface reference in the class
level as attribute and on top of the Providers attribute itself we can write @Context
so that JAX-RS will injects so that we can use in any method bcz class singleton.
2. Constructor level:
If the class is singleton then we can inject Providers interface reference in the
contractor as param and on that contractor param it self we will write @Context and
declare Providers attribute in the class level so that JAX-RS will injects so that we can
use in any method bcz class singleton.
3. At each and every method param level:
If the class is non-singleton then we should not declare the Providers reference in the
class level rather we need to inject in a method where ever we want to avoid the
multithreading concurrency issues.
@Provider
@Consumes(MediaType.APPLICATION_XML)
public class JAXBMessageBodyReader implements MessageBodyReader<Object> {
// This class is singletone hence we can inject Providers ref in the class level, if this
Class is not singleton then we should not inject in the class level rather we need
to inject in the method level or contractor level
@Context
private Providers providers;
We can write alternately as follows and we may can take any MediaType like
@Consumes(MediaType.APPLICATION_XML) as well.
@Provider
@Consumes(MediaType.APPLICATION_XML)
public class JAXBMessageBodyReader implements MessageBodyReader<Object> {
private Providers providers;
// This class is singletone hence we can inject Providers ref in the class level, if this
Class is not singleton then we should not inject in the class level rather we need
to inject in the method level or contractor level
public JAXBMessageBodyReader(@Context Providers providers) {
this.providers = providers;
}
....
}
Whether or not the JAX-RS runtime is allocating the component instance, JAX-RS will
perform injection into properly annotated fields and setter methods. Again, you can
only inject JAX-RS objects that are found using the @Context annotation.
Access the application:
JERSEY:
http://localhost:8083/3.2JAXBCustomContentHandlerWithContextResolver
/resource/idea/recharge
Select method as POST
Content-Type=application/xml
Send the req with data as part of body
<subscriber><mobile>929922</mobile><plan>Vennala</plan><amount>100.9</
amount></subscriber>
Response:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<receipt>
<receiptNo>7cb3266c-32d7-4f56-91c2-f27830c71787</receiptNo>
<mobile>929922</mobile>
<balance>100.9</balance>
<status>success</status>
</receipt>
Send multiple req's and observe the output on the console:
For Req-1
JAXBContext obj hashCode in ContextResolver: 1113914983
isReadable(..)
readFrom(..)
JAXBContext obj hashCode in Reader: 1113914983
isWritable(..)
isWritable(..)
writeTo(..)
JAXBContext obj hashCode in Writer: 1113914983
For Req-2
isReadable(..)
readFrom(..)
JAXBContext obj hashCode in Reader: 1113914983
isWritable(..)
isWritable(..)
writeTo(..)
JAXBContext obj hashCode in Writer: 1113914983
POJOMapping Feature using Jackson:
JERSEY:
If we don't want to write Custom Content handlers and ContextResolvers in Jersy then
add Jackson jar which is 3rd party library and register these Reader and writer classes
with JAX-RS Runtime in JERSEY it will work.
Jackson is used to convert xml to obj and vice versa.
RESTEasy:
We no need to write any Custom Content Handlers or COntext Resolvers in case of
RESTEasy bcz it has built in Custom Content Handlers and ContextResolvers so we
don't need to write any Readers or Writers and if we wanted to write also there is no
problem.
Access the application:
http://localhost:8082/4PredefinedCustomContentHandlerRESTEasy
/resource/idea/recharge
Check with multiple combinations:
We annotated the Resource as follows
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
http://localhost:8082/4PredefinedCustomContentHandlerRESTEasy
/resource/idea/recharge
Content-Type=application/xml
<subscriber><mobile>929922</mobile><plan>Vennala</plan><amount>100.9</
amount></subscriber>
Response:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<receipt>
<receiptNo>b364b383-0f6d-4c24-913b-8689e5bbb0b0</receiptNo>
<mobile>929922</mobile>
<balance>100.9</balance>
<status>success</status>
</receipt
http://localhost:8082/4PredefinedCustomContentHandlerRESTEasy
/resource/idea/recharge
Content-Type=application/json
Note:
Syntax of JSON:
We taken mobile as int, plan as String, amount as float,
Element/Attribute names put in "mobile"
Data if it string then put in "Vennala"
If float data then send as 100.9 but not like 100.9f
If int data then send as 929922
Send the req as follows
{"mobile":929922,"plan":"Vennala","amount":100.9}
Response:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<receipt>
<receiptNo>c624724f-bd10-49fe-82ca-4ac6eff2acc3</receiptNo>
<mobile>929922</mobile>
<balance>100.9</balance>
<status>success</status>
</receipt
Bydefault it will sends response as who is in 1st place that as response type for
example if we write @Produces({ MediaType.APPLICATION_XML,
MediaType.APPLICATION_JSON }) then it will sends response type as XML to the client
bcz XML type is at the 1st place.
If we write @Produces({MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML}) then response type as JSON will be sent to the client
bcz JSON type is at the 1st place.
But we can send any type as input either XML or JSON which is called as
Representation Orientation hence RESTful can take any data formats and hence more
interoperable bcz there is no restrictions of protocol and versions of data formats.
Wrapping Up:
In this chapter learned that JAX-RS can automatically convert Java objects to a specific
data type format and write it out as an HTTP response. It can also automatically read
in HTTP request bodies and create specific Java objects that represent the request.
JAX-RS has a number of built-in handlers, but we can also write your own custom
marshallers and unmarshallers.
How did you written the Binding classes as part of your RESTful services to work with
Custom Content handlers?
Representing the XML we need to write the XSD from that XSD we need to generate
the Binding classes. In order to generate the Binding classes we need to run xjc tool
that is there as part of the java by passing XSD as input.
How many places JAX-RS injection is possible?
JAX-RS injection is possible in class attribute level, Resource method level and
contractor level.