Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,31 @@ abstract class OtelJavaExtension {
// with resolution:=optional but no version constraint.
abstract val osgiUnversionedOptionalPackages: ListProperty<String>

// SPI interfaces whose implementations this bundle registers via META-INF/services.
// Generates Provide-Capability: osgi.serviceloader;... and requires the registrar extender.
abstract val osgiServiceLoaderProvides: ListProperty<String>

// SPI interfaces this bundle discovers at runtime via ServiceLoader.
// Generates Require-Capability: osgi.serviceloader;... (resolution:=optional) to hint the
// BND resolver to include provider bundles. Does NOT add the processor extender requirement —
// use osgiServiceLoaderProcessor for that.
abstract val osgiServiceLoaderRequires: ListProperty<String>

// When true, adds Require-Capability: osgi.extender=osgi.serviceloader.processor so that a
// ServiceLoader mediator (e.g. SPI Fly) weaves this bundle's ServiceLoader.load() calls to
// route through the OSGi service registry. Set this on whichever bundle contains the actual
// ServiceLoader.load() call site; the SPI types being loaded live elsewhere.
abstract val osgiServiceLoaderProcessor: Property<Boolean>

abstract val minJavaVersionSupported: Property<JavaVersion>

init {
minJavaVersionSupported.convention(JavaVersion.VERSION_1_8)
osgiEnabled.convention(true)
osgiOptionalPackages.convention(emptyList<String>())
osgiUnversionedOptionalPackages.convention(emptyList<String>())
osgiOptionalPackages.convention(emptyList())
osgiUnversionedOptionalPackages.convention(emptyList())
osgiServiceLoaderProvides.convention(emptyList())
osgiServiceLoaderRequires.convention(emptyList())
osgiServiceLoaderProcessor.convention(false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ if (!project.hasProperty("otel.release") && !project.name.startsWith("bom")) {
packageExcludes.addAll(
"*.internal",
"*.internal.*",
"io.opentelemetry.internal.shaded.jctools.*",
"io.opentelemetry.sdk.trace.internal.shaded.jctools.*",
// Temporarily suppress warnings from public generated classes from :sdk-extensions:jaeger-remote-sampler
"io.opentelemetry.sdk.extension.trace.jaeger.proto.api_v2"
)
Expand Down
41 changes: 36 additions & 5 deletions buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,43 @@ tasks {
val unversionedImports = unversionedOptionalPackages.joinToString(",") { "$it.*;resolution:=optional" }
val fullImportPackages = if (unversionedImports.isNotEmpty()) "$unversionedImports,$importPackages" else importPackages

bnd(mapOf(
// Exclude shaded internal packages from exports; they are implementation details and
// should not be part of the OSGi bundle's public API surface.
"-exportcontents" to "!io.opentelemetry.internal.shaded.*,io.opentelemetry.*",
val bndInstructions = mutableMapOf(
"-exportcontents" to "io.opentelemetry.*",
"Import-Package" to fullImportPackages
))
)

// OSGi ServiceLoader Mediator capabilities.
// Providers declare what SPI implementations they register via META-INF/services.
// Consumers declare what SPI interfaces they discover at runtime via ServiceLoader.
// Both require the corresponding extender from a ServiceLoader mediator (e.g. SPI Fly).
val slProvides = otelJava.osgiServiceLoaderProvides.get()
val slRequires = otelJava.osgiServiceLoaderRequires.get()
val requireClauses = mutableListOf<String>()

if (slProvides.isNotEmpty()) {
bndInstructions["Provide-Capability"] = slProvides.joinToString(",") {
"osgi.serviceloader;osgi.serviceloader=\"$it\""
}
requireClauses.add("osgi.extender;filter:=\"(osgi.extender=osgi.serviceloader.registrar)\"")
}
if (slRequires.isNotEmpty()) {
slRequires.forEach {
// resolution:=optional: hints the BND resolver to include provider bundles.
// Does not add the processor extender — use osgiServiceLoaderProcessor for that.
requireClauses.add("osgi.serviceloader;filter:=\"(osgi.serviceloader=$it)\";cardinality:=multiple;resolution:=optional")
}
}
if (otelJava.osgiServiceLoaderProcessor.get()) {
// Mandatory: actively pulls a ServiceLoader processor (e.g. SPI Fly) into the resolved
// bundle set so that ServiceLoader.load() calls are routed via the OSGi service registry.
// Set on whichever bundle contains the ServiceLoader.load() call site.
requireClauses.add("osgi.extender;filter:=\"(osgi.extender=osgi.serviceloader.processor)\"")
}
if (requireClauses.isNotEmpty()) {
bndInstructions["Require-Capability"] = requireClauses.joinToString(",")
}

bnd(bndInstructions)
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ plugins {

description = "OpenTelemetry API Common"
otelJava.moduleName.set("io.opentelemetry.common")
// ServiceLoaderComponentLoader (this bundle) is the ServiceLoader.load() call site for all
// ComponentLoader.forClassLoader() usage; requires the processor extender to weave it.
otelJava.osgiServiceLoaderProcessor.set(true)

dependencies {
}
1 change: 1 addition & 0 deletions dependencyManagement/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ val DEPENDENCIES = listOf(
"io.opentracing:opentracing-noop:0.33.0",
"junit:junit:4.13.2",
"nl.jqno.equalsverifier:equalsverifier:3.19.4",
"org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle:1.3.7",
"org.apache.felix:org.apache.felix.framework:7.0.5",
"org.awaitility:awaitility:4.3.0",
"org.codehaus.mojo:animal-sniffer-annotations:1.27",
Expand Down
8 changes: 8 additions & 0 deletions exporters/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ plugins {

description = "OpenTelemetry Exporter Common"
otelJava.moduleName.set("io.opentelemetry.exporter.internal")
otelJava.osgiOptionalPackages.set(listOf("com.fasterxml.jackson.core", "com.google.common.io", "io.opentelemetry.api.incubator.config"))
// sun.misc, io.grpc, and org.jspecify are not OSGi bundles and have no package versioning; must use unversioned optional.
otelJava.osgiUnversionedOptionalPackages.set(listOf("sun.misc", "io.grpc", "org.jspecify.annotations"))
// This bundle's exporters load sender implementations via SPI.
otelJava.osgiServiceLoaderRequires.set(listOf(
"io.opentelemetry.sdk.common.export.GrpcSenderProvider",
"io.opentelemetry.sdk.common.export.HttpSenderProvider"
))

java {
sourceSets {
Expand Down
3 changes: 3 additions & 0 deletions exporters/otlp/all/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ apply<io.opentelemetry.gradle.OtelVersionClassPlugin>()

description = "OpenTelemetry Protocol (OTLP) Exporters"
otelJava.moduleName.set("io.opentelemetry.exporter.otlp")
otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator.config"))
// io.grpc is not an OSGi bundle and has no package versioning; must use unversioned optional.
otelJava.osgiUnversionedOptionalPackages.set(listOf("io.grpc"))
base.archivesName.set("opentelemetry-exporter-otlp")

dependencies {
Expand Down
1 change: 1 addition & 0 deletions exporters/otlp/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ plugins {

description = "OpenTelemetry Protocol Exporter"
otelJava.moduleName.set("io.opentelemetry.exporter.internal.otlp")
otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator"))

val versions: Map<String, String> by project
dependencies {
Expand Down
1 change: 1 addition & 0 deletions exporters/sender/jdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugins {
description = "OpenTelemetry JDK HttpSender"
otelJava.moduleName.set("io.opentelemetry.exporter.sender.jdk.internal")
otelJava.minJavaVersionSupported.set(JavaVersion.VERSION_11)
otelJava.osgiServiceLoaderProvides.set(listOf("io.opentelemetry.sdk.common.export.HttpSenderProvider"))

dependencies {
annotationProcessor("com.google.auto.value:auto-value")
Expand Down
7 changes: 7 additions & 0 deletions exporters/sender/okhttp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ plugins {

description = "OpenTelemetry OkHttp Senders"
otelJava.moduleName.set("io.opentelemetry.exporter.sender.okhttp.internal")
otelJava.osgiServiceLoaderProvides.set(listOf(
"io.opentelemetry.sdk.common.export.GrpcSenderProvider",
"io.opentelemetry.sdk.common.export.HttpSenderProvider"
))
// okhttp3, okio, and org.jspecify.annotations are not OSGi bundles; imports must be optional.
// (org.jspecify.annotations is pulled in by OkHttp's Kotlin-compiled types, not this bundle's code.)
otelJava.osgiUnversionedOptionalPackages.set(listOf("okhttp3", "okio", "org.jspecify.annotations"))

dependencies {
implementation(project(":exporters:common"))
Expand Down
Loading
Loading