Finding why OBR considers a bundle to be required - osgi

Is there a simple way to understand why Bndtools OBR resolution has decided it needs a particular bundle to satisfy the run requirements? In my particular case, I have all org.slf4j.* packages in -runsystempackages, but it still insists on including slf4j-api.

Are there bundle fragments involved? Fragments need to be installed into a host bundle, and in the case of SLF4J, the static binding mechanism is expecting to be installed into the slf4j.api bundle.

Related

OSGi - modify a class in a bundle without modifying the original bundle

I have a need to modify a class in a OSGi bundle. But this bundle comes from an upstream project hence I cannot(/won't) touch it. But I have to modify a class in it to match my project's requirements. So, I'm looking for a solution for this.
So, I created my own bundle which has the patched classes. But there is a complication since the upstream OSGi bundle, and the patch OSGi bundle I created has the same packages. I want to over-ride the classes in the upstream OSGi bundle, without touching the it.
I looked at the osgi.org Fragment page. It suggests a way to patch a host bundle by using a fragment bundle, but for that I have to modify the upstream project bundle's MANIFEST.MF to set Bundle-ClassPath: patch.jar,.
Any suggestions?
You can implement a WeavingHook and catch the class loading of that bundle.
However, do it only if you do not have any other chance to solve your problem. If you have any chance to contribute to that project that contains the jar, send a patch for them and have this only as a temporary solution. With hacks like this, your system will be chaotic for others.

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.

What is the intended use case for Bundle-Classpath in OSGI bundles

I am trying to understand the intended use case for Bundle-Classpath in OSGI bundles.
Here is my understanding, please help me understand if this is correct.
Let's say I am working on creating an OSGI bundle which will be deployed in an ecosystem of other bundles. The bundle I am working on needs some other bundles, but they are not loaded/exported in this ecosystem, and I do not have control on what the ecosystem exports. In such a scenario, I can put these bundles inside some directory (say 'lib') which becomes part of my bundle. These bundles should also be referenced from the Bundle-Classpath, so they can be loaded.
Is this a correct use case for Bundle-Classpath ?
Will these additional bundles also be loaded in the OSGI container and will packages exported by them be available to other bundles ?
Bundle-ClassPath is intended for including dependencies in our bundle, so that our bundle can be deployed standalone.
Let's take an example. Suppose the code in my bundle uses a library, e.g. Google Guava. I have two choices for packaging my bundle:
Simply create my bundle with only my own code inside it. The bundle will now have the Import-Package statements that declare a dependency on Guava, and anybody who wants to deploy my bundle into his application will also have to deploy Guava.
Alternatively I can include a copy of Guava inside my bundle and reference it from my Bundle-ClassPath. Whoever deploys my bundle can deploy just my bundle, and doesn't need to worry about where to get Guava from. In fact, the existence of Guava inside my bundle is an implementation detail, and the deployer doesn't even need to know that I am using it.
The choice between these two options is a trade-off. Option 2 has the advantage that my bundle is easier to deploy because it is standalone -- everything it needs is right there inside it. On the other hand my bundle is much bigger than it needs to be, which could become a problem if lots of other bundles also embed their own copy of Guava.
A more severe problem with option 2 is that all of the dependencies of the library now become my dependencies as well. Actually Guava is a rare example of a Java library with no dependencies of its own... but many other Java libraries drag in a huge tree of transitive dependencies. If you use this approach with, say, Hibernate then your own bundle will also have that large dependency set. This gets very ugly, very quickly.
So, you should be cautious not to overuse Bundle-ClassPath/Embed-Dependency. You should only consider using it if the dependency is (a) small, and with no transitive dependencies, and (b) your bundle uses the library as an internal implementation detail, i.e. it is not part of your public API.
UPDATE
I forgot to answer your second question about the exports. The answer is NO, the exports of any "bundles" you put on your Bundle-ClassPath will NOT become exports of your own bundle. In fact the JARs we put on Bundle-ClassPath are not treated as bundles at all, they are just JARs.
You can choose to export packages that come from within the JARs on your Bundle-ClassPath but you have to do this in the MANIFEST.MF of your own bundle.
The most common use case for this header is the packaging of external libraries. Let's say you have some library foo.jar, and want to use its classes in your bundle.
You put the jar into your bundle like so,
/
com/company/Activator.class
foo.jar
META-INF/MANIFEST.MF
In you manifest, you can now use
Bundle-ClassPath: foo.jar,.
Remember to include the . on the classpath, or you will not be able to find the classes in your bundle.
When classes are on the Bundle-ClassPath, you can use them like any other class: use them in your code, or export them.
I think you may be a bit off here.
A Bundle-Classpath is an ordered, comma-separated list of relative
bundle JAR file locations to be searched for class and resource
requests.
What this means is that when some bundle class needs another class in
the same bundle, the entire bundle class path of the containing bundle
is searched to find the class.
From OSGI in Action.
Let's consider a concrete case. Imagine a bundle (JAR file) with the following structure:
src/a/A.class
src2/b/B.class
src3/c/C.class
if you wanted a.A, b.B and c.C to be available to each other, you'd have to define src, src2 and src3 as pertaining to the bundle classpath. That would mean you'd have to add to your manifest file the following line:
Bundle-ClassPath: src,src2,src3

Does the OSGi Bundle.update() method also download new dependencies

I have a similar goal as the person here: Using OSGi to implement auto-update
Essentially, I want to be able to update bundles in my applications automatically. The post above mentions that you can just call Bundle.Update(), but will that also download new dependencies if some have been added since the previous version? If so, where does it look for them?
Furthermore, what differentiates this solution with provisioning management tools, as discuses here: What's the difference between features in Karaf and OBR
That is, why would you use something like OBR vs. this Bundle.update() method.
No, bundle.update() only updates the individual bundle that you updated, it never does anything to any other bundles. Therefore it's certainly possible that, after the update, the bundle will no longer resolve due to missing dependencies. So this is exactly why you should use something higher-level like OBR or Karaf etc versus bundle.update().

When should I use Import-Package and when should I use Require-Bundle?

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.

Resources