Dependency Versioning
Posted by Mike Haller
on Monday, February 18. 2008
at 20:15
in Java
OSGi bundles let you specify exactly which version of a dependency is required:- no version specified - every bundle exporting your requested packages will satisfy your dependency
- exact version: [1.0.0,1.0.0] - only the bundle which exports packages with v1.0.0 will satisfy your dependency
- version range [1.0.0,1.2.0] - bundles which export packages 1.0.x, 1.1.x and 1.2.x will satisfy your dependency
- version range [1.0.0,2.0.0) - bundles which export packages 1.0.0 up to 1.9.9 will satisfy your dependency, but bundles exporting package 2.0.0 and higher won't
When specifying a version dependency, you need to know which version of a library is compatible with your code. You can be very strict and specify an exact requirement, or you can be tolerant.
Suppose you're using the latest stable build of a dependency (e.g. 1.4) and you know they're working on 1.5 and it will probably be compatible, you can use a range of 1.4-1.5. Since you don't know yet if version 1.6 will also be compatible, you probably don't want it to include in your version range. But what happens if 1.6 and 1.7 are still compatible? You would still need to repackage your bundle and respecify your dependency versions.
When working with Equinox (the Eclipse OSGi implementation), you probably use the Eclipse Extension Point mechanism. Sadly, using the Extension Point mechanism makes your bundle practically a singleton. No way of installing multiple versions of your plug-in. For example, Eclipse would not be able to decide whether a menu contribution should be taken from your 1.0 bundle or from version 1.1. In such cases, Eclipse will start the bundle with the highest version.
Let's do an example:
I created four bundles. Two of which are dependencies which provide some logic, let's call them osgi.dependency_v10 and osgi_dependency_v11
Then two clients: the first client wants to have osgi.dependecy in the exact version v10 and the second client is more tolerant and will work with v10 and v11.
The code of the osgi.dependency bundles is pretty simple: There is a package osgi.dependency.example with a class HelloWorld, which returns either "I am v1.0" or "I am v1.1". Both have the same package namespace and classname.
The clients use HelloWorld to print a message on the console when started:
osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.4.0.v20071207 1 RESOLVED osgi.dependency_v11_1.0.0 2 RESOLVED osgi.client_v10_1.0.0 3 RESOLVED osgi.client_v10v11_1.0.0 4 RESOLVED osgi.dependency_v10_1.0.0 osgi> start 3 Client v10/v11 starting, uses dependency: I am v1.1 osgi> start 2 Client v10 starting, uses dependency: I am v1.0
Now, if we're going to remove the dependency v10 from the OSGi container, the client v10v11 will still work with dependency v11, but the client v10 won't be able to start:
osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.4.0.v20071207 1 RESOLVED osgi.dependency_v11_1.0.0 2 RESOLVED osgi.client_v10_1.0.0 5 RESOLVED osgi.client_v10v11_1.0.0 6 RESOLVED osgi.dependency_v10_1.0.0 osgi> uninstall 6 osgi> refresh osgi> start 5 Client v10/v11 starting, uses dependency: I am v1.1 osgi> start 2 org.osgi.framework.BundleException: The bundle could not be resolved. Reason: Missing Constraint: Import-Package: osgi.dependency.example; version="[1.0.0,1.0.0]"
The refresh is required to re-read the dependencies and their exported packages, after uninstalling or installing bundles at runtime. Don't know if this is only required for Equinox.
