Include possible values of @PathParam to WADL
Asked Answered
S

2

8

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.

Subvention answered 21/2, 2013 at 17:11 Comment(2)
public enum Attribure should be public enum Attribute ... was it copied from your code or just a typo?Lenwood
@martinjakubik, sure it was typo. Thank you. I've just edited the post and fixed this mistake.Subvention
B
2

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();
    }
}
Brote answered 2/3, 2013 at 16:14 Comment(0)
D
3

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

Dulcy answered 21/2, 2013 at 17:49 Comment(2)
Thank you for your effort. I also did not see any reference to this functionality but supposed that it is probably invoked somehow inderictly by reflection.Subvention
There are only a few implementors of WadlGenerator, and createParam() is not very sophisticated in any of them, so I just concluded that the option list is ignored if there is no reference to its getter.Dulcy
B
2

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();
    }
}
Brote answered 2/3, 2013 at 16:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.