Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC] Add support for Java Service Loader in the OSGi core framework #853

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

laeubi
Copy link
Member

@laeubi laeubi commented Mar 2, 2025

As discussed in the last spec call it would be very handy if the OSGi core framework would allow native support for Java Service Loader (SPI) without to require any special handling or metadata like ServiceLoader Mediator (SLM) that currently require bytecode weaving.

It is commonly used and a constant source of problems when used inside OSGi and beside SLM there are even other workarounds like the OSGi ResourceLoader especially in JakartaEE but this requires some brittle handling in the code of the bundle itself.

It is therefore the aim to actually have a simple and native integration in the OSGi core framework making use of how the SPI works and the internal knowledge of the OSGi Framework implementation that is usually not accessible to the outside.

This demonstrates such implementation in the following way:

  • Whenever resources are requested that starts with "META-INF/services/" then it is detected by the framework
  • The interface/class of the SPI is extracted and loaded by the bundle, and then checked if any other bundle is currently wired to this (e.g. by import package)
  • For all such bundles it is checking for the same resource to be present and return them as if they where part of the same bundle
  • Additionally all classnames mentioned in such resources are recorded and linked to such bundle
  • If later a classload occurs and nothing is found in the bundle itself as a last resort it checks this mapping if a SPI class has to be loaded from the foreign bundle

All this works without having both bundles to declare anything special or knwo of each other, the only connection is a compatible SPI type and the presence of the corresponding "META-INF/services/" and a SPI lookup performed.

See

FYI @tjwatson @fipro78

An example project can be found here:
osgi-core-service-loader-example.zip

As discussed in the last spec call it would be very handy if the OSGi
core framework would allow native support for Java Service Loader (SPI)
without to require any special handling or metadata like ServiceLoader
Mediator (SLM) that currently require bytecode weaving.

It is commonly used and a constant source of problems when used inside
OSGi and beside SLM there are even other workarounds like the OSGi
ResourceLoader especially in JakartaEE but this requires some brittle
handling in the code of the bundle itself.

It is therefore the aim to actually have a simple and native integration
in the OSGi core framework making use of how the SPI works and the
internal knowledge of the OSGi Framework implementation that is usually
not accessible to the outside.

This demonstrates such implementation in the following way:

- Whenever resources are requested that starts with "META-INF/services/"
then it is detected by the framework
- The interface/class of the SPI is extracted and loaded by the bundle,
and then checked if any other bundle is currently wired to this (e.g. by
import package)
- For all such bundles it is checking for the same resource to be
present and return them as if they where part of the same bundle
- Additionally all classnames mentioned in such resources are recorded
and linked to such bundle
- If later a classload occurs and nothing is found in the bundle itself
as a last resort it checks this mapping if a SPI class has to be loaded
from the foreign bundle

All this works without having both bundles to declare anything special
or knwo of each other, the only connection is a compatible SPI type and
the presence of the corresponding "META-INF/services/" and a SPI lookup
performed.
Copy link

github-actions bot commented Mar 2, 2025

Test Results

  669 files  ±0    669 suites  ±0   1h 21m 20s ⏱️ + 6m 25s
2 223 tests ±0  2 176 ✅ ±0   47 💤 ±0  0 ❌ ±0 
6 813 runs  ±0  6 670 ✅ ±0  143 💤 ±0  0 ❌ ±0 

Results for commit cc19a6e. ± Comparison against base commit 95c7e08.

@HannesWell
Copy link
Member

Isn't this 'just' #295 built into the framework and ignoring the service-loader capabilities and requirements to avoid the need to specify them?

@laeubi
Copy link
Member Author

laeubi commented Mar 3, 2025

Isn't this 'just' #295 built into the framework and ignoring the service-loader capabilities and requirements to avoid the need to specify them?

Yes, we have had some discussion in the last spec call if the OSGi-core specification should probably have direct support for the Java Service Loader but then it should:

  1. not require any bytceode waving (like aries-spyfly)
  2. not require any framework specific extensions (like Implement OSGi Service Loader Mediator Specification #295)
  3. not require any special headers, configuration or alike as it is often hard to convince third party library providers what "they need it here" and there is always a risk of it breaking later.

therefore I now baked this directly into the framework for demonstration purpose on how a framework implementation could use its internal knowledge to support the serviceloader natively.

If it would become part of the spec, then we of course can still implement it by a (default shipped) Equinox extension and that's also why I have not implemented it in detail, it should just show the concept used in #295

@merks
Copy link
Contributor

merks commented Mar 3, 2025

The problem being solved here really is a recurring hassle for me. This problem is especially challenging when Orbit needs to bundlize plain-old jars because who knows what inner workings are involved without deeply inspecting the source code. So I end up doing purely evil things like this:

For the batik case, downstream consumers were doing other nasty kinds of workarounds like providing a fragment to make the lookup work or using reflection to inject the registrations. Horrible! 😱 🤯

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.

3 participants