Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Include possible values of @PathParam to WADL

We are using Jersey for RESTful API implementation utilizing its cool feature of automatic WADL generation.

Just as an example we have method

@GET
@Path("/{id}/{attribute}")
@Produces(MediaType.APPLICATION_JSON)
public Object getAttributeByID(@PathParam("id") long id, @PathParam("attribute") String attribute) {
....
}

This generates the following fragment in WADL:

<param type="xs:string" style="template" name="attribute:.*"/>

Attribute can be name, type, size and we want not only to validate the value at runtime but also show it in generated wadl According to this document such feature should be supported by generating several tags <option> inside <param>, i.e. I am expecting something like the following:

<param type="aws:Attributes" style="template" name="attribute">
  <option value="name"/> 
  <option value="type"/> 
  <option value="size"/> 
</param>

My problem is to enable it with Jersey. If failed to find relevant document and assumed that probably if I change the type of parameter from String to enum this feature will work automatically, so I changed the method signature to:

@Path("/{id}/{attribute}")
@Produces(MediaType.APPLICATION_JSON)
public Object getAttributeByID(@PathParam("id") long id, @PathParam("attribute") Attribute attribute) {
....
}

where

public enum Attribute {
    name, type, size
}

but Jersey still generates <param> tag without options and the type of parameter is still xs:string.

I tried to find it in code of Jersey and found class com.sun.research.ws.wadl.Option with relevant JAXB annotations, so it seems to be relevant, but I do not know how to make it working. I guess the problem is in WadlGeneratorConfig.

Here is relevant part of Jersey definition in our web.xml

<filter>
<filter-name>REST-API</filter-name>
<filter-class>com.sun.jersey.spi.container.servlet.ServletContainer</filter-class>
    ................
<init-param>
    <param-name>com.sun.jersey.config.property.WadlGeneratorConfig</param-name>
    <param-value>com.mycompany.resource.OurWADLGenerator</param-value>
</init-param>
<init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>com.mycompany</param-value>
</init-param>
</filter>

where OurWADLGenerator code is:

public class OurWADLGenerator extends WadlGeneratorConfig {
    @Override
    public List<WadlGeneratorDescription> configure() {
        return generator(WadlGeneratorApplicationDoc.class)
                .prop("applicationDocsStream", "application-doc.xml")
            .generator(WadlGeneratorResourceDocSupport.class)
                .prop("resourceDocStream", "resourcedoc.xml").descriptions();
    }
}

What am I missing here? Thanks in advance.

like image 302
AlexR Avatar asked Feb 21 '13 17:02

AlexR


2 Answers

A quick search for the usages of com.sun.research.ws.wadl.Param.getOption() (see results here) shows that it's actually never invoked from the library. I guess it's only there because these classes are generated by xjc from the wadl.xsd. It seems though that Jersey basically ignores this piece of information in wadl files, and similarly doesn't care to include it in wadl files it generates.

A couple of years ago we ended up writing our own code to generate wadl, because the available tooling was so poor. This might have changed since then, but the above issue shows that proper support for wadl is still not quite there. :(

like image 65
zagyi Avatar answered Oct 19 '22 19:10

zagyi


After few investigations I didn't find any code in jersey where the option list is populated. (probably something that is not supported yet)

So you can implement your own WadlGenerator and inserting it the generator chain.

Here is a sample OptionsWadlGenerator adding the <option> elements for parameter of type Enum

package com.mycompany;

import com.sun.jersey.api.model.AbstractMethod;
import com.sun.jersey.api.model.AbstractResource;
import com.sun.jersey.api.model.AbstractResourceMethod;
import com.sun.jersey.api.model.Parameter;
import com.sun.jersey.server.wadl.WadlGenerator;
import com.sun.research.ws.wadl.Application;
import com.sun.research.ws.wadl.Method;
import com.sun.research.ws.wadl.ObjectFactory;
import com.sun.research.ws.wadl.Option;
import com.sun.research.ws.wadl.Param;
import com.sun.research.ws.wadl.RepresentationType;
import com.sun.research.ws.wadl.Request;
import com.sun.research.ws.wadl.Resource;
import com.sun.research.ws.wadl.Resources;
import com.sun.research.ws.wadl.Response;

import javax.ws.rs.core.MediaType;

public class OptionsWadlGenerator implements WadlGenerator {

    private WadlGenerator _delegate;

    private ObjectFactory objectFactory = new ObjectFactory();

    @Override
    public Param createParam(AbstractResource r, AbstractMethod m, Parameter p) {
        Param param = _delegate.createParam(r, m, p);
        if(((Parameter)p).getParameterClass().isEnum()){
            Object[] values = p.getParameterClass().getEnumConstants();
            for(Object enumItem:values){
                Option option = objectFactory.createOption();
                option.setValue(((Enum)enumItem).name());
                param.getOption().add(option);
            }
        }
        return param;
    }

    @Override
    public void setWadlGeneratorDelegate(WadlGenerator delegate) {
        this._delegate = delegate;
    }

    @Override
    public Application createApplication() {
        return _delegate.createApplication();
    }

    ... all other methods also simply call the _delegate equivalent method    
}

And of course, to insert it in your chain, do something like that:

public class OurWADLGenerator extends WadlGeneratorConfig {
    @Override
    public List<WadlGeneratorDescription> configure() {
        return generator(WadlGeneratorApplicationDoc.class)
                .prop("applicationDocsStream", "application-doc.xml")
            .generator(WadlGeneratorResourceDocSupport.class)
                .prop("resourceDocStream", "resourcedoc.xml")
            .generator(OptionsWadlGenerator.class).descriptions();
    }
}
like image 35
ben75 Avatar answered Oct 19 '22 20:10

ben75