Sunday, January 27, 2013

Custom wadl generator in Jersey

Recently I was working on a REST service in Jersey. It seems that the wadl generator in Jersey does not support the "required" attribute for query parameters. The matter is discussed in various forums, and a few solutions has been proposed but not implemented in Jersey. So I decided to implement my own wadl generator and a required annotation to be used by my service implementation.

First I introduced an annotation to be used in the service implementation:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredParam
{}

Next I used the annotation in the service implementation:

@GET
@Produces(MediaType.APPLICATION_XML)
@Path("XYZ/{field}/{value}")
public Result getData(@RequiredParam @PathParam("field") final String field, @RequiredParam @PathParam("value") final String value) {
...
}

With that in place I introduced my own wadl generator. By extending the provided generator in Jersey only one method needs to be overridden.

public class CustomJAXBWadlGeneratorWithRequiredSupport extends WadlGeneratorJAXBGrammarGenerator{
@Override
public Param createParam( AbstractResource r, AbstractMethod m, final Parameter p )
{
Param param = super.createParam(r,m,p);
RequiredParam requiredAnnotation = p.getAnnotation(RequiredParam.class);
if(requiredAnnotation!=null) {
param.setRequired(true);
}
return param;
}
}

The wadl generator is configured by a custom wadl configuration:


public class CustomWadlGeneratorConfig extends WadlGeneratorConfig
{
    @Override
    public List<WadlGeneratorDescription> configure() {
        return generator(CustomJAXBWadlGeneratorWithRequiredSupport.class).descriptions();
    }    
}

With all the building blocks in palce, I wrapped it all up in a Guice module:


public class MyJerseyModule extends JerseyServletModule
{
@Override
protected void configureServlets()
{
// various bindings goes here

// furthermore, the module is served with the custom wadl generator
serve("/*").with(GuiceContainer.class, ImmutableMap.of("com.sun.jersey.config.property.WadlGeneratorConfig", CustomWadlGeneratorConfig.class.getCanonicalName()));
}
}

When requesting the wadl it now contains the resource:

...
<resource path="XYZ/{field}/{value}">
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" required="true" type="xs:string" style="template" name="field"/>
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" required="true" type="xs:string" style="template" name="value"/>
<method name="GET" id="getData">
<response>
<ns2:representation xmlns:ns2="http://wadl.dev.java.net/2009/02" xmlns="" mediaType="application/xml" element="Data"/>
</response>
</method>
</resource>
...

Hope you find this useful.

No comments:

Post a Comment