CXF – Missing WADL method parameter element types with JSON JAX-RS services

If you’re using JSON to exchange data with your CXF JAX-RS service clients you might find your out-of-the-box WADL file a little light on detail.

WADL (Web Application Description Language) files are an excellent way of making our REST services self-describing. The structured list of data types and endpoints, when used with general purpose client applications like SoapUI, let us drive our lightweight XML over HTTP services with much the same point-and-click convenience with which we can drive our meatier SOAP ones.

And, if you’re working code-first with JAX-RS and using Apache CXF as your provider, you’ll get a fully functional WADL file for free for your XML over HTTP services.

If, on the other hand, your doing JSON over HTTP, you’ll have a little bit of extra work to do.

The code for this example is available on GitHub

Following a few comments from people struggling to get this example to work, I’ve also uploaded a runnable containerised version to Docker Hub.

Waddling with XML

By way of a simple example, here’s a JAX-RS annotated interface for a mortgage payment calculator service:-

@Path("/")
public interface MortgageCalculator {

    @POST
    @Consumes(MediaType.APPLICATION_XML)
    @Path("/payments")
    Float calculatePayment(MortgageDetails dtls);
}

This service takes the total mortgaged property value, the deposit value, the term and the associated interest rate and returns an indicative monthly payment. Since these parameters don’t really lend themselves to a URL path, though we could accept them as query parameters on an HTTP GET, for this example we’ll instead take them in an HTTP POST request parameter matching the MortgageDetails value object-

@XmlRootElement
public class MortgageDetails {

    private Float interestRate;
    private Integer totalAmount;
    private Integer depositAmount;
    private Integer termInYears;

    public MortgageDetails() {
    }

    // Getters and Setters
}

Deploying this service via CXF with the following Spring configuration:-

<jaxrs:server id="cxfMortgageCalculatorService" address="/">
    <jaxrs:serviceBeans>
        <ref bean="mortgageCalculator" />
    </jaxrs:serviceBeans>
</jaxrs:server>

