3

I try to add an error object to response (with HTTP status 422). It works fine, but I want also to add the scheme of my error object to the automatically generated WADL.

Code:

JAX-B model classes:

@XmlRootElement(namespace = "http://www.test.com/test")
@XmlAccessorType(value = XmlAccessType.FIELD)
public class UnprocessableEntityError {

    @XmlElement
    private String key;

    public String getKey() {
        return key;
    }

    public void setKey(final String key) {
        this.key = key;
    }
}

@XmlRootElement(namespace = "http://www.test.com/")
public class TestModel {
}

JAX-RS resource class:

@Path("test")
public class TestResource {

    @POST
    public TestModel doSomething() {
        throw new WebApplicationException("Error", Response.status(422).entity(new UnprocessableEntityError()).build());
    }
}

CXF configuration:

<jaxrs:server address="/rest" id="test" staticSubresourceResolution="true">
    <jaxrs:serviceBeans>
        <ref bean="testResource" /> 
    </jaxrs:serviceBeans>
    <jaxrs:providers>
        <bean class="org.apache.cxf.jaxrs.provider.JAXBElementProvider" />
        <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider" />
    </jaxrs:providers>
</jaxrs:server>

WADL:

<?xml version="1.0"?>
<application xmlns:prefix1="http://www.test.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://wadl.dev.java.net/2009/02">
    <grammars>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.test.com/" targetNamespace="http://www.test.com/" elementFormDefault="unqualified" attributeFormDefault="unqualified">
            <xs:complexType name="testModel">
                <xs:sequence/>
            </xs:complexType>
        </xs:schema>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.test.com/" targetNamespace="http://www.test.com/" elementFormDefault="unqualified" attributeFormDefault="unqualified">
            <xs:import/>
            <xs:element name="testModel" type="testModel"/>
        </xs:schema>
    </grammars>
    <resources base="http://localhost:8080/test-app/services/rest/1">
        <resource path="/test">
            <method name="POST">
                <response>
                    <representation mediaType="*/*" element="prefix1:testModel"/>
                </response>
            </method>
        </resource>
    </resources>
</application>

Is there any way, to add (just for documentation) an additional element to the grammar of an automatically generated WADL?

Community
  • 1
  • 1
dur
  • 15,689
  • 25
  • 79
  • 125
  • Include an XSD with definition would be valid for you? (with a link to external xsd or inline definition) – pedrofb Jun 07 '16 at 07:56
  • @pedrofb: How could I include a XSD? Is there any annotation or configuration property? – dur Jun 07 '16 at 08:29

1 Answers1

0

You can customize WADL automatic generation but is not very flexible. The general CXF documentation is here but is not helpful.

In the area of <grammars> you can include a custom XSD or link one

Define a WadlGenerator in CXF spring file, and include in the jaxrs provider. For example with books.xsd

<jaxrs:server address="/rest" id="test" >
    <jaxrs:providers>
        <ref bean="wadlGenerator" />
    </jaxrs:providers>
</jaxrs:server>

<bean id="wadlGenerator" class="org.apache.cxf.jaxrs.model.wadl.WadlGenerator">
    <property name="schemaLocations" value="classpath:/books.xsd"/>
</bean>

Programmatically is also possible

WadlGenerator wg = new WadlGenerator();
wg.setSchemaLocations(Collections.singletonList("classpath:/books.xsd"));

The generated WADL will be like this

<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <grammars>
       <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://superbooks" attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://superbooks">
         <xs:element name="thebook" type="tns:book"/>
         <xs:complexType name="book">
            <xs:sequence>
                <xs:element minOccurs="0" name="chapter" type="xs:string"/>
                <xs:element name="id" type="xs:int"/>
            </xs:sequence>
         </xs:complexType>
      </xs:schema>
</grammars>

Also, you can include a link

<bean id="wadlGenerator" class="org.apache.cxf.jaxrs.model.wadl.WadlGenerator">
    <property name="externalLinks" value="http://books"/> 
</bean>

WADL

<grammars>
    <include href="http://books"/>
