Follow my blog with Bloglovin

Monday, July 23, 2012

Spring managed components interoperability with JPA/JAXB or others


Spring managed components interoperability with JPA/JAXB or others

As Spring is a de-facto standard in creating JEE application, one problem faced commonly is interoperability between Spring  managed beans and objects created by other  runtimes like JPA or JAXB. As most of the application is using JPA or JAXB we will look into ways to use Spring beans in other objects and injecting other objects in Spring beans. In This article I will refer objects created by other runtime like JAXB or JPA as external object and the classes as external class.

Configuring bean programmatically in Java class:

For configuring beans programmatically Spring provided their <beans> and <bean> equivalent in Java.

packageinterop.config.test;

importorg.springframework.beans.factory.annotation.Value;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;

@Configuration
publicclassConfigurationClass {

      class SampleBean{
           
      }
     
      @Bean(name="sample")
      SampleBean getBean(@Value("#{systemProperties['user.dir']}")String param, ServiceClass service){
            return new SampleBean();
      }
}

1. Annotate the class with @Configuration
2. Annotate method for creating beans with @Bean

Spring will execute this method and register the returned object as bean with name as method name.

Parameters passed into these creator methods are of type any Spring bean or annotated as @Value. If this method requires other objects, then call other bean methods and those returned beans will be available to it.

@Configuration is equivalent to XML configuration and @Bean is equivalent to <bean/> and has all configuration options like Scope, Name etc are available.

Other useful annotations:
@ImportResource – Allows us to import a spring xml file or other @Configuration class for importing beans and resource like XML configuration.
@Value – Use SpEL expressions to inject properties or other bean attributes.

This class must be under component scan.

For more details:

Injecting Spring components (beans) into external classes (instantiated using new operator):

This is useful in using Spring components/services in external classes instantiated using new operator. Spring intercepts the autowired beans in the Configurable class and new calls for the class. There are several strategies for weaving those classes like weaving at compile or load time. Look resource #1.

This can be done using @Configurable class.

This needs following jars:
cglib-2.2.2.jar
asm-all-3.3.1.jar
aspectjrt-1.6.9.jar
aspectjweaver-1.6.9.jar
org.springframework.aspects-<VERSION>.jar
org.springframework.instrument-<VERSION>.jar

Add following in applicatioContext.xml
<context:spring-configured />
<context:load-time-weaver />

Run application with VM argument:
            -javaagent:lib/org.springframework.instrument-<VERSION>.jar

Sample Code:

@Configurable class:

packageinterop.config.test;

importinterop.config.test.ConfigurationClass.SampleBean;

importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.beans.factory.annotation.Configurable;

@Configurable
publicclassConfigurableClass {

      @Autowired
      SampleBean sampleBean;
     
      public SampleBean getSampleBean() {
            return sampleBean;
      }
}

applicationContext.xml
                                   
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:context="http://www.springframework.org/schema/context"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:component-scan base-package="interop.config.test" />

          <context:spring-configured />
          <context:load-time-weaver />
</beans>

@Configuration class to create the autowired bean is the class described in previous topic.

Issues with @Configurable

Resources:

Injecting external objects in Spring context to use them in Spring components:

Adding objects to application context [singletons]:
Singletons can be added the way described in resources listed #1 and dynamic bean definitions can be added as listed in Resources #2.

Sample code for registering singleton in ApplicationContext:           
public class ConfigTest {

          public static void main(String[] args) {
                   ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
                   //registering singleton
                   context.stop();
                   ConfigurableListableBeanFactory factory = context.getBeanFactory();
                   factory.registerSingleton("auto", new Auto("fgfdg", "fgfd"));
                   context.start();
                   System.out.println(Arrays.asList(context.getBean("auto")));
          }
         
          static class Auto{
                   String a;
                   String b;
                   public Auto(String a, String b) {
                             this.a=a;
                             this.b=b;
                   }
          }
}

Now how to register beans in ptototype scope dynamically?

