OSGI Import-Package: version vs bundle-version - what's the difference?
Asked Answered
E

1

6

I'm having trouble getting an OSGi bundle loaded into a third party framework. It keeps rejecting my bundle because I am specifying Import-Package versions that do not exist, even though I know they are loading the bundles with the versions I want/need. To get around the problem, I have temporarily disabled my version requirements, but it is a little ugly.

Import-Package: com.ghc.ghTester.expressions,org.apache.ws.security;vers
 ion=0,org.apache.ws.security.components.crypto;version=0,org.apache.ws.
 security.message;version=0,org.apache.ws.security.message.token;version
 =0,org.apache.ws.security.processor;version=0

When I look at the Manifest for my dependency, I see:

Bundle-Version: 1.5.11
Bundle-ClassPath: wss4j-1.5.11.jar
Bundle-Vendor: Apache
Export-Package: org.apache.ws.axis.security,
 org.apache.ws.axis.security.handler,
 org.apache.ws.security,
 org.apache.ws.security.action,
 org.apache.ws.security.components.crypto,
 org.apache.ws.security.conversation,
 org.apache.ws.security.conversation.dkalgo,
 org.apache.ws.security.handler,
 org.apache.ws.security.message,
 org.apache.ws.security.message.token,
 org.apache.ws.security.processor,
 org.apache.ws.security.saml,
 org.apache.ws.security.transform,
 org.apache.ws.security.util

After complaining to the Framework team, they have told me that I need to use bundle-version and not version in my Import-Package statement, such as:

Import-Pacakge: org.apache.ws.security;bundle-version=[1.5.0,2)

I've read through the OSGi Specs (page 50), but cannot seem to understand the nuance between these two values:

The developer can specify arbitrary matching attributes. See Attribute Matching on page 58. The following arbitrary matching attributes are predefined:

• version - A version-range to select the exporter's package version. The syntax must follow Version Ranges on page 36. For more information on version selection, see Semantic Versioning on page 54. If this attribute is not specified, it is assumed to be [0.0.0, ∞).

• specification-version - This attribute is an alias of the version attribute only to ease migration from earlier versions. If the version attribute is present, the values must be equal.

• bundle-symbolic-name - The bundle symbolic name of the exporting bundle. In the case of a fragment bundle, this will be the host bundle's symbolic name.

• bundle-version - A version-range to select the bundle version of the exporting bundle. The default value is [0.0.0, ∞). See Semantic Versioning on page 54. In the case of a fragment bundle, the version is from the host bundle.

