This page explores versioning of Tuscany modules and 3rd party libraries distributed with Tuscany. The description below assumes that each Tuscany module is converted into one OSGi bundle and that each 3rd party library jar is converted into one OSGi bundle. The ongoing discussion on the shape of Tuscany distributions will impact versioning, but as long as entities that require to be versioned are available as OSGi bundles, these examples will be relevantapply in some form. At least for 3rd party libraries, we should definitely provide one bundle per jar to enable versioning that is compatible with 3rd party bundles from other OSGi repositories.
...
In the figure above, Tuscany is a black box (ok, a blue one) which has been OSGi-enabled at a fine grained level. Tuscany modules and 3rd party libs are all individual bundles, with proper versions, and multiple extensions can execute using different versions of 3rd party libs side-by-side. So Tuscany now has the full benefits of OSGi. From an application developer's point of view, Tuscany is a big blob that provides the Tuscany SPI/SCA API. Everything else is internal to Tuscany. If an application wants to use Axis, it will install a different copy of it. So applications will not accidentally use Tuscany's 3rd party jars. And Tuscany also will not accidentally use other 3rd party jars bundle-ized elsewhere. Tuscany's imports and exports are guarded with Tuscany-specific attributes in this case. IMO this is not good practice in OSGi - Tuscany is not being a good citizen.
In this case, apart from Tuscany SPI/SCA API, which we want others to use, other import/exports will look like:
No Format |
---|
Export-Package: javax.xml.bind;version="2.1";exporter="tuscany";mandatory:=exporter
Import-Package: javax.xml.bind;version="[2.1,3.0)";exporter="tuscany"
|
An user wishing to migrate to a different version of a 3rd party bundle will not be able to use a version from a different OSGi repository. An application can potentially use Tuscany's copy of a third party library, but only by explicitly specifying the "tuscany" attribute.
Figure 2) also uses exactly the same set of bundles as Figure 1), but in this case there are no Tuscany-specific attributes. 3rd party bundles are potentially shared across applications and Tuscany when import version ranges match. Some of this sharing may not be intentional. Tuscany has to interoperate with bundles from elsewhere, and Tuscany's 3rd party bundles should interoperate with applications. For 3rd party jars which are used to share classes between applications and Tuscany, this is the only option.
In this case, import/exports in Tuscany as well as applications will look like:
No Format |
---|
Export-Package: javax.xml.bind;version="2.1" Import-Package: javax.xml.bind;version="[2.1,3.0)" |
OSGi runtime has the flexibility to choose appropriate bundles when multiple copies are present. IMO we should use 2) rather than 1).
...
How complicated can it be? We have version numbers across all jars and bundles and we want to put sufficient restrictions in place to use appropriate versions of jars. For small applications, versioning is not a big deal at all, and can be easily handled using defaults from tools. But for something the size of Tuscany with nearly 150 third party libraries which is used to assemble applications which may use some of these jars for other purposes, import versioning can indeed be extremely complex to support all possible scenarios. The level of sharing of classes required, the level of isolation required for side-by-side execution of multiple versions, the flexibility required for upgrading 3rd party libs and the actual version ranges that are compatible should all be taken into account to determine the optimum version range (for each of the 150 3rd party libs).
Let us start with a (simple) scenario:
...
This is the default used by most tools like maven-bundle-plugin, and other versions of 3rd party libraries from repositories like SpringSource also make version ranges in imports as broad as possible. OSGi spec says: "the import should be as unconstrained as possible to allow the resolver maximum flexibility". So what does this really mean?
Let's look at the example from Fig 3.
Tuscany imports can import Axiom API using an import statement like:
...
The broader the version range, the higher the class sharing that is enabled. Since we still have 3rd party libraries which rely on thread context classloaders which dont fit in with OSGi modularity/versioning, a broad range in import versioning reduces the risk of classloading errors arising from multiple definitions of a class in different classloaders.But there is a negative side - as shown in Figure 4, we are preventing side-by-side execution of different versions of jaxb.
Another scenario is upgrading one 3rd party library in Tuscany. An user wants to migrate Tuscany to using version 1.4 of Axiom. A flexible versioning system enables the user to just install the new version of Axiom, without any changes to Tuscany bundles. eg. An user can get Axiom 1.4 from SpringSource and easily run it with Tuscany without waiting for the next release of Tuscany that ships with Axiom 1.4.
But there is a negative side - as shown in Figure 4, we are preventing side-by-side execution of different versions of jaxb. Also we cannot easily prevent Tuscany being run with a newer version which may be incompatible if an application wants to switch to a newer version.
Narrow version ranges in imports
...
Back to the scenario where an user wants to migrate to a later version of a 3rd party library like 1.4 of Axiom. The narrow version range in Tuscany imports essentially means that the user has to modify every single Tuscany bundle which imports Axiom to switch to import version 1.4 of Axiom.
So narrow version ranges can reduce class sharing and increase side-by-side execution, but that also make upgrades difficult, introduce introduces more number of classes loaded in the system, and can result in classloading exceptions resulting from multiple definitions of classes. TCCL based classloading can potentially lead to problemsexceptions.
Figure 4) and Figure 5) are showing the two extremes, the version range we want may be somewhere in between. eg. By using the version range \ [1.2.5,2.0.0) of Axiom, Tuscany will be importing any version of Axiom within this major version 1, and preventing importing of the next major version 2, which may contain breaking changes. Wiki Markup
If you have actually read this far, you must be patient. I will try to summarize the points that we need to considernow.
Points to consider for versioning 3rd party libs
- What is the minimum version that Tuscany will work against? Can we assume that the currently used version is the lowest supported?
- What is the maximum version that Tuscany will work against? Can we assume that all the APIs we use will remain backward compatible? For a generic versioning scheme which doesn't involve looking at every single one of the 149 libs, the choices for max version are
- current version
- any minor version with the current major version
- infinity
- What are the third party libs where classes are shared between applications and Tuscany?
- For libraries in 3) what are the versioning requirements? Min version? Max version?
- Does Tuscany have extensions which require different versions of a 3rd party lib? If so these should use very narrow version ranges to enable the libs to coexist.
- Are there applications which require different versions of a 3rd party lib from Tuscany? If so, should we use very narrow version ranges to enables enable these libs to co-exist?
- Do we want to be consistent with bundles from other sources? SpringSource? ServiceMix? Any others?
...
- One bundle per Tuscany module, one bundle per 3rd party lib
- Based on the discussion on aggregating modules into larger bundles, the Tuscany module bundling may change.
- All Tuscany modules export packages at Tuscany version (eg. 1.4.0), all third party libs export packages at their jar version.
- All bundles with exports will also import those packages using the same version range as other importers (OSGi best practice)
- Imports and "uses" statements in exports
- Minimum version: same as currently used version in Tuscany
- Maximum version - use defaults in the first instance for all 3rd party libs (ie. maximum version is infinity).unmigrated-wiki-markup
- Move to maximum version under current major version, Eg. Jaxb 2.1.6 will be imported using version range \ [2.1.6,3.0.0)
- Fine tune further to narrow down version ranges for specific third party libs as we learn more.
...