Use Case:
  • Application is receiving data from external API (REST or Web Services) in XML format.
  • JAXB populates object graph from XML.
  • Application has @Service (s) which needs this object graph populated by JAXB. For example, populating data transfer object (DTO) by using SpEL (@Value annotated fields) or the @Service consuming this object graph by any other purpose.

How to make this object graph available to those services (Spring components)?

First of all such Service need to of scope prototype as it depends on data retrieved from external sources (will be different every time, so objects state depends on this data. This object is stateful). A Service can be singleton which is using that data only in a method and by this object graph as argument, but in scenario like populating DTO using SpEL where fields of class is annotated with @Value(<expression>). We will look into prototype scoped bean.
Scope is not necessarily be prototype, can be changed according to requirements.

We need following things:
  1. A Spring component bean in which we will store our object. This component will be singleton and will be in application context already. This will be annotated with @Component and will have fields for holding data.

packageinterop.config.test;

importjava.util.Map;
importjava.util.TreeMap;

importorg.springframework.stereotype.Component;

@Component
publicclassExternalObjectContainer {

publicMap<String, Object> container= newTreeMap<String, Object>();
     
            publicvoidaddObject(String key, Object value){
                  container.put(key, value);
            }
     
            publicObject getObject(String key){
                  return container.get(key);
            }
}

In this class currently there is only one field of type Map for holding data. This field can be of Object type or some specific JAXB root class type. Also, for concurrent access or storing more objects, data structure and storage methods can be changed as per requirement.

External class:

packageinterop.external;

publicclass Root {

            publicCustomer customer;
            publicOrder order;
            publicObject object;
     
            publicclass Customer{
                  publicString name;
                  publicCustomer(String name) {
                        this.name=name;
                  }
            }
     
            publicclass Order{
                  publicint quantity;
                  publicOrder(intquantity) {
                        this.quantity=quantity;
                  }
            }
}

  1. The requester of the external API will set the object graph, populated from response received to the singleton component, created in previous step. This needs reference to the component. This reference can be acquired by autowiring the bean to this requester. If this requester is not a spring component then make this @Configurable.

packageinterop.config.test;

importinterop.external.Root;

importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.context.ApplicationContext;
importorg.springframework.stereotype.Service;

@Service
publicclassServiceClass {

      @Autowired
      ExternalObjectContainer container;
     
      @Autowired
      ApplicationContext context;
     
      public void service(){
            /*
             * In real this object will be created by data received from external sources, e.g.
             * ReST API response XML converted to object model by JAXB.
             */
            Root root = newRoot();
            root.customer=root.new Customer("customeR");
            root.order=root.new Order(9);
            //the container we created has Map to store objects, so put any random key.
            //this depends on storage model.
            container.addObject("randomKey", root);
            /*
             * request the consumer of this data, prototype scoped bean from application context.
             * This will create new object with all mapped fields populated from external object.
             * Cannot autowire because this will instantiate on creation of this service class and will
             * not have data or may even fail to inject required dependency.
             */
            ExternalObjectConsumerComponent component = (ExternalObjectConsumerComponent) context.getBean("externalObjectConsumerComponent");
            //check data populated in the bean
            System.out.println(component);
            //Doing the same thing with non Spring component, a configurable class.
            ExternalObjectConsumerConfigurable configurable = newExternalObjectConsumerConfigurable();
            System.out.println(configurable);
           
      }
}

  1. The @Service class which will populate the DTO from this object graph. This will be a prototype scoped bean as this will have fields annotated with @Value(<SpEL expression>). This is the actual DTO. This bean will be requested only after step #2 is done. Obviously, DTO can be populated only if one have the required data.
One can make this singleton by moving these annotations to other class.

Using this injected external object in Spring component:

packageinterop.config.test;

importorg.springframework.beans.factory.annotation.Value;
importorg.springframework.context.annotation.Scope;
importorg.springframework.stereotype.Service;

@Service
@Scope("prototype")
publicclassExternalObjectConsumerComponent {

      @Value("#{externalObjectContainer.container.get('randomKey').customer.name}")
      String name;
      @Value("#{externalObjectContainer.container.get('randomKey').order.quantity}")
      int qty;
     
      @Override
      public String toString() {
            return super.toString()+"{Name : "+name+", Quantity : "+qty+"}";
      }
}

