Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import static io.opentelemetry.api.internal.ConfigUtil.defaultIfNull;

import io.opentelemetry.context.ComponentLoader;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -222,4 +223,7 @@ default List<DeclarativeConfigProperties> getStructuredList(
* @return the configuration property keys
*/
Set<String> getPropertyKeys();

/** Return a {@link ComponentLoader} that should be used to load SPIs. */
ComponentLoader getComponentLoader();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.api.incubator.config;

import io.opentelemetry.context.ComponentLoader;
import java.util.Collections;
import java.util.List;
import java.util.Set;
Expand All @@ -15,6 +16,8 @@ final class EmptyDeclarativeConfigProperties implements DeclarativeConfigPropert

private static final EmptyDeclarativeConfigProperties INSTANCE =
new EmptyDeclarativeConfigProperties();
private static final ComponentLoader COMPONENT_LOADER =
ComponentLoader.forClassLoader(EmptyDeclarativeConfigProperties.class.getClassLoader());

private EmptyDeclarativeConfigProperties() {}

Expand Down Expand Up @@ -74,4 +77,9 @@ public List<DeclarativeConfigProperties> getStructuredList(String name) {
public Set<String> getPropertyKeys() {
return Collections.emptySet();
}

@Override
public ComponentLoader getComponentLoader() {
return COMPONENT_LOADER;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.context;

import java.util.ServiceLoader;

/** A loader for components that are discovered via SPI. */
public interface ComponentLoader {
/**
* Load implementations of an SPI.
*
* @param spiClass the SPI class
* @param <T> the SPI type
* @return iterable of SPI implementations
*/
<T> Iterable<T> load(Class<T> spiClass);

/**
* Create an instance for the {@code classLoader} using {@link ServiceLoader#load(Class,
* ClassLoader)}.
*/
static ComponentLoader forClassLoader(ClassLoader classLoader) {
return new ServiceLoaderComponentLoader(classLoader);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.logging.Level;
Expand Down Expand Up @@ -103,9 +102,10 @@ static ContextStorage createStorage(AtomicReference<Throwable> deferredStorageFa
return ContextStorage.defaultStorage();
}

ComponentLoader componentLoader =
ComponentLoader.forClassLoader(LazyStorage.class.getClassLoader());
List<ContextStorageProvider> providers = new ArrayList<>();
for (ContextStorageProvider provider :
ServiceLoader.load(ContextStorageProvider.class, LazyStorage.class.getClassLoader())) {
for (ContextStorageProvider provider : componentLoader.load(ContextStorageProvider.class)) {
if (provider
.getClass()
.getName()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.context;

import java.util.ServiceLoader;

class ServiceLoaderComponentLoader implements ComponentLoader {

private final ClassLoader classLoader;

ServiceLoaderComponentLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}

@Override
public <T> Iterable<T> load(Class<T> spiClass) {
return ServiceLoader.load(spiClass, classLoader);
}

@Override
public String toString() {
return "ServiceLoaderComponentLoader{classLoader=" + classLoader + "}";
}
}
7 changes: 6 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-context.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
Comparing source compatibility of opentelemetry-context-1.52.0-SNAPSHOT.jar against opentelemetry-context-1.51.0.jar
No changes.
+++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.context.ComponentLoader (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.context.ComponentLoader forClassLoader(java.lang.ClassLoader)
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.Iterable<T> load(java.lang.Class<T>)
GENERIC TEMPLATES: +++ T:java.lang.Object
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI new API surface area @jkwatson

Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
Comparing source compatibility of opentelemetry-exporter-otlp-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-1.51.0.jar
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder setComponentLoader(io.opentelemetry.context.ComponentLoader)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder setComponentLoader(io.opentelemetry.context.ComponentLoader)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder setComponentLoader(io.opentelemetry.context.ComponentLoader)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder setComponentLoader(io.opentelemetry.context.ComponentLoader)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder setComponentLoader(io.opentelemetry.context.ComponentLoader)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder setComponentLoader(io.opentelemetry.context.ComponentLoader)
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-spi-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-spi-1.51.0.jar
No changes.
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.context.ComponentLoader getComponentLoader()
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-1.51.0.jar
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(io.opentelemetry.context.ComponentLoader)
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.stream.Collectors.joining;

import io.opentelemetry.context.ComponentLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import javax.annotation.Nullable;

Expand Down Expand Up @@ -48,7 +48,8 @@ public static Compressor validateAndResolveCompressor(String compressionMethod)
private static Map<String, Compressor> buildCompressorRegistry() {
Map<String, Compressor> compressors = new HashMap<>();
for (CompressorProvider spi :
ServiceLoader.load(CompressorProvider.class, CompressorUtil.class.getClassLoader())) {
ComponentLoader.forClassLoader(CompressorUtil.class.getClassLoader())
.load(CompressorProvider.class)) {
Compressor compressor = spi.getInstance();
compressors.put(compressor.getEncoding(), compressor);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.internal.ConfigUtil;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.context.ComponentLoader;
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.exporter.internal.TlsConfigHelper;
import io.opentelemetry.exporter.internal.compression.Compressor;
Expand All @@ -26,7 +27,6 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.StringJoiner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -67,7 +67,8 @@ public class GrpcExporterBuilder<T extends Marshaler> {
private Supplier<MeterProvider> meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider;
private InternalTelemetryVersion internalTelemetryVersion = InternalTelemetryVersion.LEGACY;

private ClassLoader serviceClassLoader = GrpcExporterBuilder.class.getClassLoader();
private ComponentLoader componentLoader =
ComponentLoader.forClassLoader(GrpcExporterBuilder.class.getClassLoader());
@Nullable private ExecutorService executorService;

// Use Object type since gRPC may not be on the classpath.
Expand Down Expand Up @@ -158,8 +159,8 @@ public GrpcExporterBuilder<T> setInternalTelemetryVersion(
return this;
}

public GrpcExporterBuilder<T> setServiceClassLoader(ClassLoader servieClassLoader) {
this.serviceClassLoader = servieClassLoader;
public GrpcExporterBuilder<T> setComponentLoader(ComponentLoader componentLoader) {
this.componentLoader = componentLoader;
return this;
}

Expand Down Expand Up @@ -268,7 +269,7 @@ public String toString(boolean includePrefixAndSuffix) {
if (grpcChannel != null) {
joiner.add("grpcChannel=" + grpcChannel);
}
joiner.add("serviceClassLoader=" + serviceClassLoader);
joiner.add("componentLoader=" + componentLoader);
if (executorService != null) {
joiner.add("executorService=" + executorService);
}
Expand Down Expand Up @@ -302,8 +303,7 @@ public String toString() {
*/
private GrpcSenderProvider resolveGrpcSenderProvider() {
Map<String, GrpcSenderProvider> grpcSenderProviders = new HashMap<>();
for (GrpcSenderProvider spi :
ServiceLoader.load(GrpcSenderProvider.class, serviceClassLoader)) {
for (GrpcSenderProvider spi : componentLoader.load(GrpcSenderProvider.class)) {
grpcSenderProviders.put(spi.getClass().getName(), spi);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.internal.ConfigUtil;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.context.ComponentLoader;
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.exporter.internal.TlsConfigHelper;
import io.opentelemetry.exporter.internal.compression.Compressor;
Expand All @@ -24,7 +25,6 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.StringJoiner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -64,7 +64,8 @@ public final class HttpExporterBuilder<T extends Marshaler> {
@Nullable private RetryPolicy retryPolicy = RetryPolicy.getDefault();
private Supplier<MeterProvider> meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider;
private InternalTelemetryVersion internalTelemetryVersion = InternalTelemetryVersion.LEGACY;
private ClassLoader serviceClassLoader = HttpExporterBuilder.class.getClassLoader();
private ComponentLoader componentLoader =
ComponentLoader.forClassLoader(HttpExporterBuilder.class.getClassLoader());
@Nullable private ExecutorService executorService;

public HttpExporterBuilder(
Expand Down Expand Up @@ -143,8 +144,8 @@ public HttpExporterBuilder<T> setProxyOptions(ProxyOptions proxyOptions) {
return this;
}

public HttpExporterBuilder<T> setServiceClassLoader(ClassLoader servieClassLoader) {
this.serviceClassLoader = servieClassLoader;
public HttpExporterBuilder<T> setComponentLoader(ComponentLoader componentLoader) {
this.componentLoader = componentLoader;
return this;
}

Expand Down Expand Up @@ -265,7 +266,7 @@ public String toString(boolean includePrefixAndSuffix) {
if (retryPolicy != null) {
joiner.add("retryPolicy=" + retryPolicy);
}
joiner.add("serviceClassLoader=" + serviceClassLoader);
joiner.add("componentLoader=" + componentLoader);
if (executorService != null) {
joiner.add("executorService=" + executorService);
}
Expand Down Expand Up @@ -299,8 +300,7 @@ public String toString() {
*/
private HttpSenderProvider resolveHttpSenderProvider() {
Map<String, HttpSenderProvider> httpSenderProviders = new HashMap<>();
for (HttpSenderProvider spi :
ServiceLoader.load(HttpSenderProvider.class, serviceClassLoader)) {
for (HttpSenderProvider spi : componentLoader.load(HttpSenderProvider.class)) {
httpSenderProviders.put(spi.getClass().getName(), spi);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
Expand All @@ -29,10 +28,10 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ServiceLoader;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
Expand Down Expand Up @@ -306,7 +305,7 @@ void providerConfig() {

@Test
void componentProviderConfig() {
DeclarativeConfigProperties properties = mock(DeclarativeConfigProperties.class);
DeclarativeConfigProperties properties = spy(DeclarativeConfigProperties.empty());
T exporter = exporterFromComponentProvider(properties);

assertThat(exporter).extracting("wrapperJsonObject").isEqualTo(true);
Expand All @@ -330,22 +329,22 @@ void componentProviderConfig() {
@SuppressWarnings("unchecked")
protected T exporterFromComponentProvider(DeclarativeConfigProperties properties) {
return (T)
((ComponentProvider<?>)
loadSpi(ComponentProvider.class)
.filter(
p -> {
ComponentProvider<?> c = (ComponentProvider<?>) p;
return "otlp_file/development".equals(c.getName())
&& c.getType().equals(componentProviderType);
})
.findFirst()
.orElseThrow(() -> new IllegalStateException("No provider found")))
StreamSupport.stream(
properties.getComponentLoader().load(ComponentProvider.class).spliterator(), false)
.filter(
p -> {
ComponentProvider<?> c = p;
return "otlp_file/development".equals(c.getName())
&& c.getType().equals(componentProviderType);
})
.findFirst()
.orElseThrow(() -> new IllegalStateException("No provider found"))
.create(properties);
}

@SuppressWarnings("unchecked")
protected T exporterFromProvider(ConfigProperties config) {
Object provider = loadProvider();
Object provider = loadProvider(config);

try {
return (T)
Expand All @@ -358,8 +357,9 @@ protected T exporterFromProvider(ConfigProperties config) {
}
}

private Object loadProvider() {
return loadSpi(providerClass)
private Object loadProvider(ConfigProperties config) {
return StreamSupport.stream(
config.getComponentLoader().load(providerClass).spliterator(), false)
.filter(
p -> {
try {
Expand All @@ -372,8 +372,4 @@ private Object loadProvider() {
.findFirst()
.orElseThrow(() -> new IllegalStateException("No provider found"));
}

protected static Stream<?> loadSpi(Class<?> type) {
return Streams.stream(ServiceLoader.load(type, type.getClassLoader()).iterator());
}
}
Loading
Loading