Skip to content

fix(#733): report when a factory descriptor is provided by several bundles - #741

Open
oscerd wants to merge 1 commit into
apache:mainfrom
oscerd:fix/733-factoryfinder-diagnostics
Open

fix(#733): report when a factory descriptor is provided by several bundles#741
oscerd wants to merge 1 commit into
apache:mainfrom
oscerd:fix/733-factoryfinder-diagnostics

Conversation

@oscerd

@oscerd oscerd commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #733

What

getResource walks bundleContext.getBundles() in install order and takes the
first bundle with a matching descriptor, loading the class from that bundle's
classloader with no scoping to the requesting context's wiring. findClass
caches the result per key (addToClassMap), so whichever bundle happened to be
scanned first stays selected for the life of the finder.

With two versions of a bundle installed side by side — a normal state during a
rolling upgrade — install order silently decides which one supplies the factory.
An operator who has installed a patched bundle has no way to tell it is being
ignored.

How

The selection is unchanged. First bundle in install order still wins. What
is new is that the scan no longer breaks early, so when more than one bundle
provides the descriptor it logs at WARN which bundle was picked, that it was
picked only for being installed first, and which others also provide it:

Factory descriptor META-INF/services/org/apache/camel/some-factory is provided by
more than one bundle. Using the one from camel-foo/4.18.1 [42], selected only
because it is installed first; also provided by camel-foo/4.18.2 [77].

The list is only allocated when there actually is a clash, and findClass
caches per key, so this costs an already-O(bundles) scan not breaking early on
first resolution of each key.

Why not the wiring-scoped fix

#733 floats scoping selection to the requesting context's BundleWiring. I have
deliberately not done that here:

  • it changes the semantics of a core resolution path that has behaved this way
    for a long time, with a wide blast radius and no cheap way to prove no
    regression short of the full Pax Exam suite
  • factory-finder keys are internal Camel SPI names — they never come from
    message content — so there is no urgency that would justify that risk

Making the situation visible is the cheap half, and it is what tells us whether
the ambiguity happens in practice at all. Happy to follow up with the
wiring-scoped selection as a separate change if you want it.

Tests

OsgiFactoryFinderTest covers no-provider (returns null), single provider, and
several providers — the last asserting that install order still decides, so
the selection behaviour the rest of the resolution path depends on is pinned
against future edits to this method.

Tests run: 15, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

Note

BundleEntry goes from private static class to package-private. getResource
is public and returns it, so private was never actually restricting
anything; this just makes the type nameable from tests in the same package.


Claude Code on behalf of Andrea Cosentino

…ral bundles

getResource walks bundleContext.getBundles() in install order and takes
the first bundle with a matching descriptor, loading the class from that
bundle's classloader. findClass caches the result per key, so whichever
bundle happened to be scanned first stays selected for the life of the
finder. With two versions of a bundle installed side by side, which is a
normal state during a rolling upgrade, install order silently decides
which one supplies the factory, and an operator who installed a patched
bundle has no way to tell it is being ignored.

Keep the selection exactly as it is and report the ambiguity: finish the
scan, and if more than one bundle provided the descriptor log at WARN
which bundle was picked, that it was picked only for being installed
first, and which others also provide it.

Deliberately not changing the resolution semantics. Scoping selection to
the requesting context's bundle wiring is the fix the issue floats, but it
alters a core resolution path that has behaved this way for a long time,
and factory finder keys are internal Camel SPI names that never come from
message content, so there is no urgency to justify that risk. The
diagnostics make the situation visible first.

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.
@oscerd
oscerd requested review from essobedo and jbonofre August 24, 2026 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OsgiFactoryFinder: first-match-wins bundle scan is order-dependent and silently sticky

1 participant