Using this injected external object in external object:

packageinterop.config.test;

importjavax.annotation.PostConstruct;

importinterop.external.Root;

importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.beans.factory.annotation.Configurable;

/**
 *This example is for using external object injected in Spring component in a non Spring/external object.
 *
 *This will be useful in using external object in external object but in flow there is Spring services in between.
 */
@Configurable
publicclassExternalObjectConsumerConfigurable {

      @Autowired
      ExternalObjectContainer container;
      String name;
      int qty;

      /*
       * Rather than using SpEL the data can be accessed directly from the bean.
       * Put this initialization logic in in method annotated with PostConstruct to populate data
       * just after initializing object. This is just an alternative for SpEL and nothing to do with
       * it is Spring component or not.
       */
      @PostConstruct
      public void mapProperties(){
            Root root = (Root) container.getObject("randomKey");
            name=root.customer.name;
            qty=root.order.quantity;
      }
     
      @Override
      public String toString() {
            return super.toString()+"{Name : "+name+", Quantity : "+qty+"}";
      }
}

Resources:

Testing all this:

importinterop.config.test.ConfigurableClass;
importinterop.config.test.ServiceClass;

importorg.springframework.context.ApplicationContext;
importorg.springframework.context.support.ClassPathXmlApplicationContext;


publicclass Main {

      public static void main(String[] args) {
            ApplicationContext context = newClassPathXmlApplicationContext("/applicationContext.xml");
            System.out.println("\n\n\n");
            //check sample bean is configured through @Configuration class
            System.out.println(context.getBean("sample"));
            System.out.println("\n\n\n");
            //check Spring components injected in external objects through @Configurable class
            ConfigurableClass configurable = newConfigurableClass();
            System.out.println("\n\n\n");
            System.out.println(configurable.getSampleBean());
            System.out.println("\n\n\n");
            //Test injecting external objects into Spring components
            ServiceClass service = (ServiceClass)context.getBean("serviceClass");
            service.service();
            System.out.println("\n\n\n");
      }
     
}

Running this with java agent:

In Eclipse configure Run Configuration like this:


Project overview:


Injecting prototype scoped bean into Singleton bean such that every time singleton bean is requested it will have new instance of prototype scoped bean.
It can be achieved using proxy scoped bean. In @Scope annotation parameter set proxyMode parameter. For details look.

Alternatively, method injection can be used.

Get project codeand add Spring framework libraries as in eclipse projects classpath.

Thursday, May 3, 2012

Easy REST with JAX-RS and RESTEasy

REST with JAX-RS
What is REST?

REST stands for Representational State Transfer. It’s an architectural style for building lightweight network software (client/server) over a stateless, cacheable, client-server communication protocol. As HTTP is most widely used protocol on the web it is most widely used (almost all implementation use this only). HTTP describes methods like GET, PUT, POST etc. and work on URI. This makes it suitable for defining, locating resource and defining operations on them. HTTP is simple and well established protocol for communicating on web.

Software architecture is defined by a configuration of architectural elements-components, connectors, and data--constrained in their relationships in order to achieve a desired set of architectural properties. [Roy Fielding]

Resources are located by URI and represented in any format [XML, JSON, CSV or any MIME type]. Operation and communication are defined by HTTP methods like GET, POST, PUT, DELETE, HEAD etc.

There is no W3C recommendation for REST like there is for Web Services (WSDL). A contract for published service should be defined and documented for the client to discover what resources and operations are available. There is a submission made by Sun Microsystems called WADL for defining resources in web applications.


Java standardizes this in JSR 311 called Java API for XML – RESTful Services.

Why REST?
  • It’s a light weight and simple web service then complex RPC mechanism like CORBA, XML-RPC and Web Services.
  • Like web services it is platform independent, language independent and standard based as it runs on top of HTTP.
  • Even an AJAX (Java Script) client can call a REST service. No need of stubs or complex request format (SOAP request).
  • Can handle complex request/response and any MIME type.  Can use any format for representation of resource in request/response. XML provide type safety and a predefine contract.
  • REST based systems are scalable and simple.
  • Authentication/Authorization and security handled as in HTTP. Something like OAuth or simpler basic or form based authentication or other mechanism can be used.
