In OSGi bundles packages are exported and imported with a version number. Still bundles have a version defined. This seems redundant to me.
What is the point of versioning both the bundle and the packages contained in it? I understand that bundles/jars might be versioned for using it in non-OSGi systems (for maven, for example). Is there any other reason for this?
I am a bit confused about these two levels of versioning. Are these two version numbers dependent or independent to the package versions they contain? If dependent, what are the rules to version the bundle?
In OSGi, packages define the contracts that are used to let bundles collaborate. Bundles are JARs that hold the code.
The purpose of a package version is to make sure that bundles only depend/use contracts that they are compatible with. Packages use semantic versioning to signal minor, micro, and major changes. A bundle can export and import any number of packages and does provide and consume any number of contracts.
The purpose of a bundle version is to have a unique identifier for each JAR so it can be handled during the development life cycle. A SHA code would work as well but would be less convenient. Some organizations bump the bundle version based on the highest change in any of its packages. I.e. if its contained packages only had minor changes,the bundle would be bumped with a minor change. If one package had a major change, the bundle would be bumpled major.
Related
I am looking for a way to ensure that all the features I deploy in Karaf require dependencies that are of the same version. The project is composed of more than 40 bundles which makes it difficult to verify manually.
I am thinking of developping a Maven plug-in that would make the check, but before I would like to be sure that such a solution do not exist yet.
If you want to be sure you use the same versions then create a parent project and define versions of dependencies only there. So you can be sure all your modules have the same dependencies. Of course this only makes sense if all these modules are very closely related (e.g. belong to the same application / release unit).
Why would you even want to do this? Each bundle should depend on the versions of the package it needs, and that dependency should be a range. So if you compile against and API package version 1.0.0, and you are a consumer of that API, then you should import with the range [1.0.0, 2.0.0). Refer to the OSGi Core Release 5 specification, section 3.7.3 ("Semantic Versioning") for details.
At runtime the OSGi Framework will ensure that your bundle is wired to a package version that is within its permitted range. Obviously if you have non-overlapping version ranges from different importers then the Framework will not be able to satisfy them with a single exporter.
This might be a bit of a weird question, so bear with me.
I've got a Tycho/Eclipse plug-in that only contains a lib folder containing a third-party-library. The library has version 1.17.0, so in an attempt to make the process more transparent, the plug-in has, too.
It exports all the packages of the lib and everything is fine. At least it was, because now I realized it would have been much, much better if I exported the packages with a version. Plug-in 1.17.0 is already released (I know, Tycho doesn't normally release, just assume I don't want to re-use a version that has been stable for a year).
As an OSGi bundle, I am not allowed to take the version 1.17.0.1 or 1.17.0B or something, but I can't just use the version 1.17.1, because it would imply the third-party library is 1.17.1 as well, and it isn't. Updating the third-party library is not possible at the moment, because time is critical.
I tried 1.17.01 just now and nobody complains, but I'm not sure how Maven and OSGI sort this version relative to 1.17.0 and 1.17.1. The Maven manual does not really talk about zeros, but I guess it would force the string comparison, yielding in completely screwed up version sorting.
So... what version can I use safely?
You could just use something like "1.17.0.p1" which indicates a "patch 1" - it should be ok with OSGi as "the qualifier can be any string, sorted lexicographically" (quote taken from your OSGi link). "p1" is similar to qualifiers like "beta1" or "RC1" or...
major.minor.micro.qualifier
This should work for Maven as well. Moreover, we can assume that you would not provide a series of more than ten patches (at least I assume).
Personally, I would avoid leading zeros, as this looks quite odd to me. A series of HotFix or Patch versions should lead to an increment of a higher version number once in a while.
I'm not sure about Tycho but the OSGI framework treats the first three parts of the version as numbers and converts them to int using Integer.parseInt (see org.osgi.framework.Version).
So '01' will be treated exactly the same as '1'.
For the new bundle version, you need to make sure that is is larger in terms of the OSGi semantics - the Maven semantics don't really matter here. So you either need to increase the numeric value of one of the first three segments (e.g. 1.17.1) or add a fourth segment. The fourth segment, the so-called qualifier, is a string, and OSGi considers a version with qualifier to be larger than the same version without a qualifier. So you could e.g. use 1.17.0.a.
One word of warning for the change that you want to introduce with the new bundle version, which is to add versions to the exported packages. It absolutely makes sense to export the packages with a version, but is is crucial that you pick the right version for the packages.
Experience has shown that using the library version as package version is a bad idea: When we built an OSGi wrapper for javax.mail 1.4.1, we also used the version 1.4.1 for the exported packages. But when Sun released javax.mail as OSGi bundle in version 1.4.2, they decided that the packages fulfill the 1.4 API contract and hence exported them as version 1.4. So our older version of the library pretended that its exported packages were better, and the OSGi resolve would therefore prefer it over the newer library version.
To make things worse, we in the meantime also had lots of other bundles that required the javax.mail packages with a minimum version of 1.4.1, so they couldn't even be forced to use the newer library version. In the end, it took us more than a year to clean up the mess we had gotten ourselves with the bad choice of package version in the OSGi wrapper.
Conclusion: Only use very small package version numbers when converting a library that you don't own to an OSGi bundle. When using package version numbers with zero as major version, e.g. 0.1.17, you should be on the safe side that the versions that you invented yourself are smaller than all future "official" version.
One of our bundle has 'imports' on joda-time/2.2.0 but when I start it,it always picks up the joda-time/1.6.2 available in the system folder of Karaf. I am not sure how can that happen? is there a way to tell the karaf not to pick the system bundle over the one specified by us? please note that we drop bundles to deploy folder to get them installed.
First of all, make sure you really need two versions of this bundle. Yes, you can make that work but in general, unless you really need different versions of bundles to be present within your application, avoid it. Check what bundles consume joda-time and what version ranges they specify in their Import-Package statement.
I'm not sure if you're aware of this, but if you import a package, you always, either implicitly or explicitly specify a version range you are compatible with:
If you don't specify any version, you effectively state you're compatible with a version range from zero to infinity.
If you specify only one version, you state you're compatible with that version and anything higher than that, up to infinity.
If you specify two versions, you state you're compatible with that range and you can either use square or round brackets to state if this includes or excludes the borders.
Not specifying a range explicitly is considered bad practice. OSGi has a whitepaper on semantic versioning that explains this in more detail.
So, make sure you understand what versions your bundles that consume joda-time use, see if you can deploy just one implementation of joda-time and ensure that your own bundle also uses a version range that is compatible with that.
Just add the version you need to import after the package you are importing like this.
Import-Package: org.xx.xx;version=1.5.0
Refer this
Can a OSGi bundle have two dependencies, each on a different version of the same OSGi bundle?
Can a OSGi package have two dependencies, each on a different version of the same OSGi package?
(I am trying to learn OSGi from the ground up. This question is just intended to help me understand the basic concepts. From reading online articles about OSGi services, I gather that such dependencies certainly wouldn't be recommended practice. But are they possible at all?)
(Update: rephrased the two questions.)
No. OSGi provides a consistent class space for a bundle. This means that it is only exposed to a single class of a given name. So a bundle cannot simultaneously see more than one version of a package at a time.
This does not mean that ClassCastExceptions are impossible since code your bundle is directly dependent on, can expose objects from their dependencies to your bundle. The proper use of uses constraints on export packages is important to prevent this.
Can a OSGi bundle depend on two different versions of another OSGi bundle at the same time?
Can an OSGi package depend on two different versions of another OSGi package at the same time?
Sort of. You can depend on ranges or specific versions of another OSGI bundle or package like this:
Import-Package: org.osgi.framework;version="[1.3,2.0)"
Not sure if that applies in the first section because bundles should not depend on other bundles, only packages. This is what 'Require-Bundle' does but is suggested you don't use it. Require-Bundle takes versions as well so theoretically it should support version ranges.
Once your OSGi bundle is resolved within OSGi, it will find the package of any of those versions. However, it can't resolve a package (org.osgi.framework) to two separate bundles (one which provides version 1.9 and one which provides 1.8). It will choose the most recent version based on SemVer.
If you try to specify it twice in Import-Package, you will get a 'Duplicate Import' error.
OSGi allows for dependencies to be determined via Import-Package, which just wires up a single package (exported from any bundle), and Require-Bundle, which wires up to a specific named bundle's exports.
In building a greenfield OSGi application, which approach should I use to represent dependencies? Most of the bundles will be internal, but there will be some dependencies on external (open-source) bundles.
I believe Require-Bundle is an Eclipse thing (that has now made it in the OSGi spec to accommodate Eclipse). The "pure" OSGi way is to use Import-Package, as it specifically decouples the package from the bundle that provides it. You should be declaring dependencies on functionality that you need (the Java API provided by a certain version of a certain package) instead of where that functionality is coming from (which should not matter to you). This keeps the composition of bundles more flexible.
JavaScript analogy: This is like detecting whether a web browser supports a certain API versus inferring from what the user-agent string says what kind of browser it is.
Peter Kriens of the OSGi Alliance has more to say about this on the OSGi blog.
Probably the only case where you need to use Require-Bundle is if you have split packages, that is a package that is spread across multiple bundles. Split packages are of course highly discouraged.
Favour Import-Package over Require-Bundle.
Require-Bundle:
specifies the explicit bundle (and version) to use. If a requirde bundle needs to be refactored and a package moved elsewhere, then dependents will need changes to their MANIFEST.MF
gives you accesss to ALL exports of the bundle, regardless of what they are, and regardless of whether you need them. If the parts you don't need have their own dependencies you will need those to
bundles can be re-exported
although discouraged, allows the use of split packages, ie: a package that is spread across multiple bundles
can be used for non-code dependencies, eg: resources, Help etc.
Import-Package:
looser coupling, only the package (and version) is specified and the run-time finds the required bundle
Actual implementations can be swaped out
Dependent packages can be moved to different bundles by the package owner
But requires more metadata to be maintained (i.e: each package name) at lower levels of granularity
I believe Import-Package gives you looser coupling and should be preferred. I use it when declaring dependencies on packages that I don't own, such as slf4j, and I can swap implementations as I wish. I use Require-Bundle when the dependency is something I have control over, such as my own bundles, because any important change would have gone through myself anyway.
Avoid Import-Package.
As packages provide many-to-many relationships between bundles, they are prone to dependency cycles that are hard to detect and avoid.
Require-Bundle on the other hand, references a single bundle, making dependency graph protected from cycles by a trivial build-time check.
With Require-Bundle it is much easier to build layered architecture with isolated lower level of abstraction.
Import-Package should be better because, as previously said, you can move a package from one bundle to another without changing existing client's MANIFEST.MF
But...
There is a practical reason to use Require-Bundle if you are using Eclipse to develop your bundles:
Eclipse don't use packages as units of resolution. It uses bundles. That is, if you use one package of a bundle, Eclipse compiles your bundle without reporting any problem with the use of the rest of packages not imported from that bundle.
You could (you are human) think that everything is OK and upload your bundle for deployment but ... your bundle will break at runtime.
I'm sure about it because this problem has happened (to me!) today.
The good solution would be to change the Eclipse classpath container but... if this is not going to be done... you could decide to avoid this kind of problems requiring bundles, instead of packages, paying the mentioned price (no backward compatible code movement between bundles).
I'm not convinced that using Import-Package is better, because my default expectation when working with a bundle is to work with the associated public API. For that reason, Require-Bundle makes more sense.