Skip to content

Commit 2923430

Browse files
Add initial OSGi support (#7964)
Co-authored-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
1 parent 3f3780c commit 2923430

28 files changed

Lines changed: 325 additions & 0 deletions

File tree

all/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44

55
description = "OpenTelemetry All"
66
otelJava.moduleName.set("io.opentelemetry.all")
7+
otelJava.osgiEnabled.set(false)
78

89
// Skip ossIndexAudit on test module
910
tasks.named("ossIndexAudit") {

animal-sniffer-signature/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ plugins {
88

99
description = "Build tool to generate the Animal Sniffer Android signature"
1010
otelJava.moduleName.set("io.opentelemetry.internal.animalsniffer")
11+
otelJava.osgiEnabled.set(false)
1112

1213
val signatureJar = configurations.create("signatureJar") {
1314
isCanBeConsumed = false

api/all/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ plugins {
99
description = "OpenTelemetry API"
1010
otelJava.moduleName.set("io.opentelemetry.api")
1111
base.archivesName.set("opentelemetry-api")
12+
// These packages cannot be compileOnly dependencies (api:incubator depends on api:all, creating a
13+
// circular dependency; sdk:autoconfigure is in a different module family). Declare them as optional
14+
// imports without a version constraint so OSGi wires them if present but does not require them.
15+
otelJava.osgiUnversionedOptionalPackages.set(listOf("io.opentelemetry.sdk.autoconfigure", "io.opentelemetry.api.incubator", "io.opentelemetry.api.incubator.internal"))
1216

1317
dependencies {
1418
api(project(":context"))

api/incubator/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ plugins {
88

99
description = "OpenTelemetry API Incubator"
1010
otelJava.moduleName.set("io.opentelemetry.api.incubator")
11+
otelJava.osgiOptionalPackages.set(listOf("com.fasterxml.jackson.databind"))
1112

1213
dependencies {
1314
api(project(":api:all"))

api/testing-internal/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44

55
description = "OpenTelemetry API Testing (Internal)"
66
otelJava.moduleName.set("io.opentelemetry.api.testing.internal")
7+
otelJava.osgiEnabled.set(false)
78

89
dependencies {
910
api(project(":api:all"))

buildSrc/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ repositories {
3333
}
3434

3535
dependencies {
36+
implementation("biz.aQute.bnd:biz.aQute.bnd.gradle:7.2.0")
3637
implementation(enforcedPlatform("com.squareup.wire:wire-bom:6.2.0"))
3738
implementation("com.google.auto.value:auto-value-annotations:1.11.1")
3839
// When updating, update above in plugins too

buildSrc/src/main/kotlin/io/opentelemetry/gradle/OtelJavaExtension.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,29 @@
66
package io.opentelemetry.gradle
77

88
import org.gradle.api.JavaVersion
9+
import org.gradle.api.provider.ListProperty
910
import org.gradle.api.provider.Property
1011

1112
abstract class OtelJavaExtension {
1213
abstract val moduleName: Property<String>
1314

15+
// Set to false for modules that are not OSGi bundles (e.g. test helpers, build tooling,
16+
// aggregators). Skips BND bundle metadata generation entirely.
17+
abstract val osgiEnabled: Property<Boolean>
18+
19+
abstract val osgiOptionalPackages: ListProperty<String>
20+
21+
// Packages that should be optional imports but are not on the compile classpath (e.g. due to
22+
// circular dependencies), so BND cannot resolve version="${@}" for them. Added to Import-Package
23+
// with resolution:=optional but no version constraint.
24+
abstract val osgiUnversionedOptionalPackages: ListProperty<String>
25+
1426
abstract val minJavaVersionSupported: Property<JavaVersion>
1527

1628
init {
1729
minJavaVersionSupported.convention(JavaVersion.VERSION_1_8)
30+
osgiEnabled.convention(true)
31+
osgiOptionalPackages.convention(emptyList<String>())
32+
osgiUnversionedOptionalPackages.convention(emptyList<String>())
1833
}
1934
}

buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ plugins {
99
eclipse
1010
idea
1111

12+
id("biz.aQute.bnd.builder")
1213
id("otel.errorprone-conventions")
1314
id("otel.jacoco-conventions")
1415
id("otel.spotless-conventions")
@@ -132,6 +133,38 @@ tasks {
132133
}
133134
}
134135

136+
afterEvaluate {
137+
if (otelJava.osgiEnabled.get()) {
138+
named<Jar>("jar") {
139+
// Configure OSGi metadata
140+
bundle {
141+
// Compute import packages.
142+
// Certain packages like javax.annotation.* are always optional.
143+
// Modules may have additional optional packages, typically corresponding to compileOnly dependencies.
144+
// Append wildcard "*" last to import any other referenced packages.
145+
val optionalPackages = mutableListOf("javax.annotation")
146+
optionalPackages.addAll(otelJava.osgiOptionalPackages.get())
147+
val importPackages = optionalPackages.joinToString(",") { "$it.*;resolution:=optional;version=\"\${@}\"" } + ",*"
148+
149+
// Packages not on the compile classpath (e.g. due to circular dependencies) cannot use
150+
// version="${@}" since BND cannot resolve the version. Add them as optional imports without
151+
// a version constraint; they are listed before the wildcard so BND uses our explicit
152+
// instruction rather than auto-detecting them with a version.
153+
val unversionedOptionalPackages = otelJava.osgiUnversionedOptionalPackages.get()
154+
val unversionedImports = unversionedOptionalPackages.joinToString(",") { "$it.*;resolution:=optional" }
155+
val fullImportPackages = if (unversionedImports.isNotEmpty()) "$unversionedImports,$importPackages" else importPackages
156+
157+
bnd(mapOf(
158+
// Exclude shaded internal packages from exports; they are implementation details and
159+
// should not be part of the OSGi bundle's public API surface.
160+
"-exportcontents" to "!io.opentelemetry.internal.shaded.*,io.opentelemetry.*",
161+
"Import-Package" to fullImportPackages
162+
))
163+
}
164+
}
165+
}
166+
}
167+
135168
withType<Jar>().configureEach {
136169
inputs.property("moduleName", otelJava.moduleName)
137170

dependencyManagement/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ val DEPENDENCY_BOMS = listOf(
3636
"io.zipkin.brave:brave-bom:6.3.1",
3737
"io.zipkin.reporter2:zipkin-reporter-bom:3.5.3",
3838
"org.assertj:assertj-bom:3.27.7",
39+
"org.osgi:org.osgi.test.bom:1.2.1",
3940
"org.testcontainers:testcontainers-bom:2.0.5",
4041
"org.snakeyaml:snakeyaml-engine:2.10"
4142
)
@@ -88,11 +89,13 @@ val DEPENDENCIES = listOf(
8889
"io.opentracing:opentracing-noop:0.33.0",
8990
"junit:junit:4.13.2",
9091
"nl.jqno.equalsverifier:equalsverifier:3.19.4",
92+
"org.apache.felix:org.apache.felix.framework:7.0.5",
9193
"org.awaitility:awaitility:4.3.0",
9294
"org.codehaus.mojo:animal-sniffer-annotations:1.27",
9395
"org.jctools:jctools-core:4.0.6",
9496
"org.junit-pioneer:junit-pioneer:1.9.1",
9597
"org.mock-server:mockserver-netty:5.15.0:shaded",
98+
"org.osgi:osgi.core:8.0.0",
9699
"org.skyscreamer:jsonassert:1.5.3",
97100
"com.android.tools:desugar_jdk_libs:2.1.5",
98101
)

exporters/common/compile-stub/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ plugins {
44

55
description = "OpenTelemetry Exporter Compile Stub"
66
otelJava.moduleName.set("io.opentelemetry.exporter.internal.compile-stub")
7+
otelJava.osgiEnabled.set(false)

0 commit comments

Comments
 (0)