Reputation: 157
My project has two OSGi bundles (A and B) which need to use different versions of javax.activation
— A requires version 1.1.0, while B requires 1.1.1.
By default in AEM 5.6.1, there is a bundle already installed which exports version 1.1.1, which bundle A is using. In order to make it use 1.1.0 instaed, I used boot delegation to get the javax.activation from the JRE 7 for the system bundle which is 1.1.0. I am setting this using sling.properties file in AEM 5.6.1.
If I give a version for javax.activation greater than 1.1.1 in this sling.properties file, both A and B are using the system version (even though version of import-packages is specified in the manifest.mf
file); but if I give a version less than 1.1.1, both bundles use the version provided by AEM.
How can I configure my bundles so as to use different versions of javax.activation for Bundle A from Bundle B?
Upvotes: 2
Views: 6078
Reputation: 56
If you want to use exactly 1.1.0 version in bundle A, than you should specify it in A's manifest file:
Import-Package: javax.activation;version="[1.1.0,1.1.0]"
For bundle B manifest will be:
Import-Package: javax.activation;version="[1.1.1,1.1.1]"
Upvotes: 3
Reputation: 9402
This can be downright difficult due to the OSGI bundle resolution rules. Check out this article--I found it to be a good explanation of the various rules that apply. Specifically, check out the uses
directive:
http://www.christianposta.com/blog/?p=241
Life will be much simpler if there is a way for both to use the the same version (but I know that may not always be possible).
This is from the article above:
For every Import-Package package declaration, there must be a corresponding Export-Package with the same package
Bundles can also attach other attributes to the packages it imports or exports. What if we added a version attribute to our example:
Bundle-Name: Bundle A Import-Package: org.apache.foo;version="1.2.0"
This means, Bundle A has a dependency on package org.apache.foo with a minimum version of 1.2.0. Yes, you read correctly. Although with OSGI you can specify a range of versions, if you don’t specify a range but rather use a fixed version, it will result in a meaning of “a minimum” of the fixed value. If there is a higher version for that same package, the higher version will be used. So bundle A will not resolve correctly unless there is a corresponding bundle B that exports the required package:
Bundle-Name: Bundle B Export-Package: org.apache.foo;version="1.2.0"
Note that the reverse is not true… If Bundle B exported version 1.2.0, Bundle A is not required to specify a version 1.2.0. It can use this import and resolve just fine:
Bundle-Name: Bundle A Import-Package: org.apache.foo
This is because imports declare the versions they need. An exported version does not specify anything an importing bundle must use (which holds for any attributes, not just version). Import-Package dictates exactly what version (or attribute) it needs, and a corresponding Export-Package with the same attribute must exist
What happens if you have a scenario where Bundle A imports a package and it specifies a version that is provided by two bundles:
Bundle-Name: Bundle A Import-Package: org.apache.foo;version="1.2.0"
Bundle-Name: Bundle B Export-Package: org.apache.foo;version="1.2.0"
Bundle-Name: Bundle C Export-Package: org.apache.foo;version="1.2.0"
Which one bundle does Bundle A use? The answer is it depends on which bundle (B or C) was installed first. Bundles installed first are used to satisfy a dependency when multiple packages with the same version are found
Things can get a little more complicated when hot deploying bundles after some have already been resolved. What if you install Bundle B first, then try to install Bundle A and the following Bundle D together:
Bundle-Name: Bundle D Export-Package: org.apache.foo;version="1.3.0"
As we saw from above, the version declaration in Bundle A (1.2.0) means a minimum version of 1.2.0; so if a higher version was available then it would select that (version 1.3.0 from Bundle D in this case). However, that brings us to another temporal rule for the bundle resolution: Bundles that have already been resolved have a higher precedence that those not resolved
The reason for this is the OSGI framework tends to favor reusability for a given bundle. If it’s resolved, and new bundles need it, then it won’t try to have many other versions of the same package if it doesn’t need to. Bundle “uses” directive
The above rules for bundle resolution are still not enough and the wrong class could still be used at runtime resulting in a class-cast exception or similar. Can you see what could be missing?
What if we had this scenario. Bundle A exports a package, org.apache.foo, that contains a class, FooClass. FooClass has a method that returns an object of type BarClass, but BarClass is not defined in the bundle’s class space, it’s imported like this:
1 2 3 public class FooClass { public BarClass execute(){ ... } }
Bundle-Name: Bundle A Import-Package: org.apache.bar;version="3.6.0" Export-Package: org.apache.foo;version="1.2.0"
So far everything is fine as long as there is another bundle that properly exports org.apache.bar with the correct version.
Bundle-Name: Bundle B Export-Package: org.apache.bar;version="3.6.0"
These two bundles will resolve fine. Now, if we install two more bundles, Bundle C and Bundle D that look like this:
Bundle-Name: Bundle C Import-Package: org.apache.foo;version="1.2.0", org.apache.bar;version="4.0.0"
Bundle-Name: Bundle D Export-Package: org.apache.bar;version="4.0.0"
We can see that Bundle C imports a package, org.apache.foo from Bundle A. Bundle C can try to use FooClass from org.apache.foo, but when it gets the return value, a type of BarClass, what will happen? Bundle A expects to use version 3.6.0 of BarClass, but bundle C is using version 4.0.0. So the classes used are not consistent within bundles at runtime (i.e., you could experience some type of mismatch or class cast exception), but everything will still resolve just fine at deploy time following the rules from above. What we need is to tell anyone that imports org.apache.foo that we use classes from a specific version of org.apache.bar, and if you want to use org.apache.foo you must use the same version that we import. That’s exactly what the uses directive does. Let’s change bundle A to specify exactly that:
Bundle-Name: Bundle A Import-Package: org.apache.bar;version="3.6.0" Export-Package: org.apache.foo;version="1.2.0"";uses:=org.apache.bar
Given the new configuration for Bundle A, the bundles would not resolve correctly from above. Bundle C could not resolve, because it imports org.apache.foo but the “uses” constraint on Bundle A specifies that C must use the same version that A does (3.6.0) for org.apache.bar, otherwise the bundle will not resolve when trying to deploy. The solution to this is change the version in Bundle C for org.apache.bar to be 3.6.0.
Upvotes: 2