Can someone please clarify the difference between the version and the bundle-version? From the way I read the docs, the bundle-version (ie: the manifest's Bundle-Version) would extend to all packages in the bundle. So wouldn't the package version (ie: version) be the same as the bundle-version (ie: bundle-version) on the Import-Package statement? Why is there an ability to specify the two differently?

Euonymus answered 5/4, 2016 at 18:36 Comment(0)
C
12

It's pretty simple: bundle-version binds you to the version of the bundle that is exporting the package, whereas version binds to the version of the package itself.

You almost never want to use bundle-version on imports. The version of the exporting bundle is pretty much irrelevant... in fact when you are importing a package you shouldn't even care about the identity of the bundle exporting it. It's the package you care about, not the bundle it comes from.

Who told you to use bundle-version? There might be some very special reason for this, but I doubt it. More likely the person who told you this is just mistaken.

You asked:

So wouldn't the package version (ie: version) be the same as the bundle-version (ie: bundle-version) on the Import-Package statement?

No it isn't! Bundle version has nothing to do with package version. When you change a package, you change the version of that package. The bundle is just a delivery mechanism.

UPDATE

Rereading your question, I notice that the version of the packages exported by the dependency is not specified. That means that the framework is exporting everything as version 0.0.0, since that is the "default" version when a version is not specified. The version of a package emphatically does NOT default to the version of the bundle it lives in. Unfortunately it seems that the author of this framework does not have a good understanding of OSGi.

UPDATE 2

Since the framework you are using does not provide versions, I recommend creating a no-code wrapper bundle that re-exports the packages with a proper version attached. This is quite simple to build, and it will keep all the ugliness in one place. Then any of your other bundles can simply import the packages with normal versions.

Since you haven't told me the BSN of the problematic bundle I will call it "org.foo". First create a file called manifest.txt as follows:

Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.foo.wrapper
Bundle-Version: 1.5.11
Require-Bundle: org.foo; bundle-version="[1.5.11,1.5.11]"
Export-Package: org.apache.ws.axis.security;version=1.5,
 org.apache.ws.axis.security.handler;version=1.5,
 ...

(Obviously I have made some other assumptions about versions here which you can correct.)

Now build the bundle with:

jar cfm wrapper.jar manifest.txt

Now that you have this bundle, your normal bundle can import a package like this:

Import-Package: org.apache.ws.security; version="[1.5,2)"

Hopefully you are using a tool based on bnd, in which case the Import-Package header will be generated for you including the version range.

Capper answered 5/4, 2016 at 18:48 Comment(10)
So where/how do you specify the package version? Is there a statement in the Manifest that indicates which version it is? Or in the package-info.java file?Euonymus
See my update. In the sources for a bundle you can specify versions in package-info.java but it looks like you are using a pre-built binary bundle. In that case the version of the exports is purely defined by the Export-Package header in MANIFEST.MF. And since the bundle hasn't specified any versions, all the versions are 0.Capper
I think I follow. In the framework I'm using, it looks like they have taken pre-built binaries (ex: Apache WSS4j 1.5) and rewrote a Manifest to expose packages via the Manifest entry. So if I were to change my dependency to a bundle version, although it would not be technically correct in a true OSGi environment, targeted for this specific framework, I suspect it should work - correct? Basically indicating all packages that the specified bundle-version exposes? Is that accurate?Euonymus
The best thing to do is to get the exporting bundle fixed to use versions. Failing this, yes I suppose you can use bundle-version attribute on the import. You also need to use the bundle-symbolic-name attribute because otherwise you would be importing from version X of any bundle... the version is only meaningful for this specific bundle.Capper
Frankly I feel it would be better to write a wrapper bundle that depends on the 3rd party framework bundle (using Require-Bundle) and re-export those packages with a version on them. That puts all the evil shit into one special place and avoids tainting your "ordinary" bundles with weird attributes on their imports.Capper
Not a bad idea, but not exactly sure how that will work/play with dependencies, etc. I fear that the framework will try to load its own versions of classes (ie: those it exposes itself) instead of those found in my wrapper bundle. Plus, that also gives me 2 bundles to deploy together - one wrapper and my own. Finally - net result would be the same - no? I would have to rely on my wrapper's bundle-symbolic-name and a bundle-version instead of actual package names/versions.Euonymus
No that's not a problem. The proposed 'wrapper' bundle doesn't contain a copy of the types, it just requires the original bundle and reexports the packages with a version. So the framework is guaranteed to use the same types as your app. Also you don't need to do anything special with your bundles: just import the packages with a version. Enabling this is the point of creating the special wrapper. It does create one extra bundle to deploy though, that is unavoidable.Capper
So now I'm confused. The only way I can think of doing that is create an empty bundle with only package-info.java classes in each package defining the package version. Then, in the manifest of this bundle, have it use a dependency on the provided bundle-version/bundle-symbolic-name. So my wrapper bundle would still have an ugly dependency on the framework's exposed packages. My implementation bundle, would be able to have clean dependencies. Is this accurate? Is there another way of defining package versions that I'm not understanding? Can you show a quick sample (pseudo code is fine)?Euonymus
It's much simpler than that. I'll update the answer with details.Capper
Of course! Sometimes the obvious solutions are the easiest to overlook. Thanks!Euonymus

© 2022 - 2024 — McMap. All rights reserved.