Wednesday, March 10, 2010

On the way to WADL-first development in CXF JAXRS

Yesterday I added a CXF JAXRS request filter capable of generating the Java code for consuming a given JAXRS resource on demand. I was about to finish it two weeks ago but had to postpone it a bit. Here's a test code fragment showing how to get a zip containing the source :


// Get the link to the source
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 testPetStoreSource test for more details. The _os query is optional, it is there to ensure you can get OS-specific line separators added to some of the generated classes (ex, the server is running on Windows, you'd like to run a test client on Unix). You can also add a _language query but it is only Java which is supported for now.

What is interesting is that a newly introduced CodeGeneratorProvider generates the source from a WADL instance which another provider, WADLGenerator, creates after introspecting a given JAXRS resource. The zipped source will contain the JAXB-generated classes from a WADL grammar section (if any) and the proxies (interfaces) representing WADL resources, including the subresources, with the resource methods linking to corresponding JAXB classes.

You can request that classes be generated from a WADL grammar section only by adding a _codeType=grammar query. Two other possible values here are 'proxy' and 'web', the former value is a default one at the moment with the latter one being not supported yet. I need to think a bit more about what sort of code can be generated for a _codeType=web. Suppose we have a resource named BookStore. What is the difference between a method called getBook (in case of proxies) and a method called get(in case of web clients) ? I'm also not sure yet what to do with cases where WADL resource contains multiple methods like GET but with different mediaType values, need a bit more time. In meantime if the word 'proxy' makes you nervous :-) then please just use _codeType=grammar to download the classes representing schema elements and proceed from there by using your JAXRS runtime's web client api.

How does CodeGeneratorProvider generate the proxies given that WADL does not provide for it ? Internally, it requests a WADLGenerator to add 'id' attributes to WADL resource elements representing root or subresource resources and to WADL resource/method elements. Example, wadl:resource/@id="{org.apache.cxf.test.RootResource}" and wadl:resource/wadl:method/@id="getBook".

This is really all what 'facilitates' the WADL to Java code generation which is exactly what CodeGeneratorProvider does. In fact, I haven't actually even thought originally about introducing such a provider but then I thought that a future command-line codegen tool allowing for a server-side code be generated from a WADL document will do exactly what CodeGeneratorProvider does, albeit it will generate the resource class skeletons as opposed to proxies. In fact, given that CodeGeneratorProvider is a regular CXF JAXRS provider, you can set a false 'generateInterfaces' property on it and you'll get a *server-side* code downloaded, perhaps you might want to use it to write your own added-value service interposing the original one.

CXF 2.2.7 will be out soon so please experiment with this new feature.

I'll need to think a bit more about the format for resource ids, perhaps it can be just "BookStore" but a future codegen tool can ask users to provide a package name. We can have JAXB classes acting as subresources that is why I've introduced an extended QName format initially. At the moment WADL does not allow internal method elements to contain ids (as opposed to global ones), hope this restriction can be lifted, alternatively it would be easy for a (UI) tool allowing users to create WADL documents to ensure resource methods are declared globally and then linked to from internal resources.

Seems like using resource and method ids for the purpose of allowing WADL-first development might make sense, it will preserve the simplicity of WADL the language.

Finally, why worry about the WADL-first development ? It is simple : IMHO it might help in making the RESTful approach toward developing web services being more attractive to many developers who prefer starting with building a picture of the future application first. So imagine your UI tool showing a nice view of how resources relate/link to each other and what formats and HTTP methods they support and schemas they're aware of, would not it be cool to be able to press a button next and have most of the code being generated for you ?

No comments: