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
Here is a preview of the documentation (This works in Chrome out of the box, no luck with Firefox and IE):
I used Mark’s WADL to HTML documentation stylesheet for styling the documentation.
The HTTP response for WADL GET recorded by Firebug:
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.
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.
LikeLike
I switched the TCP port to 8090. I should have noticed the 9998 right away. Thanks again. This is a nice example!
LikeLike
Hi Dave,
I am glad to hear you resolved the problem.
-Arul
LikeLike
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.
LikeLike
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]
LikeLike
Hi Anjum,
I believe wadlContext is not getting injected into your resource. Did you use Grizzly container or a different one?
-Arul
LikeLike
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.
LikeLike
Awesome. I am glad Jakub’s workaround resolved the problem for you.
-Arul
LikeLike