How to implement in Java?

There are many implementations available for JAX-RS. Jersey is the production quality reference implementation, other implementation like Restlet, RESTEasy are available which provide complete implementation of JAX-RS and also provide rich support for client side and other functionality like more lifecycle support for Resource class like Singleton or PerSession. JAX-RS only specifies per request scope lifecycle.

JAX-RS JSR-311

This is the JEE specification for creating RESTful services in Java. A brief summary of this specification for is presented in this topic.
It consists of Resources and/or Providers and resource methods to operate on resource. 

Resource: A web resource class represents a resource published on an URI. This path is specified with @Path annotation at class level or method level. The path is a URL which is used to access the resource.    JAX-RS provides a way to parameterize the URL.
A resource class must have a public constructor. Parameters of the constructor must be annotated with @Context, @HeaderParam, @CookieParam, @MatrixParam, @QueryParam or @PathParam annotation, as these are the only data available for JAX-RS runtime.  If more than one constructor is provided then the constructor with matching most of parameters will be used. As while instantiating resource classes the JAX-RS runtime will have only information from environment, HTTP headers, Cookies, request URL, so they can pass parameters only from these sources. For more information on these annotations http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-220003.2

Fields (class level variables) can also be annotated and will be injected by JAX-RS runtime after construction of object of resource class.

This class must have one or more resource methods annotated with HttpMethod like GET PUT, POST to operate on resources.

This class can define other annotations like @Produces, @Consumes or @ApplicationPath. If defined at class level will be common to all methods. Methods can override them individually. Produces and Consumes describes what media types resource can accept and provides. A resource can provide/accept more than one media type. By means of content negotiation clients specify request and response format they want. JAX-RS runtimes provides content negotiation by means HTTP headers (Content-Type and Accept) and URL suffix.

Lifecycle of a Resource
A resource class is instantiated for each request and after serving the request it is dereference for garbage collection. Implementers of the specifications can provide other strategy for lifecycle management. Jersey  and RESTEasy provides Singleton and PerSession strategy. A IoC (like Spring) based lifecycle management implementation like Apache Wink can support all lifecycle strategy supported by container.
Resource methods are public methods in resource classes with designated HttpMethod. They can override @Path annotation at class level.

Facts about resource methods:
  • They must be public.
  • All the parameters except one must be annotated with @Context, @HeaderParam, @CookieParam, @MatrixParam, @QueryParam or @PathParam. Only one parameter which is not annotated will be mapped to message body.
  • Parameters can be annotated with @DefaultValue to supply default value and @Encoded to get value as it was requested by client without decoding.
  • The type of parameters must have a no argument constructor or a single argument of type String or a static method named valueOf or fromString accepting a String parameter.
Providers are means of extending JAX-RS. Entity providers provide capability to handle different media types or handling media types in custom ways.  They map representation of resources (may be MIME types) and Java types to and fro. There are two types of providers: MessageBodyReader (for representation to Java type) and MessageBodyWriter (for Java types to representation). They can declare what media types they can handle.

Other providers are Context providers and Exception mapping provider.

Lifecycle of a Provider
Providers are singleton in web application. JAX-RS runtime create single instance of provider class.

JAX-RS Annotations

Configuration and deployment of application
Configuration needs a subclass of Application which provides a Set of classes which are Resource.
This can use runtime class scan or static Set of classes. This configuration subclass can be supplied depends on type of deployment.
In standalone application deployment RuntimeDelegate.createEndpoint method accepts the Application subclass and type of resource and returns Resource(s).

Servlet deployment will be described in example.

Example implementation with RESTEasy

In this example we will describe how to create a resource class. This class will allow creation and retrieval of resource. Update and delete are similar to create (just change the annotation). We will also create provider to map Java type to resource representation and vice versa using XStream.