Gives us a rather nifty WADL file when we stick ?_wadl on a request to our application (e.g. http://localhost:8080/mortgage-calculator?_wadl):-

<application xmlns="http://wadl.dev.java.net/2009/02"
             xmlns:xs="http://www.w3.org/2001/XMLSchema"
             xmlns:prefix1="https://devsumo.com/mortgagecalculator">

    <grammars>
    …
        <xs:complexType name="mortgageDetails">
            <xs:sequence>
                <xs:element minOccurs="0" name="depositAmount" 
                        type="xs:int"/>
                <xs:element minOccurs="0" name="interestRate" 
                        type="xs:float"/>
                <xs:element minOccurs="0" name="termInYears" 
                        type="xs:int"/>
                <xs:element minOccurs="0" name="totalAmount" 
                        type="xs:int"/>
            </xs:sequence>
        </xs:complexType>
        …
    </grammars>

    <resources base="http://localhost:8080/mortgage-calculator/">
        <resource path="/">
            <resource path="payments">
                <method name="POST">
                    <request>
                        <representation mediaType="application/xml"
                                element="prefix1:mortgageDetails"/>
                    </request>
                    …
                </method>
            </resource>
        </resource>
    </resources>
</application>

And we get this completely for free with recent versions of CXF 2.x, though with CXF 3 we need an extra Maven dependency as the WADL generator has been moved out of the cxf-rt-frontend-jaxrs package and into the, admittedly more meaningfully modular, cxf-rt-rs-service-description package instead.

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-rs-service-description</artifactId>
    <version>3.0.3</version>
</dependency>

The highlighted element setting in the <representation/> element allows SoapUI or other dynamic clients to auto-generate a template request body for us, which makes testing and onboarding services like this pretty slick:-

Posting to a JAX-RS XML service with SoapUI

Waddling with JSON

If we change our request parameter type to JSON though:-

@Path("/")
public interface MortgageCalculator {

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/payments")
    Float calculatePayment(MortgageDetails dtls);
}

Then our WADL comes up short:-

<method name="POST">
    <request>
        <representation mediaType="application/json"/>
    </request>
    …
</method>

Point SoapUI at this WADL and the “Re-create a default representation from the schema” button will be disabled and you’re kinda on your own.

CXF’s JSON handling piggy-backs on JAXB but the problem here is that the default configuration for its WadlGenerator class doesn’t automatically link JSON to the JAXB generated schema. In order to enable it we need to add the WadlGenerator to our Spring beans and tweak its configuration:-

<bean id="wadlGenerator"
      class="org.apache.cxf.jaxrs.model.wadl.WadlGenerator">
    <property name="linkJsonToXmlSchema" value="true" />
</bean>

<jaxrs:server id="cxfMortgageCalculatorService" address="/">
    <jaxrs:serviceBeans>
        <ref bean="mortgageCalculator" />
    </jaxrs:serviceBeans>
    <jaxrs:extensionMappings>
        <entry key="json" value="application/json" />
    </jaxrs:extensionMappings>
    <jaxrs:providers>
        <ref bean="wadlGenerator" />
    </jaxrs:providers>
</jaxrs:server>

This gets our WADL back on track:-

<method name="POST">
    <request>
        <representation mediaType="application/json"
                        element="prefix1:mortgageDetails"/>
    </request>
    …
</method>

And SoapUI can once more generate a default payload for us:-

Posting JSON to a JAX-RS service with SoapUI

Waddling with JSON (redux)

Submitting this request though results in an error:-

HTTP/1.1 415 Unsupported Media Type

And the following appears in our server log file:-

SEVERE: No message body reader has been found for class
com.devsumo. mortgagecalculator.MortgageDetails,
ContentType: application/json

Simply setting a @Produces or @Consumes to APPLICATION_JSON on a JAX-RS interface method isn’t quite enough to get JSON flowing through CXF. There are a couple of extra steps required.

First up we need to add a JSON provider to our build. CXF supports either Jettison or Jackson. For this example we’ll go with Jettison:-

<dependency>
    <groupId>org.codehaus.jettison</groupId>
    <artifactId>jettison</artifactId>
    <version>1.3.5</version>
</dependency>

And we need to add CXF’s Jettison JSONProvider to our <jaxrs:providers/> list:-

<bean id="jsonProvider"
      class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
    <property name="supportUnwrapped" value="true"/>
    <property name="dropRootElement" value="true"/>
    <property name="namespaceMap">
        <map>
            <entry key="https://devsumo.com/mortgagecalculator" 
                   value="mort"/>
        </map>
    </property>
</bean>

<jaxrs:server id="cxfMortgageCalculatorService" address="/">
    <jaxrs:serviceBeans>
        <ref bean="mortgageCalculator" />
    </jaxrs:serviceBeans>
    <jaxrs:extensionMappings>
        <entry key="json" value="application/json" />
    </jaxrs:extensionMappings>
    <jaxrs:providers>
        <ref bean="wadlGenerator" />
        <ref bean="jsonProvider"/>
    </jaxrs:providers>
</jaxrs:server>

JSONProvider offers a variety of options to control how JSON is marshalled on our behalf – the above minimal options make it friendly with the format generated by SoapUI.

8 responses to “CXF – Missing WADL method parameter element types with JSON JAX-RS services

  1. Hi,

    You say :
    “The highlighted element setting in the element allows SoapUI or other dynamic clients to auto-generate a template request body for us, which makes testing and onboarding services like this pretty slick:-”

    I can’t arrive to have this “auto-generating” template request body in soapui. Can you provide your full WADL? What version of soapui are you using?

    thanks in advance

    Clément

  2. Hi,

    I have download your code and running but it doesn’t show XSD in grammers.
    I want to make WADL Auto Generation with XSD.
    Do you have any idea for that?

    Regards,
    Albert.

    • Just tested it again to make sure it still works 🙂

      Pulling down the java-cxf-examples project, building “mortgage-calculator” with JDK 1.7/Maven 3 and deploying the WAR to TomCat 7.0.34 on my Mac still serves a WADL with a couple of <schema> elements in the <grammars/> section.

      Deployment environment “shouldn’t” matter here (but we don’t live in Should Land!) … what are you building and deploying on? Any errors in the TomCat logs?

  3. Hi,

    Thanks for your quick answer. But I can’t get the result that you have.
    I just download your code again and import my eclipse and compile. Nothing has changed regardless log. Thanks again. I couldn’t find the reason.

    1) Java : version “1.8.0.60” and “1.7.0.79”
    2) Apache CXF: 3.0.3 and 3.1.4
    3) Tomcat 7.0.59
    4) Just for Logging change in bean.xml

    5)Below is my test log
    Response-Code: 200
    Content-Type: application/xml
    Headers: {Content-Type=[application/xml], Date=[Thu, 25 Feb 2016 05:41:04 GMT]}
    Payload:

    Regards,
    Albert.

    • Hmmmm… I’ve tried the same config on OS X and Windows and the WADL comes back fine with grammars on both @ http://localhost:8080/mortgage-calculator/?_wadl. Is it a clean TomCat install? If there are any non-standard JAR files in the lib folder (or any bespoke class path entries in the environment or in the startup script) that might cause issues. Also do you get any errors or messages on the TomCat console log?

  4. Hello

    I used your webapp and it works fine. The wadl is retrieved well but i don’t know why, after creating the project into SOAPUI, the request is not automatically generated in media type.

    Any idea ? Thank you.

    Jerome

    • Hi Jerome,

      The first thing I’d check is whether the grammar and schema are present in the WADL when you view it in a web-browser. If you’ve got a schema with a mortgageDetails complex type in it, then the WADL “should” be okay and the issue may be on the SoapUI side (are you using a recent version? When you create your new project are you clicking “Import WADL” before pasting in the URL? Do you have application/json set as a media type? Are there any import errors in SoapUI’s log, etc.)

      If you don’t have a schema the issue is on the web application side. I’ve tested building and deploying this on Windows and OS X – it’s a simple project so should build clean – but if you’re not getting a schema I’d check the web container logs for any errors when you request the WADL and also check you’ve no additional JARs deployed on the boot class path for your web container (e.g. in tomcat/lib or referenced in a system CLASSPATH environment variable) that might be interfering.

Leave a Reply

Your email address will not be published. Required fields are marked *