You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 14 Next »

Unknown macro: {span}

JAX-RS Services Description

CXF JAX-RS supports (Web Application Description Language|http://www.w3.org/Submission/wadl] (WADL).
Users can use WADL documents to generate the initial code and have WADL auto-generated on demand.

WADL overview

WADL is a resource-centric description language which has been designed to facilitate the modeling, description and testing of
RESTful Web applications. Please check the official page for more information, this section provides a brief overview of main WADL constructs.

Basic example

A top level WADL document element is called "application". Usually it may contain a "grammars" section and "resources" element with one or more top-level "resource" elements, with each one representing a specific root resource, for example:

<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:ns="http://superbooks">
 <grammars>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
        xmlns:tns="http://superbooks" attributeFormDefault="unqualified" elementFormDefault="unqualified" 
        targetNamespace="http://superbooks">
    <xs:element name="thebook" type="tns:book"/>
    <xs:complexType name="book">
        <xs:sequence>
            <xs:element minOccurs="0" ref="tns:thechapter"/>
            <xs:element name="id" type="xs:int"/>
        </xs:sequence>
    </xs:complexType>
  </xs:schema>
 </grammars>
 <resources base="http://localhost:8080/">
   <resource path="/bookstore/{id}">
     <param name="id" style="template"/>
     <method name="GET">
      <response>
       <representation mediaType="application/xml" element="ns:thebook"/>
      </response>
    </method>
   </resource>
   <resource path="/books">
      <resource path="/bookstore/{id}">
        <param name="id" style="template"/>
        <method name="GET">
          <response>
           <representation mediaType="application/xml" element="ns:thebook"/>
          </response>
        </method>
      </resource>
   </resource>
 </resources>  
</application>

This document describes an application that has "http://localhost:8080/" base URI. It can handle GET requests such as
"http://localhost:8080/bookstore/1", "http://localhost:8080/bookstore/123", etc. Additionally it can handle similar GET requests at
"http://localhost:8080/books/bookstore/1", "http://localhost:8080/books/bookstore/123", etc, note an extra "books" path segment.

"application/xml" media type is supported and response representation elements link to "{http://superbooks}thebook" element declared in a schema inlined in the grammars section.

Note that "resources" element has two child "resource" elements, one with "/bookstore/{id}" path, another one with "/books" path.

These 2 resources can be represented as JAX-RS root resources. For example, these resources can be mapped to concrete Java classes such as BookStoreRootResource with @Path("/bookstore/{id}") and BooksResource with @Path("/books"). BookStoreRootResource root resource will have a single @GET resource method with no @Path, presumably returning Book (JAXB) bean. The second BooksResource root resource will have a single subresource locator with @Path("/bookstore/{id}") which will return a subresource with a single @GET resource method.

This is just one possible interpretation of how the above WADL description can be mapped to JAX-RS resources and methods.

Also note that the resource with the "/books" path has another child resource with the "/bookstore/{id}" path, but it could've had a "/books/bookstore/{id}" path instead and no child resource.

WADL with references

Basic WADL example in the previous section shows a "grammars" section with the inlined schema, as well as a "resource" description with the "/bookstore/{id}" path listed twice, as an immediate child of the "resources" and as a child of the "resource" element with the "/books" path.

Note that inlined schemas can be included instead by referencing external schemas. Likewise, most of WADL element declarations such as "resource", "method", "representation", etc can be shared by using the same document or external references. Here is how the basic example can be simplified with the help of references:

<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:ns="http://superbooks">
 <grammars>
   <include href="schemas/book.xsd"/>
 </grammars>

 <resource_type id="bookResource">
     <param name="id" style="template"/>
     <method name="GET">
       <response>
        <representation mediaType="application/xml" element="ns:thebook"/>
       </response>
    </method>
 </resource_type>

 <resources base="http://localhost:8080/">
   <resource path="/bookstore/{id}" type="#bookResource"/>
   <resource path="/books">
      <resource path="/bookstore/{id}" type="#bookResource"/>
   </resource> 
 </resources>  
</application>

Note that a book.xsd schema resource located in the 'schemas' path relative to the location of this WADL document is referenced using wadl:include element. Abstract resource type "bookResource" is declared as an immediate child of wadl:application and is linked to concrete resource elements using a "#bookResource" reference.

Sharing declarations between multiple WADLs

WADL references allow for having WADL documents with abstract declarations only and concrete WADLs referencing them, thus making it possible to reuse resource declarations in different web application descriptions.

For example, the following baseApplication.wadl documents describes an abstract "bookResource" resource:

<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:ns="http://superbooks">
 <grammars>
   <include href="schemas/book.xsd"/>
 </grammars>

 <resource_type id="bookResource">
     <param name="id" style="template"/>
     <method name="GET">
       <response>
        <representation mediaType="application/xml" element="ns:thebook"/>
       </response>
    </method>
 </resource_type>
</application>

and this WADL document links to the abstract resource by using an external WADL reference with a "baseResource" fragment.

<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:ns="http://superbooks">
 
 <resources base="http://localhost:8080/">
   <resource path="/bookstore/{id}" type="baseApplication.wadl#bookResource"/>
   <resource path="/books">
      <resource path="/bookstore/{id}" type="baseApplication.wadl#bookResource"/>
   </resource> 
 </resources>  
</application>

WADL-first Development

CXF 2.4.1 introduces a wadltojava code generator and cxf-wadl2java-plugin Maven plugin which can be used to generate server and client JAX-RS code and speed up the transition between modeling and implementation stages.

wadl2java command line tool

Running wadltojava from the command line will produce:


The options are reviewed in the following table.

Option

Interpretation

-?,-h,-help

Displays the online help for this utility and exits.

-fe frontend-name

Specifies the frontend.

wadl2java Maven plugin

If you need the code generated during the Maven build then the following plugin can be used:

<groupId>org.apache.cxf</groupId>
<artifactId>cxf-wadl2java-plugin</artifactId>
<version>2.4.1</version>

Add this plugin to the build section of your project's pom and specify a 'wadl2java' goal, for example:

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-wadl2java-plugin</artifactId>
                <version>2.4.1</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot>
                            <wadlOptions>
                                <wadlOption>
                                    <wadl>${basedir}/src/test/resources/wadl/bookstoreImport.xml</wadl>
                                    <impl>true</impl>
                                    
                                    <packagename>org.apache.cxf.systest.jaxrs.codegen.service</packagename>
                                    <schemaPackagenames>
                                       <schemaPackagename>http://superbooks=org.apache.cxf.systest.jaxrs.codegen.schema</schemaPackagename>
                                    </schemaPackagenames>
                                    
                                </wadlOption>
                            </wadlOptions>
                        </configuration>
                        <goals>
                            <goal>wadl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

Integration

External WADL documents and JAXRS endpoints.

WADL Generation

CXF JAX-RS supports the auto-generation of WADL for JAX-RS endpoints.
Note that JAX-RS subresources are supposed to be late-resolved, so using annotated interfaces for subresources and a staticSubresourceResolution=true property will let the whole resource tree/graph be described in a generated instance. Schemas will be generated for JAXB-annotated types.

WADL instances for RESTful endpoints are available from {base endpointaddress}/services, in addition to SOAP endpoints if any. Note that you can override the location at which listings are provided (in case you would like '/services' be available to your resources) using
'service-list-path' servlet parameter, ex :

> 'service-list-path' = '/listings'

Going to the service listings page is not the only way to see the wadl instances, generally one can get it using a ?_wadl query.

For example, given

Base address : 'http://localhost:8080'
WAR name : 'store'
CXFServlet : '/books/*'
jaxrs:server/@address = '/orders'
jaxrs:server/@staticSubresourceResoulution = 'true'

and 2 root resource classes registered with this endpoint, say

@Path("/fiction") 
public class FictionBookOrders {
}
@Path("/sport") 
public class SportBookOrders {
}

then

> http://localhost:8080/store/books/orders?_wadl

will give you the description of all the root resource classes registered
with a given jaxrs:server endpoint, including all the subresources. While

> http://localhost:8080/store/books/orders/fiction?_wadl
> http://localhost:8080/store/books/orders/sport?_wadl

will give you all the info for FictionBookOrders and SportBookOrders respectively.

If you have many jaxrs:endpoints then visiting

> http://localhost:8080/store/books
> http://localhost:8080/store/books/services

will let you see all the WADL links.

Note that the media type for a ?_wadl response is set to 'application/vnd.sun.wadl+xml' which is something Firefox does not really
like unless some wadl plugin is registered. If an HTTP Accept header is set to 'application/xml' then Firefox will show it with no problems. Doing
'?_wadl&_type=xml' will ensure a WADL generator will see Accept being set set to 'application/xml'.

Documenting resource classes and methods in WADL

CXF 2.4.0: org.apache.cxf.jaxrs.ext.Description and org.apache.cxf.jaxrs.ext.xml.XMLName have been moved to org.apache.cxf.jaxrs.model.wadl package given that their purpose is to improve the WADL generation. Also, org.apache.cxf.jaxrs.model.wadl.WadlElement has been renamed to 'ElementClass'.

WADL documents can include doc fragments.

Users may want to use Description annotations which can be attached to resource classes and methods.

Note that starting from CXF 2.4.0, Description annotations can be applied to input parameters. Additionally, a method-level Descriptions annotation can have a collection of categorized Description annotations, with each Description targeting a specific WADL element by setting its 'target' property to one of the DocTarget values. For example, one can use a Descriptions annotation to document the response representation of a particular resource method, as well as add documentation fragments to WADL wadl:method/wadl:request and wadl:method/wadl:response elements:

@POST
@Path("books/{bookid}")
@Descriptions({ 
   @Description(value = "Adds a new book", target = DocTarget.METHOD),
   @Description(value = "Requested Book", target = DocTarget.RETURN),
   @Description(value = "Request", target = DocTarget.REQUEST),
   @Description(value = "Response", target = DocTarget.RESPONSE),
   @Description(value = "Resource", target = DocTarget.RESOURCE)
})
public Book addBook(@Description("book id") @PathParam("id") Long id, @Description("New Book") Book book) {...}

Every unique @Path value adds a new 'resource' element to the generated WADL, thus the last Description annotation in the @Descriptions array ensures the doc extension is also added to the 'resource' element. Note that multiple resource methods having different HTTP methods but sharing the same @Path value will have the same parent 'resource' element representing this shared path fragment, in this case a Description with the DocTarget.RESOURCE target will be ignored unless it is added to the first resource method with this shared @Path:

@POST
@Path("books/{bookid}")
@Description(value = "Resource", target = DocTarget.RESOURCE),
public Book addBook(@Description("book id") @PathParam("id") Long id, @Description("New Book") Book book) {...}

@GET
@Path("books/{bookid}")
public Book addBook(@Description("book id") @PathParam("id") Long id) {...}

Custom WADL providers

One can register a custom WADLGenerator as a jaxrs:provider. The custom generator can extend the default
org.apache.cxf.jaxrs.model.wadl.WADLGenerator or register a default one with one of the following properties set.

  • wadlNamespace: default is "http://wadl.dev.java.net/2009/02", the earlier one is "http://research.sun.com/wadl/2006/10".
  • singleResourceMultipleMethods: default is 'true', for example, if a resource class has multiple methods supported at the same path such as "/" (GET, POST, etc) then WADL will list them all as the child nodes of a single resource element.
  • useSingleSlashResource: default is false, for example, if you have a root resource class with a path "root" and a resource method with a path "" or "/" then a WADL resource representing the root will not have a child resource representing this resource method (it would do if a resource method had a more specific path such as "bar").

Starting from CXF 2.4.1 and 2.3.5 the following properties are also supported:

  • applicationTitle: can be used to create an application title.
  • namespacePrefix: defaut is 'prefix', it can be set to other value such as 'ns'.
  • ignoreForwardSlash: can be used to enforce that resource path values do not start from '/'

Representing external schemas and non JAXB types

By default, the WADL grammar section will be properly generated if resource methods accept or return JAXB types.

Even when you do use JAXB, the JAXB types may have been generated from the external schema so having WADLGenerator attempting to recreate the original schema may not work well. To have a generated WADL referencing the original schema(s) please set a 'schemaLocations' list property (programmatically or from Spring) :

WadlGenerator wg = new WadlGenerator();
wg.setSchemaLocations(Collections.singletonList("classpath:/book.xsd"));

In this case the grammar section will have the 'book.xsd' schema inlined. If this schema imports other schemas then the imports with relative URIs will be replaced by the absolute URIs based on the current endpoint's base address. For example, if the endpoint address is "http://somehost/bar" and the 'book.xsd' imports "foo/book1.xsd" then the published WADL will contain an "http://somehost/bar/foo/book1.xsd". At the moment a custom RequestHandler filter will have to be registered to serve resources such as "http://somehost/bar/foo/book1.xsd" which can 'calculate' which resource is required get the absolute request URI and comparing it with the base URI, possibly with the help of the injected JAXRS UriInfo context. Alternatively, resources such as book1.xsd may be served by CXFServlet itself (see the Redirection with CXFServlet)

TODO : add ignoreImports flag so that users can list root and imported schemas in "schemaLocations" and have them all inlined.

Note that the root schema such as "book.xsd" is inlined - you can have it referenced only by setting an 'externalLinks' list property. This will work very well when the "book.xsd" is indeed available at the external URI, but this property can also be used to avoid the local schemas being inlined. Moreover, the use of JAXB will not be required. The result will look like this:

<wadl:grammars>
<wadl:include href="http://books.xsd"/>
</wadl:grammars>

Note that "schemaLocations" and "externalLinks" properties differ in that the schemas referenced by the former one are inlined.

You can also customize the way schema elements are referenced. When WADLGenerator creates WADL representation elements (representing resource method input or output types) it will be able to link to schema elements provided a given type is actually a JAXB one, so the result may look like this :

<!-- 
  thebook2 element is declared in a schema inlined in or referenced from the grammar section
  prefix1 is bound to that schema's target namespace and is declared at the wadl:application element 
-->
<representation mediaType="application/xml" element="prefix1:thebook2"/>

If no JAXB is used then you can attach an XMLName annotation to method input or output types. Alternatively, you can register an instance of ElementQNameResolver with the WADLGenerator which will be used for creating wadl:representation/@element values.

An ElementClass annotation can help with representing JAX-RS Response elements in the generated WADL.

Generating the client code from WADL at runtime

If you register an org.apache.cxf.jaxrs.ext.codegen.CodeGeneratorProvider with a jaxrs endpoint and issue a '_code' query to it then you will get back an XHTML page containing the link to a zipped client source code which you can download and start customizing.

Test/development servers can host such a provider to help implementers get started with working on the client code asap.

Internally, before the code gets generated, WADL will be generated first. The archive will include JAXB generated classes from a WADL grammar section plus the proxy based client code for accessing root and sub resources. The WebClient based code can not be generated just yet but one can request that only a WADL grammar section is processed by adding a '_codeType=grammar' query and easily adding a WebClient-based code.

Here is how to get the archive programmatically :

WebClient wc = WebClient.create("http://localhost:9080/petstore");
XMLSource source = wc.query("_code").query("_os", getOs()).get(XMLSource.class);
String link = source.getValue("ns:html/ns:body/ns:ul/ns:a/@href",  
              Collections.singletonMap("ns","http://www.w3.org/1999/xhtml"));
// download a zip file          
WebClient wcZip = WebClient.create(link);
InputStream is = wcZip.accept("application/zip").get(InputStream.class);
// unzip and compile it

Please see a testPetStore test for more details.

Hiding links to JAXRS endpoints from the services page

In some cases you may not want the users to see the links to some JAXRS endpoints. For example, if you have an AtomPullServer endpoint collecting the log entries for a number of application endpoints, you may not want to see AtomPullServer endpoint being listed among the endpoints representing the actual application and which users are actually interested in. If so then adding an "org.apache.cxf.endpoint.private" boolean property with a value "true" will do the trick; note the same property can be used by jaxws endpoints.

  • No labels