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

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.

Related

Maven: Include 3rd party jar with bundled libraries that conflict with other dependencies

I've been looking for the answer to this for a few days and have turned up empty.
I'm devving a Confluence plugin that integrates with a 3rd party app. This 3rd party app has a nice REST API and they even provide a Java SDK (yay!). Except.....the Java SDK has bundled a version of Jersey (1.18) that conflicts with Confluence's forked version of Jersey (1.8-atlassian_15). The SDK was not released as a Maven jar (or at least there's no pom.xml included). There ARE other pom.xml in the jar's META-INF for the dependencies it uses, but the SDK itself is just released as a jar download by the vendor.
So as I've done in the past, I mvn install:install-file the sucker with my own groupId and artifactId, thinking it'd be fine. Intellij recognized the library, everything compiled nicely, and then I tried my test call to the REST API. This is when it threw an error that made it evident that there's a conflict between the versions.
SOOOO. Is there anyway to get around this? Can I "sandbox" the SDK jar in a way the executes code in its own deal without being exposed to the nasties of Confluence's builtin version of libraries the SDK uses? I have a feeling that even after the Jersey dependency is resolved (if that's even possible) there will be other issues....The SDK also bundles the specific version of Jackson, Swagger, etc. with it.
I attempted to decompile the jar and include the decompiled code in my project, but that just had all of issues I'd rather not deal with ever again.
I have reached out the SDK devs to see if they could release a more maven-friendly release, but I'm not hopeful this will be done at all, and even it is, their release cycle is much different than my own requirements (read: I need a solution now). This is my last-ditch effort before rewriting the REST client from scratch.
Can I "sandbox" the SDK jar in a way the executes code in its own deal without being exposed to the nasties of Confluence's builtin version of libraries the SDK uses?
Sure you can. Often-used way to do this is to use Maven shade plugin that transforms an existing jar to a shaded jar, using another package hierarchy and getting rid of the package naming conflict. See also the documentation about relocating packages. I suggest you use that - that's what I've done in cases like this (though I haven't done confluence plugin development, but it should be the same thing as with other platforms).

Embedding multiple version of the same OSGI dependency in the same content-package

Short version:
How can I configure the maven POM of a AEM / CQ5 content package so the package embed different versions of the same OSGI bundle ?
Long version:
I'm building a content package for AEM (CQ5). This content package embed direct and indirect osgi dependencies needed for the project.
I have an issue with one of the bundle (let's call it BundleX) who remain inactivated after the content-package is deployed because of an unresolved package import on a specific version of Google Guava. Guava is part of the osgi dependencies that I embed with the project. The problem is that different bundles are dependent on different versions of Guava.
So I have:
project-content-package:
MyBundle:
depends on Guava:15.0 ( I do need this version for some features)
BundleX
depends on Guava:r06 (r06 < 15.0. I don't have the control on this bundle)
When making my content package, I'd like to embed the two guava versions. As we know, OSGi supports multiple versions of packages deployed at the same time.
Until now, in my AEM content package pom configuration, I had:
<plugin>
<groupId>com.day.jcr.vault</groupId>
<artifactId>content-package-maven-plugin</artifactId>
<configuration>
<embeddeds combine.children="append">
<embedded>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<target>${cq.slingUrlSuffix}</target>
</embedded>
...
</embeddeds>
...
</configuration>
...
</plugin>
The version of the embedded dependency is determined by maven and of course is unique for this maven project.
So, to sum up, to solve by problem, I should:
embed multiple versions of Guava in osgi container. The only solution I see is creating another artifical content package pom just to upload the other Guava dependency. But it's far to be optimal since I'd like my main package to contain all embedded osgi dependencies.
override the version of the dependency of bundle2 from r06 to 15.0, so I need only Guava 15.0 in my container. Since I don't have the control on this bundle, I should override that from outside, but I have no idea if it's possible to do that.
In advance, thank you very much for your help !
The Apache Sling OSGi installer that AEM uses under the hood does not support installing several versions of the same (meaning having the same Bundle-SymbolicName) bundle. So just adding several versions of a bundle in a content package won't help, the highest bundle version always wins and the other ones are not installed.
You can install multiple versions using the standard OSGi tooling, like the webconsole.
A workaround is to repackage a bundle with your own symbolic name (like "foo.0.15" for V0.15) so that the Sling installer considers multiple versions as distinct bundles and installs them all.
In both cases those bundles to have the correct version ranges in their Export-Package statements, to avoid conflicts.

Make sure bundles use same dependencies versions

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.

OSGi - Is it possible to override a bundle's import package version using a fragment?

I have a bundle (jersey-server) that imports a package (org.objectweb.asm) with a resolution of optional and no version specified:
org.objectweb.asm;resolution:=optional
Currently, our application is deployed to Apache Karaf (using the Equinox framework), which exports a new version of this package (org.objectweb.asm), namely version 4.0. The problem I am attempting to solve is that since the jersey-server bundle does not specify a version for the package it is wiring to 4.0. However, the version of jersey-server I am using (1.12) is incompatible with this version. I have a 3.1 version of the package available in the container that I need the jersey-server bundle to wire to.
I have attempted to use a fragment to suit my needs, but it does not appear to be working. I don't fully understand how fragment import-package conflict resolution works in Equinox (or Felix) to know if what I'm trying to do is even possible. Perhaps there is another way?
No, fragments are additive only. I.e. they can add extra imports to their host bundles, but they cannot replace or remove the imports of the host.
The jersey-server bundle is simply broken and must be fixed.
I had a similar issue with pax-web, I created a "workaround" for it:
https://github.com/ops4j/org.ops4j.pax.web/tree/master/pax-web-features/xbean-fragment
it's available also through maven:
http://search.maven.org/#artifactdetails%7Corg.ops4j.pax.web%7Cxbean-fragment%7C3.0.0.M2%7Cbundle

How do I gradually migrate Netbeans wrappers of OSGi modules to direct dependencies on the OSGi modules for a Maven built, Netbeans 7.1 RCP?

We have a Maven built Netbeans 7.1 rcp application that successfully mixes OSGi modules (packaging: bundle) and Netbeans modules (packaging: nbm) by wrapping the OSGi modules in Netbeans module wrappers. We want to migrate from using these wrappers to using the OSGi modules directly to simplify the build. There are around 30 wrapper, osgi module pairs and I would like to tackle the removal of the wrappers, one module pair at a time.
However when we replace a specific wrapper dependency with its osgi dependency in a modules pom that depends on it, and use the
<useOSGiDependencies>true</useOSGiDependencies>
in the nbm-maven-plugin configuration. Any other dependencies to other wrappers suddenly fail to build with transitive dependencies not available at runtime error.
Project uses classes from transitive module [xxx] which will not be accessible at runtime.
-- where [xxx] is the name of the OSGi module.
Of course I can fix the build by replacing the wrapper dependencies with the OSGi modules they were wrapping, but that escalates the size of the migration task considerably.
Once Ive got a build by "fixing" these transient dependencies the OSGi module that I picked to migrate appears successfully in a new cluster “extra”.
But at runtime the rcp fails to find the other osgi modules because, I guess, elsewhere in the build, they are still being referenced through wrappers.
Is there any way these wrappers and direct OSGi dependencies can co-exist? Or do I have to migrate all of the Netbeans wrappers to OSGi in one go?
Many thanks,
Phil Wilkinson.
Looks like theres no way to do this one wrapper at a time, its all-or-nothing with useOSGiDependencies. :(
You can change them to useOSGiDependencies=true one module at a time.
A detailed step-by-step guide would probably be too long for an answer, so here are some general rules for this to work:
Use nbm-maven-plugin version >= 3.11. Lower version has some bugs.
NBM wrapper's OpenIDE-Module & OpenIDE-Module-Specification-Version must match the wrapped bundle's Bundle-SymbolicName & Bundle-Version respectively.
If the bundle has package versioning, that has to be copied into the NBM wrapper's MANIFEST.MF as Netigso-Export-Package.

Resources