In this tutorial we will do following:
  • Publish a REST service.
  • Make the resource support XML and JSON and use content negotiation using HTTP headers to get and post representation of required and supported type.
  • Create a custom provider to support mapping between Java types and supported representations.
  • Test using Mozilla REST client.
  • Write clients for published service.
Softwares used:

Eclipse Galileo for JEE
JDK 1.6+
RESTEasy 2.3
XStream 1.4.2

Step  #1 Create a dynamic web project in eclipse named REST, or any other name. If any other name is used then make modification throughout the tutorial. Rename WebContent directory to JAXRS [not required just for convenience].

Step #2 Extract RESTEasy and XStream and copy jars from these distributions (lib directory) to project and add to projects build path. To do so follow these steps:



Copy jars specifies in screenshot into JAXRS [renamed from WebContent]/lib directory.
After that right click on project and go to properties. In Build Bath select on Add Jars and browse through the project folder and select the jars.

Step #3 Create a class named ERecource and copy following content into it.
EResource.java

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;

/**
 * This is the resource class for demonstration.
 * It will be published at path /resource as specified in @Path annotation. This can be overridden
 * for sub resource, the path specified in sub resource will be computed relative to the class level @Path
 * All the methods will accept and produce data in XML and JSON format as specifies in @Produces and
 * @Consumes annotation. These annotations can be overridden in resource methods if required.
 *
 *
 */
@Path("resource")
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class EResource {

                private static int count = 0;
                                
                @POST
                public int createOrder(Order order){
                                return count++;
                }
                

                @GET
                public Order getOrder(){
                                Order order = new Order();
                                order.setId(count++);
                                order.setName("name");
                                return order;
                }
               
                @GET
                @Path("/{id}")
                public Order getOrder(@PathParam("id") int id){
                                Order order = new Order();
                                order.setId(id);
                                order.setName("name");
                                return order;
                }
               
                /**
                 * This method describes how to access headers, cookies and context information.
                 * @param headers : access all headers.
                 * @param info : access all URI components
                 * @param id : access a path param by name and assign it to a variable.
                 * @param accept : access a http header by name.
                 * @return
                 */
                @GET
                @Produces(MediaType.TEXT_PLAIN)
                public String extractNReturnRequestData(@Context HttpHeaders headers, @Context UriInfo info, @DefaultValue("2")@PathParam("id") String id, @HeaderParam(HttpHeaders.ACCEPT) String accept){
                             
           }
}

Explanation:

    @POST
    public int createOrder(Order order) will be called on POST request from client to URL http://<host>:<port>/REST/resource. This method expects an instance of Order. This instance is provided by JAX-RS runtime. This requires JSON or XML representation depends on Content-Type header specified by client.

  @GET
   public Order getOrder() will be called on GET request from client to URL http://<host>:<port>/REST/resource without any parameter.

  @GET
  @Path("/{id}")
   public Order getOrder(@PathParam("id") int id) will be called on GET request from client to URL http://<host>:<port>/REST/resource/<id> with a parameter of type integer. This describes how to specify path parameters by URL template and accessing them in method.
         
@GET
@Produces(MediaType.TEXT_PLAIN)
 public String extractNReturnRequestData(@Context HttpHeaders headers, @Context UriInfo info, @DefaultValue("2")@PathParam("id") String id, @HeaderParam(HttpHeaders.ACCEPT) String accept) will be called on GET request from client to URL http://<host>:<port>/REST/resource with accept header as "text/pain".

The resource methods can expect (as parameter)/and provide (as response) any media type. see MediaType class for detail. Just mention the media type in @Consumes/@Produces annotations. Also Response and ResponseBuilder classes can be used to return responses with additional headers or more detail. To return Image or PDF InputStream can be used.

Content negotiation:
This class produces and consumes XML and JSON representation. For specifying what content type is sent and expected by client Accept and Content-Type headers are used. Resource publishes this with annotations. RESTEasy also provides URL based content negotiation. In this way content type is determined by ending suffix of URL. To do so following changes are required in web.xml:
<context-param>
        <param-name>resteasy.media.type.mappings</param-name>
        <param-value>html : text/html, json : application/json, xml : application/xml</param-value>
