Equinox Bundle import conflict - osgi

1) Bundle A reexports package com.X, which it gets from bundle C
2) Bundle B exports package com.X
3) Now bunlde D has dependency on both A and B.
From where will the bundle D get the package com.X from?

The first question is why you have 2 bundles defining the same package - this is called split packages and isn't recommended because you can have problems with shadowing.
With Import-Package the runtime will pick either bundle A or B to resolve the package dependency and you can't control this directly (you can do various tricks like the Eclipse guys do by setting mandatory properties on the exports).
With Require-Bundle you'll end up with a merged com.X package, so you'll see the superset of types, but I'm not sure what happens if you have overlapping types.
The simplest thing is to avoid split packages in the first place.

Related

Exporting API from implementation bundle

Can someone explain to me the rationale behind BND's exporting api packages that a bundle doesn't have. It seems just wrong to me, it always (AFAICT) results in ClassNotFoundException
Especifically
bundle A contains com.foo.api.MyInterface
bundle B contains com.foo.impl.InterfaceImpl which implements MyInterface
So if I tell bnd to export package com.foo in bundle A he will correctly export com.foo.api, but if i tell export package com.foo in bundle B it will incorrectly export com.foo.api, so consumers of MyInterface get wired to bundle B and then at runtime get a ClassNotFoundException
Why doesn't BND guarantee that when a bundle exports a package it actually contains it??
I know that if I export com.foo.api and com.foo.impl instead of just com.foo then the problem is solved in my trivial example, but in our real system this is not really a posibility. Also, the actual question is why BND does that, not how to fix the problem. i already know workarounds to solve the problem
This is just a feature of the bnd syntax. Because bnd has the option to embed imported packages, it considers the whole classpath when determining Export-Package and Private-Package headers. Bnd doesn't make any distinction between classes which are physically local to a folder being processed and others elsewhere on the classpath.
So if your Export-Package statement encompasses packages bnd knows about, bnd will assume you want to embed and export them. I'm surprised you're seeing exceptions, though, since bnd should also physically include the exported classes in the packaged bundle.
Did you look in bundle B to see that com.foo.api is not in there? If you tell bnd that bundle B should export com.foo.api, then bnd will actually put that package in bundle B and then export it. bnd has the "power" to include classes from anywhere in the -buildpath in the generated bundle. I think you really don't want bundle B to include and export com.foo.api, so you should modify your bnd file to remove the export statement.
I have not yet worked with bnd directly but through the maven bundle plugin which should be fairly similar. What I found is that if you export a package you do not have then bnd will embed the classes of this package inside your bundle jar.
So at least when using the maven bundle plugin bundle B should have the interface embedded when the export of com.foo.api is defined. Not sure what happens when you define only the parent package as export but I would assume the same happens.
This automatic embedding can be quite useful if you want to deploy the api together with each bundle which I do not do regularly but which seems to be a kind of best practice in OSGi (for example for bundles that implement OSGi apis).

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

How two dependent bundles in osgi gets compiled

