Difference between Import-Package resolution:=optional and no dependency at all - osgi

I have read here to use the optional resolution within the "Import-Package" directive carefully, but don't really understand why. Also what is the difference between optional and not specifying a dependency at all?

If you fail to specify an Import-Package at all, then your bundle cannot access the package even if it available and exported by some other bundle.
If you import the package optionally, then your bundle can use it if available, but that package not being available does not prevent resolution of your bundle.
You should avoid optional dependencies at the package level. Either you depend on a package or you don't. Normally a need for optional dependencies indicates that your bundle has poor coherency, i.e. it's doing too many unrelated things within one module.

If you say it is optional, than it might occur if it is exported from some other bundle. If you do not import it at all, it will be never available for the bundle.
So, for what do you need it?
If you really have an optional part in your bundle, you can import the optional dependencies. However, you have to check if they are there when you use it.
OSGi will not complain if an optional import can not be found, but the JVM will complain if you bundles uses something which is not imported at all.

Related

Maven bundle plugin, what is the difference between <Private-Package> and embedding dependency?

Here is the reference on Maven Bundle Plugin. I've read it, and concluded, that if I want to use some other package inside my bundle and I want to keep this as an implementation detail, I don't want to import or export this dependency, than I shall use Private-Package instruction. And, there is a possibility of embedding dependencies, which is described here. So, my question - isn't it the same, and if not - what is the difference?

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

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 difference between "Import-Bundle" and "Require-Bundle"?

What is the difference between spring source dm server specific Import-Bundle and OSGi's Require-Bundle?
I am confused whether to use Import-Bundle or Require-Bundle in my project.
Import-Bundle is similar to Require-Bundle, it creates a complete dependency on the other bundle, including that bundle's dependencies. This transitivity is bad because you have no idea what you depend, creating the infamous "big ball of mud" problem we're so familiar with in Object oriented programming.
In OO, we've found a solution to this entanglement by using interfaces, they separate implementation from specification. OSGi is built around a similar albeit of an higher order concept of service contracts. These contracts (interfaces, permissions, helper classes) are stored in a package. In contract based programming you depend on the contracts, not the implementations. Ergo, an OSGi bundle should depend on packages since they represent the contracts.
Import-Package <=> interface
Import-Bundle/Require-Bundle <=> implementation class
Import-Bundle is NOT OSGi, it is a proprietary Spring extension. It is a cleaner form for Require-Bundle; the uncleanliness was necessary to support some Eclipse use cases. The OSGi decided not to adopt this header since the Require-Bundle/Import-Bundle is fundamentally broken if you want to build systems from components.
Ideally you should try to rather use Import-Package instead. It makes you bundles less dependent on each other. It also allows to show that you only depend on a part of a bundle. This is also important for managing versions. In OSGi you can define the versions of exported packages independent of the bundle version. So you can make sure you only change versions of an API if it really changes. This can make your app much more manageable.
It's explained here at SpringSource
So summarizing: Import-Bundle will import all exported packages of a certain bundle, it will resolve that when deploying, while Require-Bundle really requires a bundle with that type, and that relationship stays that way during runtime.
Normally they would behave pretty much the same. For example it can be different when:
You have 'split packages': packages that exist in multiple bundles, you might 'lose' dependencies with Import-Package / Import-Bundle that you can only express with Require-Bundle (Note that you really should avoid split packages if you can)
I think the Bundle->Package resolution is when you deploy that bundle. If you redeploy the bundle with the exported bundles to a version with different export, I don't think the bundle will notice. To be honest I'm not exactly sure about this one.
All in all, I'd say stick with the OSGi standard: Import-Package or Require-Bundle if you really need it. You'll have a bit more headers but you'll so many more options in the long run.

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