Hi, Mike.
Thank you for sharing your experience to bring up the binding.ws with OSGi.
Please see my comments below.
Raymond
--------------------------------------------------
From: "Mike Edwards" <[email protected]>
Sent: Thursday, December 18, 2008 7:12 AM
To: <[email protected]>
Subject: [2.0] Getting Web Services binding running - some entertainment
Folks,
After rather more work than I would have hoped for, I have finally got Web
Services running on the 2.0 codebase.
There are interesting challenges, some of which have solutions which are
not (yet) in a form which can be adopted generally in the codebase. I'll
try to describe them here, step by step.
Most of the challenges turn out to be classloading issues - unsurprising
given the move to OSGi in 2.0. Some of the problems are a bit thorny and
some of them involve the handling of 3rd party code.
1) XML Parsing
Parts of the codebase involve the use of XML Parsing, which may either be
DOM parsing or SAX parsing. It turns out that some of our code depends
explicitly on the Xerces DOM parser implementation and some depends on the
Woodstox SAX parser implementation.
This turns out to be complex, especially on Java 1.6.0. The basic problem
is that there is an API layer for each type of parser, which in turn loads
the parser implementation code. The API layer discovers the available
parser implementations through Factory declarations in the JAR
/META-INF/services directory.
So far so good, except that in JDK 1.6.0 BOTH of the API layers (for DOM
Parsing and for SAX parsing) are actually in the base JDK itself. When
this happens, the API layer and the Parser implementation unfortunately
end up in different OSGi bundles - the Core bundle and the Equinox library
bundle (in practice). And so, when the API layer finds and tries to load
the Parser code - the Parser class is not found as it is not marked as a
dependency of the API code.
I used two alternative strategies to solve this.
For Xerces, I found only one dependency on this in binding-ws-axis2 - and
I removed it. These days, XML DOM Parsing is present in the core JDK and
there is no need to specify some other parser package.
For Woodstox, there are a number of our modules depending on this and
instead of trying to eliminate this, I used the technique of putting the
wstx-asl-3.2.4.jar into the jre\lib\endorsed directory of my JDK1.6.0
installation. This then treats the Woodstok Parser as an extension of the
Core - and in OSGi terms puts the Parser code into the same bundle as the
rest of Core.
This is related to the popular jar service provider pattern. Quite a few JDK
classes use this technique to discover concrete service providers (see
http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Service%20Provider).
Unfortunately, its classloading scheme is not OSGi-friendly because it
depends on the thread context classloader and system classloader.
In your particular case, I think there is a caveat in the following JDK
FactoryFinder classes (I think they copy it around :-():
javax.xml.parsers.FactoryFinder.class
javax.xml.stream.FactoryFinder.class
If a class is loaded by the JDK, the classloader is null. In this case, the
FactoryFinder uses the system classloader (which typically is the
application classloader based on the classpath settings) to search for
META-INF/services/<spi class name> resources. But then it uses the thread
context classloader (TCCL) to load the spi implementation class by name. If
the system classloader is not the same as the TCCL, then TCCL might be able
to find the classes available from the system classloader.
2) Incorrect labelling of OSGi Dependencies
There were a number of Manifest files that stated a dependence on
javax.xml.namespace with version="1.0.0". I have no idea where this
version number came from - but it is not good. This package is in Core in
both JDK 1.5.x and JDK 1.6.0 and it is *NOT* at 1.0.0 level.
Simply removing the version number entirely solves this problem.
The version is picked up by the Apache Felix Maven Bundle Plugin if there is
a bundle that exports such a package. Our MANIFEST.MFs were initially
generated from the plugin. In JDK 1.5, some of the packages are not part of
the JDK and they are not treated as OSGi system packages.
Removing the version number makes sense.
3) Cross-Bundle Class Loading Problems
It turns out that some of the 3rd Party libraries that we use involve code
that loads classes from outside the library, but where there is no
explicit dependency of the 3rd party code on the code package(s) from
which the classes are loaded. Typically, this involves 3rd party code
that supplies library extension APIs where the NAME of an extensio class
belonging to the CALLER is passed in through the API, but the classes are
instantiated by the code within the API layer.
There are 2 main cases of this:
1) Axis2 - the axis2-kernel-1.4.1 module. This has an API which is used
to load a class from our binding-ws-axis2 module.
2) Axiom - the axiom-api module loads classes from the axiom-dom and from
the axiomn-impl modules
In both these cases, there is no explicit dependency of the loading module
on the module from which the class(es) are loaded - and in reality,
neither should there be, since the other module is in a sense a "user" of
the loading module.
I decided that the way to fix this was to patch the MANIFEST files of the
bundles concerned to use the Eclipse Buddy technology. This means that:
a) The bundle doing the loading of these "foreign" classes is marked with
"Eclipse-BuddyPolicy: dependent"
- which says that the module has some "buddy" modules from which some
classes will be loaded
b) The bundle(s) providing the loaded classes are marked with:
"Eclipse-RegisterBuddy: xxxxxxxxxxx"
- where xxxxxxxxxxxx is the symbolic name of the bundle from a) - and this
indicates that this
bundle provides classes to the first bundle.
One problem with this approach is that it only works for Equinox and I
don't think it is available for other OSGi implementations such as Felix.
We need to think harder about this problem, but something like the
BuddyPolicy solution is needed - and we need to coax the 3rd Party
providers to add this to their bundle manifests.
It's a bit tricky if the 3rd part library is partially-baked OSGi bundle. If
it is a plain jar, we export/import all the packages for the wrapping bundle
and they can load all the publicly-exported packages. I see a few options to
fix the 3rd party bundle issues:
1) Report the issue back to the owning project and get it fixed at the root
2) Treat them as non-OSGi bundle and use our folder-based bundle packaging
technique to change the MANIFEST.MF
3) Add a fragment bundle to the 3rd-party bundle to patch the MANIFEST.MF
Yours, Mike.