Jersey 1.0.3 improves WADL support

Jersey 1.0.3 was released this week. This release has quite a few interesting new features and improvements to some of its existing functionality. Paul’s blog entry gives the specifics of this release. One of the cool features in this release is the ability to inject WadlApplicationContext in resources. This provides access to the WADL JAXB representation for your restful application. Paul already discussed about its usage in the Jersey dev mailing list. I wanted to see all these in action and you won’t believe that it took just few minutes to see styled WADL documentation in action.

The following URI paths renders XSL styled WADL documentation for resources :

http://localhost:9998/application.wadl

http://localhost:9998/wadl

Here is a preview of the documentation (This works in Chrome out of the box, no luck with Firefox and IE):

Jersey WADL documentation

I used Mark’s WADL to HTML documentation stylesheet for styling the documentation.

The HTTP response for WADL GET recorded by Firebug:

HTTP Response for WADL GET

Here is the POM for interested:

    
        
            javax.xml.bind
            jaxb-api
            2.1
        

        
            com.sun.xml.bind
            jaxb-impl
            2.1.10
        

        
            com.sun.jersey
            jersey-server
            1.0.3
        

        
            com.sun.jersey.test.framework
            jersey-test-framework
            1.0.3
            test
        

The WadlResource below shows how the WadlApplicationContext injection happens and the JAXBContext is retrieved from WadlApplicationContext. The marshaller allows you to set the property which can be used to specify an XML preamble, in this case WADL to HTML documentation stylesheet embedded in the XML headers. The rest all is the usual stuff you know.

@Produces({"application/vnd.sun.wadl+xml", "application/xml"})
@Singleton
@Path("wadl")
public class WadlResource {

    private static final Logger LOGGER = Logger.getLogger(WadlResource.class.getName());

    private static final String XML_HEADERS = "com.sun.xml.bind.xmlHeaders";

    private WadlApplicationContext wadlContext;

    private Application application;

    private byte[] wadlXmlRepresentation;

    public WadlResource(@Context WadlApplicationContext wadlContext) {
        this.wadlContext = wadlContext;
        this.application = wadlContext.getApplication();
    }

    @GET
    public synchronized Response getWadl(@Context UriInfo uriInfo) {
        if (wadlXmlRepresentation == null) {
            if (application.getResources().getBase() == null) {
                application.getResources().setBase(uriInfo.getBaseUri().toString());
            }
            try {
                final Marshaller marshaller = wadlContext.getJAXBContext().createMarshaller();
                marshaller.setProperty(XML_HEADERS, "");
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                final ByteArrayOutputStream os = new ByteArrayOutputStream();
                marshaller.marshal(application, os);
                wadlXmlRepresentation = os.toByteArray();
                os.close();
            } catch (Exception e) {
                LOGGER.log(Level.WARNING, "Could not marshal wadl Application.", e);
                return javax.ws.rs.core.Response.ok(application).build();
            }
        }
        return Response.ok(new ByteArrayInputStream(wadlXmlRepresentation)).build();
    }
}

MyWadlResource extends from WadlResource with the relative URL at “application.wadl”. There is already a patch submitted by James Strachan which will allow using WADL with implicit views. I hope this becomes available for the next 1.1.0-ea release.

@Path("application.wadl")
public class MyWadlResource extends WadlResource {
  public MyWadlResource(@Context WadlApplicationContext wadlContext) {
    super(wadlContext);
  }
}

The usual standalone Server code is shown below.

public class Server {
    public static void main(String[] args) throws IOException {
        final String baseUri = "http://localhost:9998/";
        final Map initParams = new HashMap();
        initParams.put("com.sun.jersey.config.property.packages", "jersey.wadl.resources");
        System.out.println("Starting grizzly...");
        SelectorThread threadSelector = GrizzlyWebContainerFactory.create(baseUri, initParams);
        System.out.println(String.format("Jersey app started with WADL available at %sapplication.wadl or %swadln"
                + "Try out %snHit enter to stop it...", baseUri, baseUri, baseUri));
        System.in.read();
        threadSelector.stopEndpoint();
        System.exit(0);
    }
}

Here is a simple test written using the new Jersey test framework and by default it deploys the resources to the Grizzly container. The Application class is serialized as a JAXB bean. You then have access to all the WADL components.

public class WadlTest extends JerseyTest {

  public WadlTest() throws Exception {
    super("jersey.wadl.resources");
  }

