Thursday, May 21, 2009

Annotation-free services with CXF JAX-RS

One can hear now and then people saying : "It would it be good if I did not have to add so many annotations to a service class but well...". Many JSR specifications rely seriously on annotations and it does not seem like a reversible process really. Nearly every new enhancement leads to a new annotation type being introduced.

Annotations can be handy, they can save you a ton of time, they can help you with the injection of external resources, they can tell some great external library to do the job for you, they can be great after all. And they also can clutter your code and make it less readable or plain brittle.

Some annotations in Java-based web services frameworks enable Java classes to act as services, notably those defined in JAX-RS and JAX-WS. Here is a typical JAX-RS example :


@Path("/bookstore")
public class BookStore {

@GET
@Path("books/{id}")
@Produces("application/xml")
public Book getBook(@PathParam("id") Long id) {
return books.get(id);
}

@Path("books/{id}/chapter")
public Chapter getFirstChapterSub(
@PathParam("id") Long id) {
return books.get(id).getChapter(1);
}
}


Several annotation such as @Path, @GET, @PathParam and @Produces enable Book and Chapter classes to act as resource and subresource classes respectively.

The JAX-RS specification primarily talks about two main things : which annotations enable a given Java class to act as a service and how to ensure that the expectations expressed through these annotations are met, that is how to dispatch to a given method or populate a given parameter value, etc.

In CXF JAX-RS, we thought of letting users to write services without them having to annotate Java classes for a while and now we have done a first step in this direction, by following an example of the CXF SOAP simple frontend. One can apply an externally defined model instance to an existing resource tree, on either server or client side (proxies) and a CXF JAX-RS runtime will use this model instead of annotations.

One can apply such model in a number of ways.

One can create a number of UserResource beans, each representing either a root or sub-resource resource. For example, one can subclass a UserResource instance and populate it in its @PostConstruct method using some external information. A list of UserResource instances can then either be registered from Spring using a jaxrs:modelBeans child element or programmatically.

Or you can define a model instance either directly in a Spring beans.xml or in an external xml file and link to it, using either a jaxrs:model child element or jaxrs:modelRef attribute .

When using Spring, you often want to proxify a given service bean so we ensured you can use annotation-free proxified beans too.

All options are supported on the client and server sides, for jaxrs:client and jaxrs:server. In the client case it obviously applies to a proxy-based flavor only given that http-centric clients deal with the metadata (such as path, queries, etc) explicitly.

So lets see an example. Here are BookStoreNoAnnotations root resource and ChapterNoAnnotations sub-resource classes. Both classes actually deal with JAXB-annotated beans, ChapterNoAnnotations being one of them, but such beans have been likely generated out of schemas, but even if not, one will not have to go and start coding in JAX-RS annotations if it is what one does not want to do.

In one test we attach an externally defined model instance using this configuration :


<jaxrs:server
address='/thebooks6'
modelRef='classpath:/.../resources/resources.xml'/>

In other one we embed it directly inside jaxrs:server using a jaxrs:model, see this endpoint with id 'bookservice7'. In both cases an HTTP client code can invoke on either BookStoreNoAnnotations#getBook() or ChapterNoAnnotations#getItself(), by going through a BookStoreNoAnnotations#getBookChapter() subresource method first in the latter case.

In model one simply lists all the resource classes which may want to act either as root or subresource classes. The runtime will detect which one acts as a subresource one based on the response type of a given resource method, provided the model defines path but no HTTP verb values for this method.

The model is flexible enough to accommodate for most of the information which is otherwise available from JAX-RS annotations. It is really a combination of 3 simple beans, notably UserResource, UserOperation and Parameter, and they can grow as needed to keep up with the progress of JAX-RS.

Such model can be generated by a UI tool and simple enough to be created using an XML-editing tool.

Note the goal of this CXF extension is give to users more options when a task of enabling a given set of Java classes arises. If you are conscious about writing JAX-RS compliant code such that you can swap JAX-RS implementations under the hood and see your resource working then you will likely want to skip this option.

But then you might want to consider it when a cost of annotating a set of resource classes is deemed to be high. For example, embedding explicit path values using @Path annotations does seem quite brittle - path values and the way one embeds values into them or format requirements may change and now you are faced with the need to recompile. It may or may nor be an isolated and cheap update.

So please think about it, try it and provide the feedback.

By the way, this post can be viewed as a continuation of the previous one.
Actually, I think this approach can work quite nicely once CXF JAX-RS gets embedded in a Distributed OSGi implementation, with some DOSGi users being keen to keep the Java interfaces as transparent as possible.