</grammars>

The full documentation of WadlGenerator. I have tested with CXF 2.7

EDITED

Custom WADL Generator

CXF WadlGenerator fills the 'grammars' section with XSD, external links or the autogenerated wadl, but do not allow to combine them. To add an external resource link to generated grammar is needed to create a custom WadlGenerator

<grammars>
    <include href="http://books"/>
    <!-- The autogenerated grammar-->
</grammars>

This is a fully functional class for CXF 2.7, but I hope it works at any later version because uses inheritance and rewrite only a minimal part of code. It concats externalLinks (if exists) with the autogenerated code or XSD

package com.wadl;

import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;

import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBContext;

import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.jaxrs.model.ResourceTypes;
import org.apache.cxf.jaxrs.model.wadl.WadlGenerator;

public class CustomWadlGenerator extends WadlGenerator {
    private List<URI> externalSchemaLinks;
    private static final Logger LOG = LogUtils.getL7dLogger(CustomWadlGenerator.class);

    //Overwrite setExternalLink so that it is not used in the superclass
    @Override
    public void setExternalLinks(List<String> externalLinks) {
        externalSchemaLinks = new LinkedList<URI>();
        for (String s : externalLinks) {
            try {
                String href = s;
                if (href.startsWith("classpath:")) {
                    int index = href.lastIndexOf('/');
                    href = index == -1 ? href.substring(9) : href.substring(index + 1);

                }
                externalSchemaLinks.add(URI.create(href));
            } catch (Exception ex) {
                LOG.warning("Not a valid URI : " + s);
                externalSchemaLinks = null;
                break;
            }
        }
    }


    private class ExternalSchemaWriter implements WadlGenerator.SchemaWriter {

        private List<URI>links;
        private UriInfo uriInfo;
        private SchemaWriter writer;

        public ExternalSchemaWriter(List<URI>links, UriInfo ui, SchemaWriter writer){
            this.links = links;
            this.uriInfo = ui;
            this.writer = writer;
        }
        public void write(StringBuilder sb) {
            //write links
            for (URI link : links) {
                try {
                    URI value = link.isAbsolute() ? link : uriInfo.getBaseUriBuilder().path(link.toString()).build(new Object[0]);

                    sb.append("<include href=\"").append(value.toString()).append("\"/>");
                } catch (Exception ex) {
                     CustomWadlGenerator.LOG.warning("WADL grammar section will be incomplete, this link is not a valid URI : " + link.toString());
                }
            }

           //concat with default writer
            writer.write(sb);
        }
    }   

    @Override
    protected SchemaWriter createSchemaWriter(ResourceTypes resourceTypes, JAXBContext context, UriInfo ui)  {
        SchemaWriter schemaCollectionWriter = super.createSchemaWriter(resourceTypes, context, ui);

        if (externalSchemaLinks == null){
            //default behaviour
            return schemaCollectionWriter;
        } else {
            //use custom writer
            return new ExternalSchemaWriter(externalSchemaLinks,ui,schemaCollectionWriter);
        }
    }
}

Set WadlGenerator in spring config

<bean id="wadlGenerator" class="com.wadl.CustomWadlGenerator">
    <property name="externalLinks" value="http://books"/> 
</bean>
pedrofb
  • 37,271
  • 5
  • 94
  • 142
  • Unfortunately property `externalLinks` is not working as I need. CXF adds the link, but removes all other elements from existing generated scheme. – dur Jun 07 '16 at 13:56
  • Same problem with property `schemaLocations`, CXF replaces the grammar with the new XSD instead of adding the new XSD to the generated grammar. – dur Jun 07 '16 at 14:03
  • A solution like this would be acceptable?` ` The specification allows it. `WadlGenerator` do not support this, but the code seems simple to update. If it is suitable for you, i can try it – pedrofb Jun 07 '16 at 14:44
  • `WadlGenerator.createSchemaWriter` is protected, so it can be rewritten in a subclass to combine `ExternalSchemaWriter` and the default writer. – pedrofb Jun 08 '16 at 06:39