I have a strange problem with class resolution that is befuddling me.
I have a bundle A that uses services from other bundles B and C that implement an interface I. Bundle A also uses classes from bundle D. I am using blueprint for bundle A and have a reference-list defined as such:
<reference-list id="svcs" interface="com.foo.interfaces.I" member-type="service-object" availability="optional"/>
And this is injected into a bean instantiated from a class found in bundle D, the bean itself registered as a service.
When I start Karaf (4.1.1) all bundles start just fine and everything works. The services implementing interface I from bundles B and C are all in the reference-list as expected. And I am able to restart bundle A and everything comes back up fine.
However, if I do something as simple as refreshing bundle D, bundle A fails to start indicating a ClassNotFoundException for interface I. Specifically:
Unable to start blueprint container for bundle com.foo.a.service/10.0.0.SNAPSHOT
org.osgi.service.blueprint.container.ComponentDefinitionException: org.osgi.service.blueprint.container.ComponentDefinitionException: Unable to load class com.foo.a.Service from recipe BeanRecipe[name='serviceA']
Caused by: java.lang.ClassNotFoundException: com.foo.interfaces.I not found by com.foo.a.service 
However, if I look at the headers for bundle A (#401), it shows the import of the package interface I is in is resolved, e.g. the following line in the imports is in black:
If the headers indicates the import is resolved ok, and at Karaf startup that import found the interface I, why would it fail when bundle D is refreshed?
Yes, I'm aware that if com.foo.interfaces was exported from another bundle that could cause problems, and I'm double checking everything to make sure that isn't the case, but I don't think there is another export of it.
How can I diagnose what where bundle A has resolved that import from and why it thinks interface I cannot be found? bundle:diag just reports the same stack trace I showed above.
Using an earlier install of more of less the same bundles (but not completely identical, so yes, that is a variable) in Karaf 3.0.6, everything works fine. I can restart bundle A and refresh bundle D and bundle A finds the interface I and starts fine. Did something change with Karaf 4 or the blueprint used by it?
This problem occurs because you have incorrectly marked the import for the “com.foo.interfaces” package as “resolution:=optional”. This package is *not* optional as a type from it is is injected into your blueprint bean which in turn means that the type must be loadable for your bundle to start.
When you mark a package import as optional then the OSGi framework is at liberty to not wire this package when resolving your bundle. In this case the refresh operation is either making the package that you want unavailable, or incompatible with the rest of your class space. Either way optional package imports are very difficult to do correctly, and usually require you to segregate a whole code path. You must then load this code path defensively using a try/catch ClassNotFoundException and be prepared for it not to exist.
Note that optional services are much easier to deal with than optional packages. In your case it will simply result in an empty reference list.
See also, this talk on optionality in OSGi that I gave several years ago. Sadly blueprint 1.1 never really happened as nobody was sufficiently interested in updating the standard.
Thanks for the catch. I'm dealing with some third party libs that aren't OSGi bundles and having to do stuff with declaring things optional and this one slipped into the mix.
I was not aware the framework could not wire the package when resolving the bundle even if the package is present and exported. It is also confusing that the framework reports it has been resolved. That reporting resolved when not isn't a bug? This is what was confusing.
And yes, it really is the optional service I care about.
Thanks for the link to the talk as well. A good refresher.
On Mon, Sep 4, 2017 at 1:51 AM, Timothy Ward <[hidden email]> wrote:
This is true only for optional imports and can occur if the exported package has an incompatible version, or if a uses constraint prevents the class space from being compatible.
When a bundle has been resolved it means that the OSGi framework has been able to find a valid wiring for all of the mandatory package imports for that bundle, optional imports may, or may not also have been wired, but they are optional so they must not prevent resolution from succeeding. Once this process has completed then your bundle is RESOLVED and able to load classes. A resolved wiring will not change* until the bundle is refreshed or uninstalled. You can introspect the wiring of a bundle using the bundle wiring API.
*There are a couple of ways in which package wires can be added dynamically at runtime, but no wires can ever be changed or removed.
|Free forum by Nabble||Edit this page|