Sunday, July 29, 2012

Spring MVC WADL generation


As in other REST based frameworks in java, most of them provide an out of box support for genreation of WADL file ( web application description language). It's an XML file that is being composed of the description of all the resources that your REST based API is going to expose. This blog is a collection of the codes , that is being used in order to generate WADL through Spring. (Note: spring MVC does not provide an inbuilt way of generating this file and does not implements JSR-311 fully, so we can expect some mismatch here and there), however other frameworks like JERSEY (which is a full fledged REST implementation) provides complete support for this.

In order to generate application.wadl, we must have to understand the structure of the WADL (http://www.w3.org/Submission/wadl/wadl.xsd). This XSD contains a list of all XML elements and attributes that can be present within an WADL file).

There are simply two steps to achieve the generation:
Step 1: Via using the above XSD we have to generate  the classes, that will represent all the elements in the WADL XML file. The command is simple one. Just download the XSD onto your local machine and hit the command "xjc wadl.xsd" and you will get a number of java file in the working directory. In case you want to specify a specific package name for the generated java files, you can achieve this via a number of command line options provided by xjc. So for example i have generated the files in the following folder "com.mine.wadl.artifact" and here is a list of all the files present in that folder. ( I have renamed each file so that the name starts with WADL).
            1.


Step2: This step is all about writing a spring controller, that will map to the "application.wadl" path and will generate the XML. we have to make sure, that the JAXB marshall or any otther that we are using, must be onto the classpath as Spring will make use of it to generate the XML file. Here is the source code for generating this.
package com.mine.wadl.generator;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.xml.namespace.QName;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.mine.wadl.artifact.WadlApplication;
import com.mine.wadl.artifact.WadlDoc;
import com.mine.wadl.artifact.WadlMethod;
import com.mine.wadl.artifact.WadlParam;
import com.mine.wadl.artifact.WadlParamStyle;
import com.mine.wadl.artifact.WadlRepresentation;
import com.mine.wadl.artifact.WadlRequest;
import com.mine.wadl.artifact.WadlResource;
import com.mine.wadl.artifact.WadlResources;
import com.mine.wadl.artifact.WadlResponse;

/**
 * Type name:WadlController.java
 Description:  This Class
 * will be responsible for generation the Web application descriptor file based
 * upon the  
 References:
 * 
 * 

 * 
 * @author Pankaj Bhatt.
 * @version 1.0, June 2012
 */

@Controller
@RequestMapping
public class WadlController {

 // @Autowired
 private RequestMappingHandlerMapping handlerMapping;

 /**
  * Constructor for initializing the Wadl Controller
  * 
  * @param handlerMapping
  */
 @Autowired
 public WadlController(RequestMappingHandlerMapping handlerMapping) {
  this.handlerMapping = handlerMapping;
 }


 /**
  * This is a function which will be responsible for generating the WADL
  * file.
  * 
  * @param request : Represents the Request
  * @return WadlApplication : This object will be converted to the WADL File.
  */
 @RequestMapping(method = RequestMethod.GET, produces = { "application/xml" })
 public @ResponseBody WadlApplication generateWadl(HttpServletRequest request) {
  WadlApplication result = new WadlApplication();
  WadlDoc doc = new WadlDoc();
  doc.setTitle("REST Service WADL");
  result.getDoc().add(doc);
  WadlResources wadResources = new WadlResources();
  wadResources.setBase(getBaseUrl(request));

  Map handletMethods = handlerMapping
    .getHandlerMethods();
  for (Map.Entry entry : handletMethods
    .entrySet()) {
   WadlResource wadlResource = new WadlResource();

   HandlerMethod handlerMethod = entry.getValue();
   RequestMappingInfo mappingInfo = entry.getKey();

   Set pattern = mappingInfo.getPatternsCondition().getPatterns();
   Set httpMethods = mappingInfo.getMethodsCondition().getMethods();
   ProducesRequestCondition producesRequestCondition = mappingInfo
     .getProducesCondition();
   Set mediaTypes = producesRequestCondition
     .getProducibleMediaTypes();

   for (RequestMethod httpMethod : httpMethods) {
    WadlMethod wadlMethod = new WadlMethod();

    for (String uri : pattern) {
     wadlResource.setPath(uri);
    }

    wadlMethod.setName(httpMethod.name());
    Method javaMethod = handlerMethod.getMethod();
    wadlMethod.setId(javaMethod.getName());
    WadlDoc wadlDocMethod = new WadlDoc();
    wadlDocMethod.setTitle(javaMethod.getDeclaringClass().getName()+ "." + javaMethod.getName());
    wadlMethod.getDoc().add(wadlDocMethod);

    // Request
    WadlRequest wadlRequest = new WadlRequest();

    Annotation[][] annotations = javaMethod.getParameterAnnotations();
    Class[] paramTypes = javaMethod.getParameterTypes();
    int parameterCounter = 0;

    for (Annotation[] annotation : annotations) {
     for (Annotation annotation2 : annotation) {
      if (annotation2 instanceof RequestParam) {
       RequestParam param2 = (RequestParam) annotation2;

       WadlParam waldParam = new WadlParam();

       waldParam.setName(param2.value());

       waldParam.setStyle(WadlParamStyle.QUERY);
       waldParam.setRequired(param2.required());

       if (paramTypes != null
         && paramTypes.length > parameterCounter) {
        if (paramTypes.length > parameterCounter
          && (paramTypes[parameterCounter] == javax.servlet.http.HttpServletRequest.class || paramTypes[parameterCounter] == javax.servlet.http.HttpServletResponse.class))
         parameterCounter++;
        if (paramTypes.length > parameterCounter
          && (paramTypes[parameterCounter] == javax.servlet.http.HttpServletRequest.class || paramTypes[parameterCounter] == javax.servlet.http.HttpServletResponse.class))
         parameterCounter++;

        if (paramTypes.length > parameterCounter) {

         waldParam
           .setType(getQNameForType(paramTypes[parameterCounter]));
         parameterCounter++;
        }
       }

       String defaultValue = cleanDefault(param2
         .defaultValue());
       if (!defaultValue.equals("")) {
        waldParam.setDefault(defaultValue);
       }
       wadlRequest.getParam().add(waldParam);
      } else if (annotation2 instanceof PathVariable) {
       PathVariable param2 = (PathVariable) annotation2;

       WadlParam waldParam = new WadlParam();
       waldParam.setName(param2.value());
       waldParam.setStyle(WadlParamStyle.TEMPLATE);
       waldParam.setRequired(true);
       if (paramTypes != null
         && paramTypes.length > parameterCounter) {
        if (paramTypes.length > parameterCounter
          && (paramTypes[parameterCounter] == javax.servlet.http.HttpServletRequest.class || paramTypes[parameterCounter] == javax.servlet.http.HttpServletResponse.class))
         parameterCounter++;
        if (paramTypes.length > parameterCounter
          && (paramTypes[parameterCounter] == javax.servlet.http.HttpServletRequest.class || paramTypes[parameterCounter] == javax.servlet.http.HttpServletResponse.class))
         parameterCounter++;

        if (paramTypes.length > parameterCounter) {

         waldParam
           .setType(getQNameForType(paramTypes[parameterCounter]));
         parameterCounter++;
        }
       }

       wadlRequest.getParam().add(waldParam);
      } else
       parameterCounter++;
     }
    }
    if (!wadlRequest.getParam().isEmpty()) {
     wadlMethod.setRequest(wadlRequest);
    }

    // Response
    if (!mediaTypes.isEmpty()) {
     WadlResponse wadlResponse = new WadlResponse();
     wadlResponse.getStatus().add(200l);
     for (MediaType mediaType : mediaTypes) {
      WadlRepresentation wadlRepresentation = new WadlRepresentation();
      wadlRepresentation.setMediaType(mediaType.toString());
      wadlResponse.getRepresentation()
        .add(wadlRepresentation);
     }
     wadlMethod.getResponse().add(wadlResponse);
    }

    wadlResource.getMethodOrResource().add(wadlMethod);

   }

   wadResources.getResource().add(wadlResource);

  }
  result.getResources().add(wadResources);

  return result;
 }

 private String getBaseUrl(HttpServletRequest request) {

  return request.getScheme() + "://" + request.getServerName() + ":"
    + request.getServerPort() + "" + request.getContextPath() + "/"
    + request.getServletPath().substring(1);
 }

 private String cleanDefault(String value) {
  value = value.replaceAll("\t", "");
  value = value.replaceAll("\n", "");
  value = value.replaceAll("?", "");
  value = value.replaceAll("?", "");
  value = value.replaceAll("?", "");
  return value;
 }

/**
  * This is an private function, which will return the QName based upon the
  * Java Type.
  * 
  * @param classType
  *            : Represent the type of class
  * @return QName
  */
  private QName getQNameForType(Class classType) {
  QName qName = null;

  /**
   * Check whether the thing that is coming is an Array of a data type or
   * not.
   */
  if (classType.isArray()) {
   classType = classType.getComponentType();
  }

  if (classType == java.lang.Long.class)
   qName = new QName("http://www.w3.org/2001/XMLSchema", "long");
  else if (classType == java.lang.Integer.class)
   qName = new QName("http://www.w3.org/2001/XMLSchema", "integer");
  else if (classType == java.lang.Double.class)
   qName = new QName("http://www.w3.org/2001/XMLSchema", "double");
  else if (classType == java.lang.String.class)
   qName = new QName("http://www.w3.org/2001/XMLSchema", "string");
  else if (classType == java.util.Date.class)
   qName = new QName("http://www.w3.org/2001/XMLSchema", "date");

  return qName;
 }

}

I know this is a long stuff, but let me go one by one & line by line( I will only explain those stuff, which will help you to customize your implementation).

Line 61-64: This is the most important part of the generation, as it initialized the contains the initialization of RequestMappingHandlerMapping object, which is present within spring and contains all the details of all the URI's that we have exposed through Spring MVC. In addtion, to it, it also contains details of the methods that have those Spring MVC Rest based annotations. later on we will see how we will make use of this to find out the information in which we are in terested in.
Line 74:
We are simply annotating a function so that it will be invoked once we type "http://blah.com/springserveletmapping/application.wadl".
Line 75 - 113: As you can see here we are creating the foundationg for generating XML and invoking functions of requ>estmappinghandlermapping to find out the set of functions which have the Spring MVC Rest based annotations. We are also looking for the media types that are being supported by the functions, ( if any present in the defintions of the functions). This is continued till line no 113.
Line 114: This is the section, in which we are being interested in, Here as we know, every function that ismapped to some URI via spring MVC , can have any type of parameters.
E.g. public DataToBeReturned getLoginData(@PathVariable int id, HttpServeltRequest req, @RequestParam(value="name" , required=true) String userName)
However, in the WADL we only want those parameters to be listed which we are collecting from the URI e.g. either from header, requst parameters or through path variables, any other parameters beyond them is need not be included in the WADL. So here we are removing the inclusing of HttpServletRequest and HttpServletResponse from inclusion in WADL.
     Based on the type of annotation on the parameter it will be included either as a path variable or request parameter (type QUERY). For all @RequestParameter it is mandatory to include (value and required attribute) otherwise we wont be able to include the corressponding information in the WADL. value attribute reflects the name (e.g what is the request parameter name) and required  tells us whether that parameter is necessary for processing for the request or not, otherwise you will bound to get a BAD Request ( Http 400 error code)
.


Line 212: The function at this line, will help us in calculating the Base URI on which all the resources are being mapped. This has to be modified as per your own requirements.
Line 236: Here we are specifying a function, that will return the type of QName for the type of parameters that we are passing in the function that is mapped to an URI. since here, I am only using Long,Integer,Double,String,Date. In, case you need to add more, please change this function to include the type of parameter of your choice).