Thursday, May 14, 2009

Standards in web services frameworks

CXF implements a number of specifications which have been standardized for users be able to write interoperable and indeed portable web service implementations in Java.

Interoperable web service implementations are those which can successfully communicate with web services written in other languages or with the help of alternative Java frameworks.

In the SOAP world it is specifications like WS-Security for example which attempt to describe for what is needed for an interoperable secure multi-hop SOAP conversation to go ahead.

In the REST world it can be the combination of specifications like HTTP, AtomPub, XMLSecurity.

Portable web service implementations are those which can be successfully run on alternative Java web services stacks without their code having to be changed.

JAX-WS and JAX-RS are really those kind of specifications which enable Java implementations to participate in web services interactions as opposed to specifications like WS-Security for example. These specifications make it possible to write portable Java web service implementations.

It is interesting in this regard to comment on the fact that apparently Spring does not implement JAX-RS for example. I think Spring fans simply dont mind - they do care about being able to plug in into the web services world with the help of whatever tools Spring offers to them. Such web service implementations wont be portable but so what given than Spring users are probably not even thinking of leaving Spring ? What matters most is the interoperability.

In addition to those web services standards, there is another kind of healthy standards conflict in CXF : how to enable a certain feature, should it be enabled using the core Spring support or using a CXF specific Spring extension for example.

Many users who understand and like Spring consider using the core Spring support being the only standard way. Some other users are just happy with CXF extensions. For example in CXF JAX-RS, one can use either Spring AOP or CXF specific custom invokers to intercept a method invocation.

The goal of this blog entry is to say is that it is what your favorite framework does can become a standard for you. It is about liking what it does. If you are a Spring fan and do RESTful web services with Spring REST (or whatever it is being called) then it is the standard that works for you. If you are a JAX-RS RI fan then probably you dont mind if some of its features have not been standardized.

In CXF we will think and work on popularizing its own CXF standards, with the main goal, that of helping users writing interoperable and effective web services, being the priority.

Wednesday, May 13, 2009

CXF JAX-RS XPath provider

I think writing the code like this is great :


WebClient wc = WebClient.create(endpointAddress);
Book b =
wc.get(XMLSource.class).get("/*/book[@name='Bar']",
Book.class);


But may be sometimes you may just want

WebClient wc = WebClient.create(endpointAddress);
Book b = wc.get(Book.class);


and still be able to get to the Book named 'Bar' which is part of a larger document.

So a simple XPathProvider reader has been added to CXF, you can use it on either sides by registering it programmatically or from Spring. You can register a unique provider per class by adding an expression and class name pair, or you can have a single provider applying a single expression in all cases or per-class specific expression.

Thursday, May 7, 2009

MVC the XML way with CXF JAX-RS

For some reasons, the 'V' bit in a MVC pattern is often associated by Java users with Java Server Pages (JSP). Personally I've never been a fan of JSP - I don't understand why would users want to mix Java code into HTML pages or pretend they don't do it by spending time on writing tag libraries in Java.

Fortunately, technologies like XSLT and well-known techniques for decoupling the presentation from content exist and in CXF we did a bit of work for users be able to refresh their XML skills and say goodbye to another legacy Java technology which is JSP and never look back.

If you work with CXF and do like JSP then please read on anyway - hope you will appreciate that XSLT (or technology like XQuery) can do the job too. As a side note, even if you won't want to use XSLT for generating HTML pages, in CXF you will be able to use it for generating all types of formats, doing micro-transformation routes by dealing with either legacy (backward-compatibilty) or newer (forward compatibilty) XML requests and responses. With XSLT you can pretty much just produce any format you need, starting from text and ending with RDF - you won't need RDF-specific libraries for it :-)

So we introduced an XSLTJaxbProvider. It's a very flexible provider. You can tell it to transform input or output data (on either client or server sides) . You can specify a stylesheet which will apply to all incoming data, or you can tell the provider to invoke a stylesheet a.xsl for application/xml and b.xsl for application/atom+feed formats. You can also tell it which Java classes are supported : for example, you may want JAXB to directly deal with Book class but you may also want to pre/post transform Book2 XML instances.

