fix(exposition): export internal package for OSGi resolution - #2415
fix(exposition): export internal package for OSGi resolution#2415arimu1 wants to merge 2 commits into
Conversation
prometheus-metrics-exposition-textformats imports io.prometheus.metrics.expositionformats.internal because PrometheusProtobufWriter loads the protobuf implementation via Class.forName, but the exposition-formats bundles kept that package private. Export it via bnd _exportcontents so OSGi can wire the two bundles together. Signed-off-by: arimu1 <19286898+arimu1@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes OSGi bundle resolution for prometheus-metrics-exposition-textformats by ensuring the implementation package io.prometheus.metrics.expositionformats.internal is exported from the exposition-formats bundles, matching the wiring expected by PrometheusProtobufWriter’s reflective Class.forName loading approach.
Changes:
- Adds bnd (
maven-bundle-plugin) instructions to exportio.prometheus.metrics.expositionformats.internalvia_exportcontentsfor OSGi wiring. - Keeps
io.prometheus.metrics.expositionformats.generated*exported explicitly to preserve existing generated exports. - Applies the same export behavior to both the “formats” and “no-protobuf” bundle-producing modules.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| prometheus-metrics-exposition-formats/pom.xml | Exports the internal implementation package for OSGi resolution while preserving generated package exports. |
| prometheus-metrics-exposition-formats-shaded/pom.xml | Mirrors the same OSGi export instructions for the shaded formats bundle so consumers can resolve the internal package consistently. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
jaydeluca
left a comment
There was a problem hiding this comment.
I'm not super familiar with OSGI, so I would like to see some tests or other evidence that this solves the issue if possible
| <Export-Package> | ||
| io.prometheus.metrics.expositionformats.generated*;version="${project.version}" | ||
| </Export-Package> | ||
| <_exportcontents> | ||
| io.prometheus.metrics.expositionformats.internal;version="${project.version}" | ||
| </_exportcontents> |
There was a problem hiding this comment.
${project.version} is 1.8.1-SNAPSHOT on snapshot builds, which isn't valid OSGi version
syntax, so bnd rewrites it to 1.8.1.SNAPSHOT. That sorts below a released 1.8.1. Built both
branches to confirm the delta:
main: io.prometheus.metrics.expositionformats.generated;version="1.8.1"
branch: io.prometheus.metrics.expositionformats.generated;version="1.8.1.SNAPSHOT"
bnd already defaults exported-package versions to the cleaned project version, so on a release
build the attribute produces exactly what you'd get without it — it only changes behavior on
snapshots, and only for the worse. Dropping it restores 1.8.1 (verified locally).
| <Export-Package> | |
| io.prometheus.metrics.expositionformats.generated*;version="${project.version}" | |
| </Export-Package> | |
| <_exportcontents> | |
| io.prometheus.metrics.expositionformats.internal;version="${project.version}" | |
| </_exportcontents> | |
| <Export-Package> | |
| io.prometheus.metrics.expositionformats.generated* | |
| </Export-Package> | |
| <_exportcontents> | |
| io.prometheus.metrics.expositionformats.internal | |
| </_exportcontents> |
Same applies to prometheus-metrics-exposition-formats-shaded/pom.xml
| <_exportcontents> | ||
| io.prometheus.metrics.expositionformats.internal;version="${project.version}" | ||
| </_exportcontents> | ||
| </instructions> |
There was a problem hiding this comment.
While verifying this fix I hit a second manifest problem in this bundle that's pre-existing, but
it matters for whether #2395 is actually resolved.
maven-bundle-plugin runs before maven-shade-plugin, so bnd computes the manifest against
unrelocated classes. On both main and this branch the shaded bundle declares:
Import-Package: com.google.protobuf;version="[4.36,5)" ← mandatory
…while the jar contains zero com/google/protobuf/ classes. I decompiled
internal/ProtobufUtil.class out of the built shaded jar and it references
io/prometheus/metrics/shaded/com_google_protobuf_4_36_0/*, as you'd expect. So the shaded
artifact demands a real protobuf-java bundle be installed, which rather defeats the point of
shading.
Why this blocks the original report: micrometer-registry-prometheus depends on
io.prometheus:prometheus-metrics-exposition-formats — the shaded artifact — at runtime scope. So
the bnd resolve in #2395 will get past internal after this fix and then stop at
com.google.protobuf unless protobuf-java happens to be in their resolution repository.
Second-order effect from this PR specifically: the newly exported internal package inherits
uses:="com.google.protobuf,...", propagating the bogus constraint to every consumer that wires
to it.
Adding !com.google.protobuf removes both the phantom import and the bad uses clause. Verified
against a local build:
| </instructions> | |
| <Import-Package>!com.google.protobuf,*</Import-Package> | |
| </instructions> |
Resulting manifest:
Export-Package: ...internal;version="1.8.1";uses:="io.prometheus.metrics.config,..."
↑ no com.google.protobuf
Import-Package: io.prometheus.metrics.config;version="[1.8,2)",...
↑ com.google.protobuf gone
with all that being said, are there some tests we can include to verify that all of this actually works and fixes the issue? I have limited experience with OSGi, so I defer to you and @anjeongkyun to hopefully weigh in
| <artifactId>maven-bundle-plugin</artifactId> | ||
| <configuration> | ||
| <instructions> | ||
| <!-- Required for OSGi: textformats loads protobuf impl via Class.forName --> |
There was a problem hiding this comment.
This comment is the right diagnosis, and it points at the half of the problem that this PR doesn't fix.
createProtobufWriter() is explicitly written to tolerate the impl being absent — it catches, returns null, and isAvailable() reports false (prometheus-metrics-exposition-textformats/.../PrometheusProtobufWriter.java:26-37). But bnd emits the import as mandatory:
Import-Package: ... io.prometheus.metrics.expositionformats.internal ← no resolution:=optional
So a deployment that installs only prometheus-metrics-exposition-textformats — which is the
entire reason that artifact ships separately from the protobuf-carrying ones — still fails to
resolve after this change. Exporting internal fixes the case where the formats bundle is
present; it doesn't fix the case where it deliberately isn't.
One line in prometheus-metrics-exposition-textformats/pom.xml, in a maven-bundle-plugin
<instructions> block mirroring the ones you've added here:
<Import-Package>io.prometheus.metrics.expositionformats.internal;resolution:=optional,*</Import-Package>I built this and confirmed the manifest:
Import-Package: io.prometheus.metrics.expositionformats.internal;resolution:=optional,
io.prometheus.metrics.config;version="[1.8,2)",...
There was a problem hiding this comment.
also, the comment sits above <Export-Package> but explains <_exportcontents>
Fixes #2395
Summary
io.prometheus.metrics.expositionformats.internalfrom bothprometheus-metrics-exposition-formatsandprometheus-metrics-exposition-formats-no-protobufOSGi bundlesprometheus-metrics-exposition-textformatsimports that package becausePrometheusProtobufWriterloadsPrometheusProtobufWriterImplviaClass.forName, but bnd kept the implementation package private in the formats bundles, so OSGi resolution failedApproach
Used bnd
_exportcontentsto export the otherwise-private internal package while preserving existinggenerated*exports. This matches the metadata wiring fix suggested in the issue (option 1) without moving implementation classes across bundle boundaries.Prove-it (before fix on HEAD)
After fix
Both formats bundles now export
io.prometheus.metrics.expositionformats.internal, satisfying the textformats import.Test plan
mvn -pl prometheus-metrics-exposition-formats,prometheus-metrics-exposition-formats-shaded,prometheus-metrics-exposition-textformats -am test(Java 25)META-INF/MANIFEST.MFin built JARs to confirm internal package is exported and generated exports remainMade with Cursor