  @Test
  public void wadlGet() {
    ClientResponse cr = webResource.path("wadl").get(ClientResponse.class);
    Application a = cr.getEntity(com.sun.research.ws.wadl.Application.class);
    Method m = (Method)a.getResources().getResource().get(0).getMethodOrResource().get(0);
    Response r = m.getResponse();
    List<JAXBElement> representations = r.getRepresentationOrFault();
    for (JAXBElement representation : representations) {
      RepresentationType type = representation.getValue();
      System.out.println("MediaType : " + type.getMediaType());
    }
  }
}

The other notable support in this release is integration with Guice 2.0. Maturity and quality is always the driving factor behind every Jersey release. No compromises whatsoever. Well done Paul and team!

The project sources can be downloaded from here.

8 thoughts on “Jersey 1.0.3 improves WADL support

  1. Hello:

    Thanks for posting this! I’m unable to run the project from within NetBeans, getting the following error:

    “Running jersey.wadl.client.WadlTest
    May 3, 2009 9:01:30 PM com.sun.jersey.test.framework.impl.container.grizzly.web.GrizzlyWebContainer start
    INFO: Starting grizzly…
    May 3, 2009 9:01:30 PM com.sun.grizzly.http.servlet.ServletContextImpl initListeners
    WARNING: Unable to load listener: null”

    I’m using 1.9.14 version of grizzly, but not having any prior experience with grizzly, I’m not sure what I’m missing.

    Also, the POM.xml is different from the one shown in the post, declaring some of the libraries as test scope only.

    Thank you, again.

    Like

  2. I switched the TCP port to 8090. I should have noticed the 9998 right away. Thanks again. This is a nice example!

    Like

  3. Hi, I am trying to make use of the WebResource class you described in this article. However, my web service makes use of Spring Security as well as Jersey. I am using Jersey v1.0.3 and Spring Security v2.0.4. The WebResource class constructor seems to be called with a null wadlContext. I added the following annotation to the WebResource class to integrate it with Spring:

    @Component
    @Scope(“request”)

    A snippet of my web.xml:

    org.springframework.web.context.ContextLoaderListener

    org.springframework.web.context.request.RequestContextListener

    SmartInspector Web Application
    com.sun.jersey.spi.spring.container.servlet.SpringServlet

    com.sun.jersey.config.property.packages
    com.teamphone.rest.resources;jersey.wadl.resources

    1

    SmartInspector Web Application
    /*

    springSecurityFilterChain
    org.springframework.web.filter.DelegatingFilterProxy

    springSecurityFilterChain
    /*

    Is there something that I have missed?
    Thanks in advance.

    Like

  4. My web.xml paste seems to have had its angle brackets stripped out. I am re-pasting by replacing these with square brackets.

    A snippet of my web.xml:

    [listener]
    [listener-class]org.springframework.web.context.ContextLoaderListener[/listener-class]
    [/listener]
    [listener]
    [listener-class]org.springframework.web.context.request.RequestContextListener[/listener-class]
    [/listener]

    [servlet]
    [servlet-name]SmartInspector Web Application[/servlet-name]
    [servlet-class]com.sun.jersey.spi.spring.container.servlet.SpringServlet[/servlet-class]
    [init-param]
    [param-name]com.sun.jersey.config.property.packages[/param-name]
    [param-value]com.teamphone.rest.resources;jersey.wadl.resources[/param-value]
    [/init-param]
    [load-on-startup]1[/load-on-startup]
    [/servlet]

    [servlet-mapping]
    [servlet-name]SmartInspector Web Application[/servlet-name]
    [url-pattern]/*[/url-pattern]
    [/servlet-mapping]

    [filter]
    [filter-name]springSecurityFilterChain[/filter-name]
    [filter-class]org.springframework.web.filter.DelegatingFilterProxy[/filter-class]
    [/filter]

    [filter-mapping]
    [filter-name]springSecurityFilterChain[/filter-name]
    [url-pattern]/*[/url-pattern]
    [/filter-mapping]

    Like

  5. Hi Arul,

    I posted this as a question on the Jersey users mailing list and got a reply from Jakub Podlesak. It seems that the Glassfish update center is having some technical difficulties at the moment as they have not yet release the Jersey v1.0.3 Jars for Glassfish on there yet. However, Jakub kindly replied with some instructions on how to manually update my Glassfish installation. The wadlContext is no longer null as the resulting WADL is generated as you described above.

    Thanks.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s