Make sure bundles use same dependencies versions - maven

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.

Related

How the MANIFEST file is being generated in my AEM osgi bundle?

I created one maven project with aem-project-archetype version 13.
After installing the bundle to AEM, I am getting error in felix console that 3 of the imported bundles could not be resolved.
I am trying to find out that from where these are being included into my manifest file which is inside the target/MANIFEST folder.
so that i could modify the versions of the respective bundles.
the error i am geting in felix console, my bundle is in installed state, not active
org.apache.sling.api.resource,version=[2.10,3) -- Cannot be resolved
org.apache.sling.api.servlets,version=[2.2,3) -- Cannot be resolved
org.apache.sling.models.annotations,version=[1.4,2) -- Cannot be resolved
When you're developing AEM applications, the OSGI bundle (and Manifest) is typically generated via the Felix maven-bundle-plugin.
The plugin writes your package imports based on the java packages you import in your all of your Java code. If you are importing from a maven dependency, say Sling the version for that import will be the package version from Sling.
The issue you are having here could be one of two
The package you are importing does not exists in OSGI (AEM Instance)
There a mismatch between the version in your maven dependencies and the version in OSGI (AEM instance). Hence OSGI cannot resolve the version you are importing.
2. is likely the case because sling is always bundled with AEM.
What can you do to debug/fix?
You can go to http://localhost:4502/system/console/depfinder
and try your packages there to see what version is actually exported
in OSGI.
Check wha versions of your dependencies you have in your pom.xml
and make sure the OSGI version is within the range specified by the
manifest imports. You can use maven dependency tree to list all
your dependencies and their versions.
This specific to AEM, if you are using uber-jar make sure you are
using the correct version for the correct AEM instance you're
running.
Note that in manifest imports the range [2.10,3) means it accepts all versions between 2.10.0 and 3.0.0 but NOT including 3.0.0. In my experience, Maven bundle plugin will always write the range where the min is your maven dependency package version and the max is the next major version.
Changing the imports manually:
This is not recommended and has very specific use cases, but you could manually tell the bundle plugin what version to add to the imports. See import-package instruction in the bundle plugin docs
These imports are based on the code you compiled against. There are not some nasty little tidbits that are there to pester you. Their purpose is to verify that what you run against is compatible with what you compiled against. I assume that the runtime has a lower version than you require. This implies that your compile path as setup in Maven has later versions than your runtime. If you could run your code you would likely run into Class Not Found Exception or No Such Method errors.
And maybe not. But then you might have the worse situation that things are not correct (promises made during compilation might not be fulfilled) and problems might happen much later after damage has been done.
This stuff is there for a very good reason. They are like earth pin on plugs, they protect you.
How to fix this? Take a look at your dependencies. Your must ensure you compile against a version that is lower or equal then what is present in your runtime. You can first look at the versions in your POM. If these versions are not there then look at the compile path Maven uses.
Replacing the numbers in the manifest is like sawing off the earth pin on a plug because otherwise it won't fit in the wall ... bad idea.

How can I instruct a build to use their dependency version if they have one, else use the one in my artifact

I am creating a Java library in Maven that has a dependency on a third party library.
In case any consumer applications already use that third party library, I want their build to use theirs. However in case they don't already have it, I want them to use the one I packaged in my archive.
Is there a Maven way to have your cake and eat it too, so that I can tell consumer applications to use theirs if they have one, else use mine?
When multiple versions of an artifact are found maven will attempt to select the version. It uses the nearest definition from the dependency tree.
https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
http://techidiocy.com/maven-dependency-version-conflict-problem-and-resolution/
This means that if the project consuming your library specifies a different version that will be used. If the common dependency is specified by a transitive dependency the version closest to the root of the tree will be used.
You can also use version ranges to specify many versions that work for you library.
https://docs.oracle.com/middleware/1212/core/MAVEN/maven_version.htm#MAVEN402

OSGi bundle picks up the system bundle over the specified bundle

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

what is the relation between package and bundle version in OSGi

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.

Can an OSGi bundle or package depend on multiple versions of another bundle or package?

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.

Resources