I am learning OSGi framework. What i found that Bundles consists of .class files and manifest file. Now suppose I have two bundles A and B and bundle B depends on bundle A's service. So while compiling bundle B i have to keep Bundle A's service class in the classpath of bundle B or have to keep it(Bundle A's service) inside bundle B so that compiler can compile. Now if we are doing so then how modularity is achieved and how the two bundles are independent. Please help.
Thanks
Use interfaces!!
You have in interface I. Class A implements I and therefore depends on it, both at compile time and at runtime. Class B uses an instance of I, which it obtains as an OSGi Service. Therefore B depends on I at compile time and at runtime.
Therefore neither A nor B has direct knowledge of each other. Implementation code is hidden, only the interface is visible and shared. And that's how modularity is achieved.
Manually doing dependency management is error prone. I suggest you use maven to manage the dependencies. In addition, maven-bundle-pluginIn could help you check import/export bundle at compile time; while maven-eclipse-plugin help you generate eclipse projects according to the dependency configuration in pom.

Setting OSGi import version restrictions dynamically?

I'm building an OSGi-based web application consisting of just two bundles for now. In one of them, I'm loading process instances from a process engine. Each process instance is supposed to correspond with a specific version of the other bundle, which it was initially assigned to.
For example, I would like to load one process instance in Bundle A and work with it using packages from bundle B in version 1.0. Afterwards, I would get hold of another process instance and work with it using packages from bundle B in version 2.0.
Do you see any way to achieve this functionality?
Thank you very much in advance!
Johannes
Bundle A can only be exposed to a single version of a package at any given point. So other than possibly just using reflection, Bundle A cannot use 2 versions of the same package exported by different versions of Bundle B.
Or, do you mean that the 2 versions of Bundle B implement that same package differently. Then Bundle A could see different implementation objects from the 2 versions of Bundle B because they appear to be the same interface type to Bundle A. If this is the case, then OSGi services are the best way for Bundle B to publish these objects for Bundle A to use.

OSGi classloader issues

I am very new to OSGi.
I am developing a plugin A (osgi bundle), suppose A which depends on libraries, suppose B-1.0 and C-1.0. Now If the library C-1.0 depends on library B-2.0 (Note: the different version of library B). So my plugin has two different versions of the library B in its classpath. Now, How can I handle this situation ?
As I am studying from last 4-5 days about OSGi that it creates a classloader for each plugin in the JIRA application, so that dependency version mismatch do not occur between plugins. But What would a developer do If a plugin itself needs two different versions of a library jar ?
Can I create two different classloaders in a single osgi bundle through OSGi, say one for package X and another one for package Y ?
Please help me in any of the above scenarios or point me to the right direction.
Thanks in advance.
Remember that bundles do not depend on other bundles!!
Bundles import packages that are exported by other bundles. (unless you have used Require-Bundle, but you should not). So to rephrase the scenario from your example:
Bundle A imports package org.foo. Bundle C exports package org.foo, and OSGi wires the import to the export. So far so good.
Bundle C also imports package org.bar. Bundle B 1.0 exports package org.bar. Therefore OSGi wires these together and everything is still fine.
Now... bundle A also imports package org.wibble. Bundle B 2.0 exports package org.wibble. This is fine as well! Bundles B 1.0 and B 2.0 are simply different bundles as far as OSGi is concerned, they can both be installed at the same time.
So when you look at the dependencies the way they actually work, you find that it's perfectly possible for A to import code that comes from two different versions of B. However there is a limitation. Consider the following:
Bundle D imports packages org.foo and org.bar v1.0 (yes, packages are versioned).
Bundle E exports package org.foo, which satisfies the import in D. Bundle E also imports package org.bar v2.0.
Some other bundles (say F v1 and F v2) export the 2 versions of the org.bar packages.
Actually this scenario can still work. D can import version 1.0 of package org.bar from somewhere, and E can import version 2.0 of package org.bar from somewhere else, at the same time as D is importing package org.foo from E. I personally find this pretty incredible! But it does not work if org.foo "uses" org.bar, where "uses" means that some types in org.bar are visible in the API of org.foo. In this case, bundle D would be exposed to 2 different copies org.bar, which is not allowed, so OSGi will prevent bundle D from running by not allowing it to enter RESOLVED or ACTIVE states.
In osgi bundle or plugin you'll have meta-inf flie which will define which classes you import if you pass extra agrument being the version=2.0 then it will use the class from B-2.0 if you don't specify anything then it'll resolve to one that is loaded by classloader first.
i.e.
import-package(C 1.0):
b.some.package; version="2.0" or b.some.package; version="[2.0,4.0)"
import-package(A 1.0):
b.some.package; version="1.0" or b.some.package; version="1.0"
Hope this helps
Anup
Since each OSGi bundle has its own classloader, there will be 4 bundles in the runtime, and also 4 classloaders (A, B-1.0, B-2.0, C-1.0).
You may have two copies of the same class included in B (one from 1.0 and another from 2.0). If you run this, you may simply run into a ClassCastException in the A code because two versions of B classes are not the same.
OSGi provides a "uses" clause to detect this type of situations early. For example, C may have a uses clauses like the following:
Export-Package: c.some.package;uses="b.some.package";version="1.0"
Import-Package: b.some.package;version="2.0"
In this case, you will have an earlier failure (while resolving A), known as a uses conflict, because C places a constraint for its consumer on an acceptable version of B.
Conceptually, the only way to fix this problem is to have consumers of B (A and C in this case) agree on the version of B.

Resources