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 ( 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).

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
 Description:  This Class
 * will be responsible for generation the Web application descriptor file based
 * upon the  

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

public class WadlController {

 // @Autowired
 private RequestMappingHandlerMapping handlerMapping;

  * Constructor for initializing the Wadl Controller
  * @param handlerMapping
 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");
  WadlResources wadResources = new WadlResources();

  Map handletMethods = handlerMapping
  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
   Set mediaTypes = producesRequestCondition

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

    for (String uri : pattern) {

    Method javaMethod = handlerMethod.getMethod();
    WadlDoc wadlDocMethod = new WadlDoc();
    wadlDocMethod.setTitle(javaMethod.getDeclaringClass().getName()+ "." + javaMethod.getName());

    // 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();



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

        if (paramTypes.length > parameterCounter) {


       String defaultValue = cleanDefault(param2
       if (!defaultValue.equals("")) {
      } else if (annotation2 instanceof PathVariable) {
       PathVariable param2 = (PathVariable) annotation2;

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

        if (paramTypes.length > parameterCounter) {


      } else
    if (!wadlRequest.getParam().isEmpty()) {

    // Response
    if (!mediaTypes.isEmpty()) {
     WadlResponse wadlResponse = new WadlResponse();
     for (MediaType mediaType : mediaTypes) {
      WadlRepresentation wadlRepresentation = new WadlRepresentation();





  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("", "long");
  else if (classType == java.lang.Integer.class)
   qName = new QName("", "integer");
  else if (classType == java.lang.Double.class)
   qName = new QName("", "double");
  else if (classType == java.lang.String.class)
   qName = new QName("", "string");
  else if (classType == java.util.Date.class)
   qName = new QName("", "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 "".
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 :
Hope, it helps the developer community. 
If get time, or if there is a need i will upload the maven pom for this project.