And that's all. Once you will hit at /application.wadl you will get the XML File, mentioning your resources. I have tested the file consumption by SOAP UI and it all blends well.

Here i have specifically used Spring 3.1.0.Release, however i will suggest to go for 3.1.1Release as it has some nice little improvements.

At the last, I am thankful to lot to tomasz nurkiewicz & GrĂ©gory OLIVER, it is only because of their direction and help with code, I am able to do this. So all the appreciation goes to them directly. Thanks tomasz and gregory.

here are some of the links, that you may find useful.




Here is Tomasz GitHub url for this project : https://github.com/nurkiewicz/spring-rest-wadl
Hope, it helps the developer community. 
If get time, or if there is a need i will upload the maven pom for this project. 

Thanks. 


   

Tuesday, May 15, 2012

Spring MVC - REST ( JSON/XML) - Configuration



This post is an compilation of the problems that i have faced while configuring an Spring MVC Application that will acts as an API server providing all the services in an REST based manner.
It is an simple task to configure the whole environment and to make services available. However i have faced the following set of problems, which are little different in nature->

  1. Use JAXB annotations for the keys of the JSON that is being returned from the server (by default the JACKSON processor will take the variables names and will associate them as the keys in the returned JSON.
  2. Use same keys (in json) / tags (in xml) to receive the request (in POST/PUT).
  3. Apply Hibernate Validator by making use of Spring validation.
Guys at VMWare spring has done a fabulous jobs and all of the above mentioned requirements will be collated into a single line of XML that you need to declared in your dispatcher-servlet.xml (or any of the other spring application context file you are importing to build your app context
).
&
lt;mvc:annotation-driven/>
This line does a number of things
  1. Automatically add all types of interceptors like XML Based interceptors, String, Form based interceptors.
  2. Will provide automatic marshalling of your response/value object in XML/JSON
  3. Will provide the validation of your request VO, if you have enabled the @ExceptionHandler annotation on some method in your controller.
The problem that i had faced is that i could not be able to use my JAXB annotations as the KEYS in my request/response, because when i declared a different Jackson processor and added it to the AnnotationHandlerAdapter provided by spring, it is being ignored brutally by Spring under the wraps. And the one defined by the <mvc:annotation-driven/> one will take priority. As soon as the remove this annotation to make my JSON (with jaxb annotations you required another library JACKSON-XC.jar) the validation thing ceases to stopped. This all forces me to look onto the varous JIRA bugs raised against this annotation and finally i will be able to solve the problem with the help of Spring 3.1.

Note:- In case you are using spring 3.0 jar, please replace them with Spring 3.1 jar and use 3.1.xsd everywhere in your application context xml files (because the configuration which is represented below will only work it the XSD of 3.1 is being used as 3.0 does not allow any attribute and sub-element within the <mvc:annotation-driven/> option.
Following is the configuration which will allow use to solve the above three problems.
(Please make sure before running the code you must have all the bindings and jar files available in your classpath, Here is a brief listing of those.
-> For Validation: Hibernate-validator 4.2.0.Final.jar, javax.validation.api (JSR 303)
-> For Jackson : Jackson-core.jar, jackson-mapper-asl.jar, jacakson-xc.1.7.5.jar
-> For JaxB -> Jaxb API and Impl . jar

Configuration code:



 
 
 
 
   
   
 
    
  
  
    
  
   
    
    com.mycompany..CreateEmployeeRequestVO
    com.mycompany..CreateEmployeeResponseVO
   
  
  
     

 
 
      


  
  
   
     
      
     
      
       
        
        
        
       
      
     
   
   
    
    
     
      
       
       
       
                                               
     
    
   
   
  


NOTE: Please note that in above xml the last line has only one </beans> and nothing else. The three </bean> is being printed wrongly by the javascript library, i am using.

Here is the description of the components of XML file

  • Line 1-5 simply declares the XML namespaces and the various XSD files we are going to use.
  • Line 10-15 will register the JAXB Annotation Introspecter and associate itself with the ObjectMapper of the jackson library. (Many of the blogs will show directly mapping this to annotationIntrospector property of the objectmapper, but it has been changed, with the introduction of new API, now the annotationIntrospector is present in the serializationConfig and deserializationConfig object of the Object Mapper). This is the key to use JAXB annotations in the JSON Processing.
  • Line 18-27 registers the JAXB2 Marshaller taken from the OXM Library. here we can register the varous classes that we have to use either for taking the input or in generating the output i.e. RequestValueObject and ResponseValueObject. However, i found that there is no way through which you can give directly a packagename or a list of packages. The Other option that we have is to use eitehr the contextPath property or the jaxb.index thing.
  • LIne 31-32 will intialize the Spring Validator and the conversion service factory bean, which will automatically going to use the hibernate validator present on the classpath. The advantage of using hibernate validator is that the availablity of extra custom annotations like NotEmpty etc. It provides an way through which we can remove the annotations from our value objeect and place at in an XML file and at run time, the validator picks that file to perform the listed constraints on the various attributes of the request Value object.
  • Line 34-62 is the meat of the whole subject and contains all the necesseary plumbing to make the things work. (Note: for this to work we must have to include Spring 3.1 xsd otherwise Spring will throw an error that no element or attribute will be alloweed with ). IT does two things:
    • Registeres the custom XML marshaller converter with the automatically registered AnnotationHandlerAdapter by replacing the existing default converter.
    • Registers the custom JSON mapping converter, where we are using new created jacksonObjectMapper rather then the one which is avaiable by default. This also will displace the default registered converter with AnnotationHandlerAdapter with this one.
And that's all. Voila.. all of our test cases are running and our JSON/XML are being generated/consumed properly. In order to achieve this, i had taken help from various links, which i had mentioned below. And at last thanks to Juergen Holler and the team for the clear explanation on the JIRA.

https://jira.springsource.org/browse/SPR-6306
https://jira.springsource.org/browse/SPR-7504
https://jira.springsource.org/browse/SPR-7967
https://jira.springsource.org/browse/SPR-6817
https://jira.springsource.org/browse/SPR-6524

http://www.aviyehuda.com/2010/04/using-hibernate-validator-to-cover-your-validation-needs/
http://java.dzone.com/articles/using-hibernate-validator
http://stackoverflow.com/questions/8679122/custom-httpmessageconverters-does-not-take-precedence-for-string-objects-in-spri
http://stackoverflow.com/questions/7199652/mvcannotation-driven-with-un-annotated-controllers
http://forum.springsource.org/showthread.php?106594-complete-XML-for-mimicing-annotation-driven
http://stackoverflow.com/questions/6177913/configuring-the-jacksonobjectmapper-not-working-in-spring-mvc-3
http://scottfrederick.blogspot.in/2011/03/customizing-spring-3-mvcannotation.html
http://stackoverflow.com/questions/3693397/howto-get-rid-of-mvcannotation-driven
http://forum.springsource.org/showthread.php?101450-lt-mvc-annotation-driven-gt-issues

I hope, it will prove useful for the developer community.