Table of Contents
Requirement 1. The Axiom artifacts SHOULD be usable both as normal JAR files and as OSGi bundles.
The alternative would be to produce two sets of artifacts during the build. This should be avoided in order to keep the build process as simple as possible. It should also be noted that the Geronimo Spec artifacts also meet this requirement. |
Requirement 2.
All APIs defined by the axiom-api
module, and in particular the
OMAbstractFactory
API MUST continue to work
as expected in an OSGi environment, so that code in downstream projects
doesn't need to be rewritten.
Requirement 3.
OMAbstractFactory
MUST select the same implementation
regardless of the type of container (OSGi or non OSGi). The only exception is
related to the usage of system properties to specify the default OMMetaFactory
implementation: in an OSGi environment, selecting an implementation class using
a system property is not meaningful.
This is currently not the case. In a non OSGi environment, recent versions of Axiom use JDK 1.3 service discovery to locate the default implementation and fall back to LLOM if none is found. DOOM will never be selected as the default implementation. On the other hand, the current OSGi integration will select any Axiom implementation as default implementation, but gives priority to LLOM. |
Requirement 4.
The bundles for the LLOM and DOOM implementations MUST NOT export any packages.
This is required to keep a clean separation between the public API and implementation
specific classes and to make sure that the implementations can be modified without the
risk of breaking existing code.
An exception MAY be made for factory classes related to foreign APIs, such as the
DocumentBuilderFactory
implementation for an Axiom implementation
supporting DOM.
When the Axiom artifacts are used as normal JAR files in a Maven build, this requirement implies that
they should be used in scope Although this requirement is easy to implement for the Axiom project, there are currently a couple of issues in the downstreams project that need to be addressed to make this work:
|
Requirement 5.
It MUST be possible to use a non standard (third party) Axiom implementation as a drop-in replacement
for the standard LLOM and DOOM implementation, i.e. the axiom-impl
and axiom-dom
bundles. It MUST be possible to replace axiom-impl
(resp. axiom-dom
) by any Axiom implementation that supports the full Axiom API
(resp. that supports DOM in addition to the Axiom API), without the need to change any application code.
This requirement has several important implications:
|
Requirement 6. The OSGi integration SHOULD remove the necessity for downstreams projects to produce their own custom OSGi bundles for Axiom. There SHOULD be one and only one set of OSGi bundles for Axiom, namely the ones released by the Axiom project.
Currently there are at least two projects that create their own modified Axiom bundles:
Note that this requirement can't be satisfied directly by Axiom. It requires that the above mentioned projects (Geronimo, Axis2 and Abdera) use Axiom in a way that is compatible with its design, and in particular with Requirement 4. Nevertheless, Axiom must provide the necessary APIs and features to meet the needs of these projects. |
Requirement 7. The Axiom OSGi integration SHOULD NOT rely on any particular OSGi framework such as Felix SCR (Declarative Services). When deployed in an OSGi environment, Axiom should have the same runtime dependencies as in a non OSGi environment (i.e. StAX, Activation and JavaMail).
Axiom 1.2.12 relies on Felix SCR. Although there is no real issue with that, getting rid of this extra dependency is seen as a nice to have. One of the reasons for using Felix SCR was to avoid introducing OSGi specific code into Axiom. However, there is no issue with having such code, provided that Requirement 8 is satisfied. |
Requirement 8. In a non OSGi environment, Axiom MUST NOT have any OSGi related dependencies. That means that the OSGi integration must be written in such a way that no OSGi specific classes are ever loaded in a non OSGi environment.
Requirement 9. The OSGi integration MUST follow established best practices. It SHOULD be inspired by what has been done to add OSGi integration to APIs that have a similar structure as Axiom.
Axiom is designed around an abstract API and allows for the existence of multiple
independent implementations. A factory (
It should be noted that because of the way the Axiom API is designed and taking into account
Requirement 2, it is not possible to make Axiom entirely compatible
with OSGi paradigms (the same is true for JAXB). In an OSGi-only world, each Axiom
implementation would simply expose itself as an OSGi service (of type |
Non-Requirement 1.
APIs such as JAXP and JAXB have been designed from the start for inclusion into the JRE.
They need to support scenarios where an application bundles its own implementation
(e.g. an application may package a version of Apache Xerces, which would then be
instantiated by the newInstance
method in
DocumentBuilderFactory
). That implies that the selected implementation
depends on the thread context class loader. It is assumed that there is no such requirement
for Axiom, which means that in a non OSGi environment, the Axiom implementations are always loaded from the same
class loader as the axiom-api
JAR.
This (non-)requirement is actually not directly relevant for the OSGi support, but it nevertheless has some importance because of Requirement 3 (which implies that the OSGi support needs to be designed in parallel with the implementation discovery strategy applicable in a non OSGi environment). |
As noted in Requirement 9 the Apache Geronimo has successfully
added OSGi support to the JAXB API which has a structure similar to the Axiom API. This section briefly describes
how this works. The analysis refers to the following Geronimo artifacts:
org.apache.geronimo.specs:geronimo-jaxb_2.2_spec:1.0.1
(called the "API bundle" hereafter),
org.apache.geronimo.bundles:jaxb-impl:2.2.3-1_1
(the "implementation bundle"),
org.apache.geronimo.specs:geronimo-osgi-locator:1.0
(the "locator bundle") and
org.apache.geronimo.specs:geronimo-osgi-registry:1.0
(the "registry bundle"):
The implementation bundle retains the META-INF/services/javax.xml.bind.JAXBContext
resource from the original artifact (com.sun.xml.bind:jaxb-impl
).
In a non OSGi environment, that resource will be used to discover the implementation, following
the standard JDK 1.3 service discovery algorithm will (as required by the JAXB specification).
This is the equivalent of our Requirement 1.
The manifest of the implementation bundle has an attribute SPI-Provider: true
that indicates
that it contains provider implementations that are discovered using the JDK 1.3 service discovery.
The registry bundle creates a BundleTracker
that looks for
the SPI-Provider
attribute in active bundles. For each bundle
that has this attribute set to true
, it will scan the content of
META-INF/services
and add the discovered services to a registry
(Note that the registry bundle supports other ways to declare SPI providers,
but this is not really relevant for the present discussion).
The ContextFinder
class (the interface of which is defined by
the JAXB specification and that is used by the newInstance
method in JAXBContext
) in the API bundle delegates the discovery
of the SPI implementation to a static method of the ProviderLocator
class defined by the locator bundle (which is not specific to JAXB and is used by other
API bundles as well). This is true both in an OSGi environment and in a non OSGi environment.
The build is configured (using a Private-Package
instruction)
such that the classes of the locator bundle are actually included into the API bundle, thus
avoiding an additional dependency.
The ProviderLocator
class and related code provided by the locator bundle is designed
such that in a non OSGi environment, it will simply use JDK 1.3 service discovery to locate
the SPI implementation, without ever loading any OSGi specific class. On the other hand,
in an OSGi environment, it will query the registry maintained by the registry bundle to locate
the provider. The reference to the registry is injected into the ProviderLocator
class using a bundle activator.
Finally, it should also be noted that the API bundle is configured with singleton=true
.
There is indeed no meaningful way how providers could be matched with different versions of the same API
bundle.
This is an example of a particularly elegant way to satisfy Requirement 1,
Requirement 2 and Requirement 3, especially because
it relies on the same metadata (the META-INF/services/javax.xml.bind.JAXBContext
resources)
in OSGi and non OSGi environments.
Obviously, Axiom could reuse the registry and locator bundles developed by Geronimo. This however would contradict Requirement 7. In addition, for Axiom there is no requirement to strictly follow the JDK 1.3 service discovery algorithm. Therefore Axiom should reuse the pattern developed by Geronimo, but not the actual implementation.
Application code rarely uses DOOM as the default Axiom implementation. Several downstream projects
(e.g. the Axis2/Rampart combination) use both the default (LLOM) implementation and DOOM. They select
the implementation based on the particular context. As of Axiom 1.2.12, the only way to create an object
model instance with the DOOM implementation is to use the DOOMAbstractFactory
API
or to instantiate one of the factory classes (OMDOMMetaFactory
, OMDOMFactory
or one of the subclasses of DOMSOAPFactory
). All these classes are part of
the axiom-dom
artifact. This is clearly in contradiction with Requirement 4
and Requirement 5.
To overcome this problem the Axiom API must be enhanced to make it possible to select an Axiom implementation based on capabilities/features requested by the application code. E.g. in the case of DOOM, the application code would request a factory that implements the DOM API. It is then up to the Axiom API classes to locate an appropriate implementation, which may be DOOM or another drop-in replacement, as per Requirement 5.
If multiple Axiom implementations are available (on the class path in non OSGi environment or deployed as bundles in an OSGi environment), then the Axiom API must also be able to select an appropriate default implementation if no specific feature is requested by the application code. This can be easily implemented by defining a special feature called "default" that would be declared by any Axiom implementation that is suitable as a default implementation.
DOOM is generally not considered suitable as a default implementation because it doesn't
implement the complete Axiom API (e.g. it doesn't support |
Finally, to make the selection algorithm deterministic, there should also be a concept of priority: if multiple Axiom implementations are found for the same feature, then the Axiom API would select the one with the highest priority.
This leads to the following design:
Every Axiom implementation declares a set of features that it supports. A feature is simply identified by a string. Two features are predefined by the Axiom API:
default
: indicates that the implementation is a complete
implementation of the Axiom API and may be used as a default implementation.
dom
: indicates that the implementation supports DOM
in addition to the Axiom API.
For every feature it declares, the Axiom implementation specifies a priority, which is a positive integer.
The relevant Axiom APIs are enhanced so that they take an optional argument
specifying the feature requested by the application code. If no explicit feature
is requested, then Axiom will use the default
feature.
To determine the OMMetaFactory
to be used, Axiom locates
the implementations declaring the requested feature and selects the one that
has the highest priority for that feature.
A remaining question is how the implementation declares the feature/priority information. There are two options:
Add a method to OMMetaFactory
that allows the Axiom API
to query the feature/priority information from the implementation (i.e. the
features and priorities are hardcoded in the implementation).
Let the implementation provide this information declaratively in its metadata
(either in the manifest or in a separate resource with a well defined name).
Note that in a non OSGi environment, such a metadata resource must be used anyway
to enable the Axiom API to locate the OMMetaFactory
implementations.
Therefore this would be a natural place to declare the features as well.
The second option has the advantage to make it easier for users to debug and tweak the implementation discovery process (e.g. there may be a need to customize the features and priorities declared by the different implementations to ensure that the right implementation is chosen in a particular use case).
This leads to the following design decision:
the features and priorities (together with the class name of the OMMetaFactory
implementation) will be defined in an XML descriptor with resource name META-INF/axiom.xml
.
The format of that descriptor must take into account that a single JAR may contain several
Axiom implementations (e.g. if the JAR is an uber-JAR repackaged from the standard Axiom JARs).