fix(#733): do not let an uninstalled bundle poison factory resolution (backport of #741) - #752
Merged
jbonofre merged 1 commit intoSep 7, 2026
Conversation
…lution (backport of apache#741) getResource walks bundleContext.getBundles() and returns the first bundle with a matching descriptor. getBundles() returns a snapshot, and per the OSGi spec Bundle.getEntry() throws IllegalStateException once a bundle has been uninstalled, so a concurrent feature:uninstall or bundle:update can make the scan throw on a bundle that has nothing to do with the factory being resolved. findClass calls getResource from inside DefaultFactoryFinder.addToClassMap, which catches Exception, stores it in classesNotFoundExceptions and rethrows it on every later lookup of the same key. That turns the transient race into a lasting failure: the key stays broken for the life of the context, until clear(). Skip bundles already in state UNINSTALLED, and treat an IllegalStateException from getEntry as "this bundle has no such entry" rather than letting it fail the whole scan. Selection is unchanged. The scan still stops at the first match, so install order still decides and there is no added cost on a path that is walked once per key. Log the bundle that supplied the descriptor at DEBUG: with several providers installed, that is what lets an operator tell which one is actually in use. Deliberately not warning when several bundles provide the same descriptor. The default camel-core feature installs both camel-xml-io and camel-xml-jaxb, and both ship META-INF/services/org/apache/camel/modelxml-dumper with a different implementation class, so a stock install would warn on every startup. That ambiguity is inherited from flat-classpath Camel, where classpath order picks the winner in the same way. Not addressed here: the rolling-upgrade half of apache#733. Once a key resolves, addToClassMap short-circuits on classMap and getResource is never called again for it, so a patched bundle installed afterwards is still silently ignored by an already-running context. Catching that needs cache invalidation on bundle events, in the spirit of OsgiTypeConverter revalidating its delegate on service changes, and is a separate change with its own blast radius. BundleEntry becomes package private. It was private while getResource, which returns it, is public, so the modifier was not restricting anything; this makes it reachable from the tests in the package.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backport of #741 to
camel-karaf-4.18.x. Fixes #733 on that branch.What
OsgiFactoryFinder.getResourcewalksbundleContext.getBundles()and returnsthe first bundle with a matching factory descriptor.
getBundles()returns a snapshot, and per the OSGi specBundle.getEntry()throws
IllegalStateExceptiononce a bundle has been uninstalled. So afeature:uninstallorbundle:updaterunning concurrently with a resolutioncan make the scan throw on a bundle that has nothing to do with the factory
being looked up.
What turns that transient race into a lasting failure is the caching in Camel.
findClasscallsgetResourcefrom insideDefaultFactoryFinder.addToClassMap,whose mapping function stores the exception in
classesNotFoundExceptionsandrethrows it on every later lookup of the same key.
IllegalStateExceptionis anException, so the key stays broken for the life of the context, untilclear().How
Bundle.UNINSTALLED.IllegalStateExceptionfromgetEntryas "this bundle has no suchentry" and continue, rather than letting it fail the whole scan. This covers
the bundle that is uninstalled between the state check and the call.
providers installed can tell which one is actually in use.
Selection is unchanged. The scan still
breaks at the first match, soinstall order still decides and there is no added cost on a path that is walked
once per key.
Cherry-pick
Clean cherry-pick of e784cd9, no conflicts and
no adaptation needed —
OsgiFactoryFinder.javawas identical on both branches.Tests
OsgiFactoryFinderTest(new): no provider, single provider, first-match-winswith several providers, an
UNINSTALLEDbundle never being asked for an entry,and a bundle uninstalled mid-scan not failing the lookup.