</context-param>
This is not covered in this tutorial. For more details look at http://docs.jboss.org/resteasy/2.0.0.GA/userguide/html/JAX-RS_Content_Negotiation.html


Exception handling:
As HTTP defines error and success codes for response, any exception in resource methods mapped to WebApplicationException and sent to client with appropriate code. Any unexpected exception will be mapped to HTTP error code 500 (internal server error).

Step #4 Create a class Order (a DAO to transfer content) and paste the content into it.
Order.java

public class Order {

      int id;
      String name;
      public int getId() {
            return id;
      }
      public void setId(int id) {
            this.id = id;
      }
      public String getName() {
            return name;
      }
      public void setName(String name) {
            this.name = name;
      }
     
}

We will exchange XML/JSON representation of this class in our REST service the EResource class.

Step #5 Create a class XStreamProvider to implement provider and paste following content into it.
XStreamProvider.java

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Consumes;
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.core.Response;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * This class is an provider which provide mapping of Java types and representations (XML and JSON).
 * This provider accepts and produces XML and JSON using XStream.
 */
@Provider
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class XStreamProvider implements MessageBodyReader<Object>, MessageBodyWriter<Object> {

                @Override
                public boolean isReadable(Class<?> arg0, Type arg1, Annotation[] arg2,
                                                MediaType arg3) {
//isCompatible should be used instead of equals if parameters like encoding or locale does not matter.
                                if(arg3.isCompatible(MediaType.APPLICATION_JSON_TYPE) || arg3.isCompatible(MediaType.APPLICATION_XML_TYPE))
                                                return true;
                                else
                                                return false;
                }

                @Override
                public Object readFrom(Class<Object> arg0, Type arg1, Annotation[] arg2,
                                                MediaType arg3, MultivaluedMap<String, String> arg4,
                                                InputStream arg5) throws IOException, WebApplicationException {
                                if(arg3.equals(MediaType.APPLICATION_JSON_TYPE)){
                                                XStream stream = new XStream(new JsonHierarchicalStreamDriver());
                                                return stream.fromXML(arg5);
                                }else if(arg3.isCompatible(MediaType.APPLICATION_XML_TYPE)){
                                                XStream stream = new XStream(new DomDriver());
                                                return stream.fromXML(arg5); 
                                }else {
                                                throw new WebApplicationException(Response.Status.UNSUPPORTED_MEDIA_TYPE);
                                }
                }

                @Override
                public long getSize(Object arg0, Class<?> arg1, Type arg2,
                                                Annotation[] arg3, MediaType arg4) {
                                XStream stream = null;
                                if(arg4.equals(MediaType.APPLICATION_JSON_TYPE)){
                                                stream = new XStream(new JsonHierarchicalStreamDriver());
                                }else{
                                                stream = new XStream(new DomDriver());
                                }
                                long l = stream.toXML(arg0).length();
                                System.out.println(l);
                                return l;
                }

                @Override
                public boolean isWriteable(Class<?> arg0, Type arg1, Annotation[] arg2,
                                                MediaType arg3) {
                                if(arg3.equals(MediaType.APPLICATION_JSON_TYPE) || arg3.equals(MediaType.APPLICATION_XML_TYPE))
                                                return true;
                                else
                                                return false;
                }

                @Override
                public void writeTo(Object arg0, Class<?> arg1, Type arg2,
                                                Annotation[] arg3, MediaType arg4,
                                                MultivaluedMap<String, Object> arg5, OutputStream arg6)
                                                throws IOException, WebApplicationException {
                                if(arg4.equals(MediaType.APPLICATION_JSON_TYPE)){
                                                XStream stream = new XStream(new JsonHierarchicalStreamDriver());
                                                arg6.write(stream.toXML(arg0).getBytes());
                                                arg6.flush();
                                }else if(arg4.equals(MediaType.APPLICATION_XML_TYPE)){
                                                XStream stream = new XStream(new DomDriver());
                                                arg6.write(stream.toXML(arg0).getBytes());
                                                arg6.flush();
                                }else {
                                                System.out.println(arg0);
                                                throw new WebApplicationException(Response.Status.UNSUPPORTED_MEDIA_TYPE);
                                }
                }
}