For a moment JAXP Templates are used to preprocess/compile XSLT templates and SAX events are used to drive in and out transformations. The base class, JAXBElementProvider does all the work and the XSLT provider only deals with the final marshal/unmarshal invocations by wrapping input or output streams as needed. For the record, JAXBElementProvider has been updated to deal with Stax XMLStreamReaders/Writers which gives yet another option of pre/post transforming the data and we will update the XSLT provider to pick them up, when they're available, which will essentially create 2-level transformation chains.

Template instances will get all the JAX-RS Path template variables, query and last past segment matrix name-value pairs as xsl:param parameters (note : you will only need to declare the parameters you need, in XSLT unused passed-in parameters will be ignored). Additionally UriInfo.getAbsolutePath(), UriInfo.getPath() and UriInfo.getBasePath() will be available as 'absolute.path', 'relative.path' and 'base.path' respectively. We'll update the provider as needed to push more useful information to templates.

If you don't use JAXB but deal with XML then please feel free just to create a custom provider. You'll just probably need to remove the 'extends JAXBElementProvider' bit from a class definition with few other minor updates.

I added a simple system test which shows how to merge the (JAXB-produced) XML into HTML. It's well-known technique, described in articles like

Style-free XSLT stylesheets
Integrating data at runtime with XSLT stylesheets

The idea is not to go the traditional JSP way by combining XSLT instructions and presentation tags into a single source which makes the source unreadable and difficult to maintain. Rather let people who understand how to do proper HTML work on HTML templates, Java developers produce Java code (possibly dealing with either Java classes for JAXB to handle or JAXP Sources ) and those who like XSLT write XSLT templates which sources both presentation HTML and content XML inputs and transforms them as needed. HTML templates have special xml tags which XSLT templates replace with actual data. Trust me, this seperation of roles does work in practice quite well, I can vouch for it based on my previous experience, though I guess we didnt do the complicated stuff - but it worked nonetheless.

Now lets get back to the test. Look at the jaxrs endpoint with 'bookservice5' id. It registers an XSLTJaxbProvider which is told to handle Book classes only and invoke a template.xsl for application/xhtml+xml and template2.xsl for application/xml. (For the record, this endpoint also registers CXF JAX-RS Request/Response filters which install custom Stax stream readers/writers which perform namespace translations - as advised by one of CXF users recently). The provider is also injected with URIResolverImpl, a systemId property is also supported.

The template.xsl template sources in an xhtml template book.xhtml and imports template2.xsl. Thus template2.xsl can be used on its own too. A URIResolver instance registered earlier on takes care of resolving those relative references. Now, the book.xhtml has a special tag books:bookTag which is what a template.xsl reacts upon : the flow is to copy the html tags and pull in the xml content as needed when special tags get encountered. Note that fundamental XSLT style is to do a free flow and rely on matches which makes the templates code much more readable and easier to understand.

In our test we simply copy the whole XML content blob into the resulting HTML, by preprocessing a Book instance as needed (see the imported template2.xsl). Perhaps it would be better to add special tags to book.xhtml corresponding to Book id and Book name, with HTML table tags if needed.

The server test code is in BookStoreSpring.getBookXSLT(). Note that JAX-RS Path, Query and Matrix parameter values are available to template2.xsl, when used on its own or when being imported.

See testGetBookXSLTXml() and testGetBookXSLTHtml() here. Here is the one which extract the Book instance from an HTML document :




WebClient wc = WebClient.create(endpointAddress);
wc.accept("application/xhtml+xml")
.path(666)
.matrix("name2", 2)
.query("name", "Action - ");
XMLSource source = wc.get(XMLSource.class);
Map namespaces = new HashMap();
namespaces.put("xhtml", "http://www.w3.org/1999/xhtml");
Book2 b = source.getNode(
"xhtml:html/xhtml:body/xhtml:ul/xhtml:Book",
namespaces,
Book2.class);


In reality you wont see a Book element in an xhtml namespace though :-)

So it is the XML all the way. I believe it's great fun to be able easily and explicitly control how the XML looks like and to mix a much looser XPath style with the Java code. So give it a try and have fun.

Update. I mentioned above that XSLTJaxbProvider can choose which template to use depending on the media type, such as application/atom+feed for example. Note that even though this provider has static Produces and Consumes values, you can still tell to handle other media types for the runtime to view it as an eligible provider, look at the bean with 'jsonProvider' id on how to overwrite the static Produces and Consumes values on a given message body provider. So for example you can easily tell XSLTJaxbProvider to actually convert a given JAXB-produced XML into JSON by registering an application/json specific template (which will transform XML into JSON) with it and registering application/json as a custom Produces value.