Wednesday, January 30, 2013

As of today, Apache CXF is well integrated into
JBoss AS 7 and working properly through the JBossWS layer. The
application server satisfies the JavaEE 6 requirements (of course
including the WS related ones) and most of the Apache CXF specific
funtionalities are continuously tested by the JBossWS own testsuite
and covered in the application server webservices documentation .

While we do have plans for future new features and
contributions, even directly on Apache CXF, we still have very few
action items to work on in the pure JBoss AS integration area. Last week
I dealt with one of them, which turned out to be quite interesting from
a technical point of view, so I thought it's worth to share it here.

It is well known that JBoss AS 7 is built on top of
the modules' concept and comes with a fully modular classloader defined by module dependencies and really allowing controlling which
classes to load, etc. As part of the move to the new classloading
mechanism, we created modules for most of the Java EE apis that we
include in the application server, JAX-WS api being one of them. Our own JAX-WS api
module comes with a slightly modified version of the vanilla jaxws 2.2
apis, which resolves the javax.xml.ws.spi.Provider into the
corresponding JBossWS implementation (defaults to it, unless different
configuration is specified using the Service API mechanism). The jaxws
api module is then used to define the classloader for any application in
the application server, so that whenever JAXWS is required its classes are used and the
JBossWS (Apache CXF based) implementation is automatically used under
the hood. Besides for the fact that we're easily enforcing usage of our
JAX-WS impl this way, the real benefit of such an approach is that we
always control the JAX-WS api level (currently 2.2) in use.

As a matter of fact, a mismatch in JAXWS api version
is usually a common source of major final user headaches; the reason
for that is basically in JDK 6 (well, actually starting from update 4)
coming with JAXWS 2.1 api included. Unless the bootclasspath is properly
modified by setting the java.endorsed.dirs env to a directory containing the jaxws 2.2 api jar when starting the virtual machine, 2.1 api classes
from the JDK will always be used by default whenever making use of
JAX-WS apis. Users will eventually forget about that, make mistakes in
setting that, get confused with tools / IDE configuration of that,
etc... and not understand what's really happening and why they're
getting bunch of errors.

JBoss AS 7 relies on JBoss Modules for modular
classloading and does not require any java.endorsed.dirs setup when
booting the AS. So far so good :-)

JBossWS (as well as Apache
CXF) however provides JAX-WS tools for wsdl-to-java and java-to-wsdl
generation; each of them comes with wsconsume.sh/bat and wsprovide.sh/bat
scripts to execute on command line. The tools internally rely on Apache
CXF corresponding tools, which generate code and compile it. In order
for having CXF compile jaxws 2.2 compliant code, we used to need setting
the java.endorsed.dirs env in the wsconsume and wsprovide scripts,
failing that the code couldn't simply be compiled, because CXF runs
javac which would of course get the JAXWS api directly from the JDK.

So, a jira has been created for fixing this, as
we do not want to set the java.endorsed.dirs anymore, even for ws tools.
How to solve this then?

Fortunately, Apache CXF tools
recently moved to direct usage of Java Compiler API to compile sources,
instead of forking a process for running javac (forking is still
possible, yet not the default behaviour). Starting from JDK 6, the Java
Compiler API (JSR-199) offers a powerfull (yet quite complicated) way of
creating a compilation task and supplying source files to it for being
compiled. Below is an excerpt of the CXF code making use of JSR-199:

The javax.tools.JavaFileManager interface allows
for controlling loading of any class involved in the compilation task;
it's possible to define and install a custom ForwardingJavaFileManager to
override the methods that actually fetch and provide the bytecode for
any class.

So, what we ended up in doing here was to define an extension of ForwardingJavaFileManager that would intercept requests for
the JAXWS (and JAXB...) api classes, load the corresponding ones from our
JBoss Module classloader, read their bytecode and create proper
JavaFileObject instances to return to the JDK compiler. It took a while
to write a decent JavaFileManager implementation (also considering I didn't want
any JBoss Module specific code in it in JBossWS), but it finally worked and allowed
compiling jaxws 2.2 code with JDK6 and no endorsing configuration.

Then, the usual JBossWS open source integration
process started, as I had to figure out how to extend Apache CXF in a
general way so that I could made my JavaFileManager available deep into the CXF
tools code. Once again, the Apache CXF community proved to be really
open to extensions and contributions even for really specific stuff like
this and the Apache CXF project lead even figured out possible
different scenarios that would benefit from my extension. So a CXF jira was created, we discussed design a bit on IRC and I committed my changes on
Apache CXF and JBossWS.

A new Apache CXF release is coming out very soon and hopefully by
the end of next week I'll be able to include it in JBoss AS upstream and
fix the wsconsume/wsprovide command line scripts, cool!