This provider provides mapping between any Java type and XML/JSON representation. By default RESTEasy or any other JAX-RS runtime provide this mapping. It is not required to write provider. This is just to describe how to write Providers. Its about specifying what media types it can support and implement two interfaces.

Step #6 Make following change in web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
      id="WebApp_ID" version="2.5">
      <display-name>REST</display-name>
      <listener>
            <listener-class>
                  org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
      </listener>

      <servlet>
            <servlet-name>Resteasy</servlet-name>
            <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
      </servlet>

      <servlet-mapping>
            <servlet-name>Resteasy</servlet-name>
            <url-pattern>/*</url-pattern>
      </servlet-mapping>

      <context-param>
            <param-name>resteasy.scan</param-name>
            <param-value>true</param-value>
      </context-param>
</web-app>

Register listener ResteasyBootstrap to scan resource and provider classes and context-param  resteasy.scan with value true. Also add HttpServletDispatcher to handle request and response. JAX-RS runtime is designed around this servlet.

Step #7 Deploy the service in servlet container. This can be done using either registering Tomcat (or any other container) with eclipse or deploying it to tomcat manually. Before that copy compiled class files from build directory to JAXRS/WEB-INF/classes (create classes folder).

Approach #1 Register servlet container into eclipse.
Register the container into eclipse and right click on project and click on Run As … -> Run on Server.
In this approach the resource will be published with REST as context root.

Approach #2 Deploy manually:
Copy JAXRS directory into tomcat’s webapps directory and start tomcat.
In this approach the resource will be published with JAXRS as context root.

Make adjustment to URL in accessing the resource as per context root. 

All the manual tasks like copying class files can be automated using Ant script.

Step #8 Test it with Mozilla RESTClient (any other tool like SoapUI can be used)

Test 1: GET request without id with no accept header.
 As no header specified for content negotiation, the first appearing in list of media types specified in Produces/Consumes annotation will be used. So response is sent in XML representation.

Test 2: GET request with id with accept header.
As id (23) is specified in path param and accept HTTP header is specified as "application/json". So the method expecting a integer param will be called and response will be sent in JSON representation. If this accept media type is not supported by resource an HTTP 406 (unacceptable) will be sent as error.

Test 3: POST request
 Here we are sending POST request with XML representation of Order, which will be saved and identifier will sent back to client.

Step #9 Write clients for these resource methods.

Apache HTTPClient: For this copy httpclient-*.jar and httpcore-*.jar from RESTEasy distribution's lib to project and add to project build path. Then, add a class Client with following content.

Client.java

import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;



public class Client {

    public static void main(String[] args) throws Exception {
        HttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet("http://localhost:8080/REST/resource/12");
        System.out.println(getResult(client, get));
       
        get.addHeader("Accept", "application/json");
        System.out.println(getResult(client, get));
       
        HttpPost post = new HttpPost("http://localhost:8080/REST/resource");
        post.addHeader("Content-Type", "application/xml");
        post.setEntity(new StringEntity("<Order><id>12</id><name>name</name></Order>"));
        System.out.println(getResult(client, post));
    }
   
    private static String getResult(HttpClient client, HttpRequestBase method) throws Exception, IOException{
        HttpResponse response = client.execute(method);
        if(response.getStatusLine().getStatusCode()==200){
            return EntityUtils.toString(response.getEntity());
        }else{
            throw new Exception(response.getStatusLine().toString());
        }
    }
}

For more on RESTEasy client see
http://docs.jboss.org/resteasy/2.0.0.GA/userguide/html/RESTEasy_Client_Framework.html
http://docs.jboss.org/resteasy/2.0.0.GA/userguide/html/AJAX_Client.html


RESTEasy has support for client side for Java and Javascript both. A Javascript client can be generated.

Refrences:
JSR-311 http://jsr311.java.net/nonav/releases/1.1/spec/spec.html

Popular Posts