From da087864fed9a8ad3c6aa11116331110d1d9f359 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 20 Apr 2026 09:19:03 +0000 Subject: [PATCH 1/3] docs(agent-extension-api): mark ConfigProperties @Nullable where null is possible In declarative-config mode, AutoConfigureUtil.getConfig(autoConfiguredSdk) returns null. That null already flows through AgentInstaller into the BootstrapPackagesConfigurer, IgnoredTypesConfigurer (deprecated overload), and AgentExtension SPIs, but the signatures did not reflect it. Annotate the affected parameters and locals as @Nullable and document the declarative-config contract in the SPI javadoc. No behavior change. This aligns the public contract with actual runtime behavior so NullAway can be enabled on the calling modules without needing a runtime workaround. Signed-off-by: Gregor Zeitlinger --- .../extension/ignore/IgnoredTypesConfigurer.java | 3 ++- .../opentelemetry/javaagent/tooling/AgentExtension.java | 4 +++- .../opentelemetry/javaagent/tooling/AgentInstaller.java | 8 +++++--- .../tooling/bootstrap/BootstrapPackagesConfigurer.java | 5 ++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java index 9e3dda7a4c0c..9ed7d15e09a6 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java @@ -7,6 +7,7 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.Ordered; +import javax.annotation.Nullable; /** * An {@link IgnoredTypesConfigurer} can be used to augment built-in instrumentation restrictions: @@ -32,7 +33,7 @@ default void configure(IgnoredTypesBuilder builder) {} * @deprecated Use {@link #configure(IgnoredTypesBuilder)} instead. */ @Deprecated - default void configure(IgnoredTypesBuilder builder, ConfigProperties config) { + default void configure(IgnoredTypesBuilder builder, @Nullable ConfigProperties config) { configure(builder); } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java index 556c1bbfdf2d..6d0d66633370 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java @@ -8,6 +8,7 @@ import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.Ordered; +import javax.annotation.Nullable; import net.bytebuddy.agent.builder.AgentBuilder; /** @@ -25,8 +26,9 @@ public interface AgentExtension extends Ordered { * * @return The customized agent. Note that this method MUST return a non-null {@link AgentBuilder} * instance that contains all customizations defined in this extension. + *

{@code config} is {@code null} when the agent is started with declarative configuration. */ - AgentBuilder extend(AgentBuilder agentBuilder, ConfigProperties config); + AgentBuilder extend(AgentBuilder agentBuilder, @Nullable ConfigProperties config); /** * Returns the name of the extension. It does not have to be unique, but it should be diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java index 959ce738e27a..ee95d6c11657 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java @@ -153,7 +153,7 @@ private static void installBytebuddyAgent( AutoConfiguredOpenTelemetrySdk autoConfiguredSdk = installOpenTelemetrySdk(extensionClassLoader); - ConfigProperties sdkConfig = AutoConfigureUtil.getConfig(autoConfiguredSdk); + @Nullable ConfigProperties sdkConfig = AutoConfigureUtil.getConfig(autoConfiguredSdk); setBootstrapPackages(sdkConfig, extensionClassLoader); ConfiguredResourceAttributesHolder.initialize( @@ -267,7 +267,7 @@ private static void installEarlyInstrumentation( } private static void setBootstrapPackages( - ConfigProperties config, ClassLoader extensionClassLoader) { + @Nullable ConfigProperties config, ClassLoader extensionClassLoader) { BootstrapPackagesBuilderImpl builder = new BootstrapPackagesBuilderImpl(); for (BootstrapPackagesConfigurer configurer : load(BootstrapPackagesConfigurer.class, extensionClassLoader)) { @@ -283,7 +283,9 @@ private static void setDefineClassHandler() { // Need to call deprecated API for backward compatibility with extensions that haven't migrated @SuppressWarnings("deprecation") private static AgentBuilder configureIgnoredTypes( - ConfigProperties config, ClassLoader extensionClassLoader, AgentBuilder agentBuilder) { + @Nullable ConfigProperties config, + ClassLoader extensionClassLoader, + AgentBuilder agentBuilder) { IgnoredTypesBuilderImpl builder = new IgnoredTypesBuilderImpl(); for (IgnoredTypesConfigurer configurer : loadOrdered(IgnoredTypesConfigurer.class, extensionClassLoader)) { diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java index 30d0e56a9a8c..4bfebd25dbd0 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.tooling.bootstrap; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import javax.annotation.Nullable; /** * This SPI can be used to define which packages/classes belong to the bootstrap class loader: all @@ -23,6 +24,8 @@ public interface BootstrapPackagesConfigurer { /** * Configure the passed {@code builder} and define which classes should always be loaded by the * bootstrap class loader. + * + *

{@code config} is {@code null} when the agent is started with declarative configuration. */ - void configure(BootstrapPackagesBuilder builder, ConfigProperties config); + void configure(BootstrapPackagesBuilder builder, @Nullable ConfigProperties config); } From 3a03460cc25c96b21d874c2a0bc228230c7fb2af Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 18 May 2026 17:02:02 +0000 Subject: [PATCH 2/3] fix: preserve non-null extension config contract Signed-off-by: Gregor Zeitlinger --- .../javaagent/tooling/AgentExtension.java | 4 +--- .../javaagent/tooling/AgentInstaller.java | 19 +++++++++++-------- .../BootstrapPackagesConfigurer.java | 5 +---- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java index 6d0d66633370..556c1bbfdf2d 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentExtension.java @@ -8,7 +8,6 @@ import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.Ordered; -import javax.annotation.Nullable; import net.bytebuddy.agent.builder.AgentBuilder; /** @@ -26,9 +25,8 @@ public interface AgentExtension extends Ordered { * * @return The customized agent. Note that this method MUST return a non-null {@link AgentBuilder} * instance that contains all customizations defined in this extension. - *

{@code config} is {@code null} when the agent is started with declarative configuration. */ - AgentBuilder extend(AgentBuilder agentBuilder, @Nullable ConfigProperties config); + AgentBuilder extend(AgentBuilder agentBuilder, ConfigProperties config); /** * Returns the name of the extension. It does not have to be unique, but it should be diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java index ee95d6c11657..aa189d618f15 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java @@ -154,8 +154,9 @@ private static void installBytebuddyAgent( installOpenTelemetrySdk(extensionClassLoader); @Nullable ConfigProperties sdkConfig = AutoConfigureUtil.getConfig(autoConfiguredSdk); + ConfigProperties sdkConfigOrEmpty = getConfigOrEmpty(sdkConfig); - setBootstrapPackages(sdkConfig, extensionClassLoader); + setBootstrapPackages(sdkConfigOrEmpty, extensionClassLoader); ConfiguredResourceAttributesHolder.initialize( SdkAutoconfigureAccess.getResource(autoConfiguredSdk).getAttributes()); @@ -165,7 +166,7 @@ private static void installBytebuddyAgent( } agentBuilder = agentBuilder.with(new ClassLoadListener()); - agentBuilder = configureIgnoredTypes(sdkConfig, extensionClassLoader, agentBuilder); + agentBuilder = configureIgnoredTypes(sdkConfigOrEmpty, extensionClassLoader, agentBuilder); int numberOfLoadedExtensions = 0; for (AgentExtension agentExtension : loadOrdered(AgentExtension.class, extensionClassLoader)) { @@ -176,7 +177,7 @@ private static void installBytebuddyAgent( new Object[] {agentExtension.extensionName(), agentExtension.getClass().getName()}); } try { - agentBuilder = agentExtension.extend(agentBuilder, sdkConfig); + agentBuilder = agentExtension.extend(agentBuilder, sdkConfigOrEmpty); numberOfLoadedExtensions++; } catch (Exception | LinkageError e) { logger.log( @@ -266,8 +267,12 @@ private static void installEarlyInstrumentation( agentBuilder.installOn(instrumentation); } + private static ConfigProperties getConfigOrEmpty(@Nullable ConfigProperties config) { + return config != null ? config : EmptyConfigProperties.INSTANCE; + } + private static void setBootstrapPackages( - @Nullable ConfigProperties config, ClassLoader extensionClassLoader) { + ConfigProperties config, ClassLoader extensionClassLoader) { BootstrapPackagesBuilderImpl builder = new BootstrapPackagesBuilderImpl(); for (BootstrapPackagesConfigurer configurer : load(BootstrapPackagesConfigurer.class, extensionClassLoader)) { @@ -283,13 +288,11 @@ private static void setDefineClassHandler() { // Need to call deprecated API for backward compatibility with extensions that haven't migrated @SuppressWarnings("deprecation") private static AgentBuilder configureIgnoredTypes( - @Nullable ConfigProperties config, - ClassLoader extensionClassLoader, - AgentBuilder agentBuilder) { + ConfigProperties config, ClassLoader extensionClassLoader, AgentBuilder agentBuilder) { IgnoredTypesBuilderImpl builder = new IgnoredTypesBuilderImpl(); for (IgnoredTypesConfigurer configurer : loadOrdered(IgnoredTypesConfigurer.class, extensionClassLoader)) { - configurer.configure(builder, config != null ? config : EmptyConfigProperties.INSTANCE); + configurer.configure(builder, config); } Trie ignoredTasksTrie = builder.buildIgnoredTasksTrie(); diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java index 4bfebd25dbd0..30d0e56a9a8c 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesConfigurer.java @@ -6,7 +6,6 @@ package io.opentelemetry.javaagent.tooling.bootstrap; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import javax.annotation.Nullable; /** * This SPI can be used to define which packages/classes belong to the bootstrap class loader: all @@ -24,8 +23,6 @@ public interface BootstrapPackagesConfigurer { /** * Configure the passed {@code builder} and define which classes should always be loaded by the * bootstrap class loader. - * - *

{@code config} is {@code null} when the agent is started with declarative configuration. */ - void configure(BootstrapPackagesBuilder builder, @Nullable ConfigProperties config); + void configure(BootstrapPackagesBuilder builder, ConfigProperties config); } From f282f69e3f03b5c7065841179bbf27cfc80a164e Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 18 May 2026 17:19:53 +0000 Subject: [PATCH 3/3] fix: keep ignored types config non-null Signed-off-by: Gregor Zeitlinger --- .../javaagent/extension/ignore/IgnoredTypesConfigurer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java index 9ed7d15e09a6..9e3dda7a4c0c 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ignore/IgnoredTypesConfigurer.java @@ -7,7 +7,6 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.Ordered; -import javax.annotation.Nullable; /** * An {@link IgnoredTypesConfigurer} can be used to augment built-in instrumentation restrictions: @@ -33,7 +32,7 @@ default void configure(IgnoredTypesBuilder builder) {} * @deprecated Use {@link #configure(IgnoredTypesBuilder)} instead. */ @Deprecated - default void configure(IgnoredTypesBuilder builder, @Nullable ConfigProperties config) { + default void configure(IgnoredTypesBuilder builder, ConfigProperties config) { configure(builder); } }