Skip to content

Commit

Permalink
Use autoconfigured ClassLoader to load declarative config (#6725)
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg authored Sep 20, 2024
1 parent 39b2411 commit 325822c
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
private Function<ConfigProperties, ConfigProperties> configPropertiesCustomizer =
Function.identity();

private SpiHelper spiHelper =
SpiHelper.create(AutoConfiguredOpenTelemetrySdk.class.getClassLoader());
private ComponentLoader componentLoader =
SpiHelper.serviceComponentLoader(AutoConfiguredOpenTelemetrySdk.class.getClassLoader());

private boolean registerShutdownHook = true;

Expand Down Expand Up @@ -401,14 +401,14 @@ public AutoConfiguredOpenTelemetrySdkBuilder setResultAsGlobal() {
public AutoConfiguredOpenTelemetrySdkBuilder setServiceClassLoader(
ClassLoader serviceClassLoader) {
requireNonNull(serviceClassLoader, "serviceClassLoader");
this.spiHelper = SpiHelper.create(serviceClassLoader);
this.componentLoader = SpiHelper.serviceComponentLoader(serviceClassLoader);
return this;
}

/** Sets the {@link ComponentLoader} to be used to load SPI implementations. */
AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(ComponentLoader componentLoader) {
requireNonNull(componentLoader, "componentLoader");
this.spiHelper = SpiHelper.create(componentLoader);
this.componentLoader = componentLoader;
return this;
}

Expand All @@ -417,6 +417,7 @@ AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(ComponentLoader compone
* the settings of this {@link AutoConfiguredOpenTelemetrySdkBuilder}.
*/
public AutoConfiguredOpenTelemetrySdk build() {
SpiHelper spiHelper = SpiHelper.create(componentLoader);
if (!customized) {
customized = true;
mergeSdkTracerProviderConfigurer();
Expand All @@ -428,7 +429,8 @@ public AutoConfiguredOpenTelemetrySdk build() {

ConfigProperties config = getConfig();

AutoConfiguredOpenTelemetrySdk fromFileConfiguration = maybeConfigureFromFile(config);
AutoConfiguredOpenTelemetrySdk fromFileConfiguration =
maybeConfigureFromFile(config, componentLoader);
if (fromFileConfiguration != null) {
maybeRegisterShutdownHook(fromFileConfiguration.getOpenTelemetrySdk());
maybeSetAsGlobal(fromFileConfiguration.getOpenTelemetrySdk());
Expand Down Expand Up @@ -527,7 +529,8 @@ public AutoConfiguredOpenTelemetrySdk build() {
}

@Nullable
private static AutoConfiguredOpenTelemetrySdk maybeConfigureFromFile(ConfigProperties config) {
private static AutoConfiguredOpenTelemetrySdk maybeConfigureFromFile(
ConfigProperties config, ComponentLoader componentLoader) {
String otelConfigFile = config.getString("otel.config.file");
if (otelConfigFile != null && !otelConfigFile.isEmpty()) {
logger.warning(
Expand All @@ -552,8 +555,10 @@ private static AutoConfiguredOpenTelemetrySdk maybeConfigureFromFile(ConfigPrope
Class<?> openTelemetryConfiguration =
Class.forName(
"io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel");
Method create = configurationFactory.getMethod("create", openTelemetryConfiguration);
OpenTelemetrySdk sdk = (OpenTelemetrySdk) create.invoke(null, model);
Method create =
configurationFactory.getMethod(
"create", openTelemetryConfiguration, ComponentLoader.class);
OpenTelemetrySdk sdk = (OpenTelemetrySdk) create.invoke(null, model, componentLoader);
Method toConfigProperties =
configurationFactory.getMethod("toConfigProperties", openTelemetryConfiguration);
StructuredConfigProperties structuredConfigProperties =
Expand Down Expand Up @@ -608,7 +613,7 @@ void callAutoConfigureListeners(SpiHelper spiHelper, OpenTelemetrySdk openTeleme
@SuppressWarnings("deprecation") // Support deprecated SdkTracerProviderConfigurer
private void mergeSdkTracerProviderConfigurer() {
for (io.opentelemetry.sdk.autoconfigure.spi.traces.SdkTracerProviderConfigurer configurer :
spiHelper.load(
componentLoader.load(
io.opentelemetry.sdk.autoconfigure.spi.traces.SdkTracerProviderConfigurer.class)) {
addTracerProviderCustomizer(
(builder, config) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,24 @@ private SpiHelper(ComponentLoader componentLoader) {

/** Create a {@link SpiHelper} which loads SPIs using the {@code classLoader}. */
public static SpiHelper create(ClassLoader classLoader) {
return new SpiHelper(new ServiceLoaderComponentLoader(classLoader));
return new SpiHelper(serviceComponentLoader(classLoader));
}

/** Create a {@link SpiHelper} which loads SPIs using the {@code componentLoader}. */
public static SpiHelper create(ComponentLoader componentLoader) {
return new SpiHelper(componentLoader);
}

/** Create a {@link ComponentLoader} which loads using the {@code classLoader}. */
public static ComponentLoader serviceComponentLoader(ClassLoader classLoader) {
return new ServiceLoaderComponentLoader(classLoader);
}

/** Return the backing underlying {@link ComponentLoader}. */
public ComponentLoader getComponentLoader() {
return componentLoader;
}

/**
* Load implementations of an SPI which are configurable (i.e. they accept {@link
* ConfigProperties}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ static <T> T requireNonNull(@Nullable T object, String description) {
*/
static <T> T loadComponent(SpiHelper spiHelper, Class<T> type, String name, Object model) {
// Map model to generic structured config properties
StructuredConfigProperties config = FileConfiguration.toConfigProperties(model);
StructuredConfigProperties config =
FileConfiguration.toConfigProperties(model, spiHelper.getComponentLoader());
return spiHelper.loadComponent(type, name, config);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
Expand Down Expand Up @@ -49,6 +50,8 @@ public final class FileConfiguration {
private static final Logger logger = Logger.getLogger(FileConfiguration.class.getName());
private static final Pattern ENV_VARIABLE_REFERENCE =
Pattern.compile("\\$\\{([a-zA-Z_][a-zA-Z0-9_]*)}");
private static final ComponentLoader DEFAULT_COMPONENT_LOADER =
SpiHelper.serviceComponentLoader(FileConfiguration.class.getClassLoader());

private static final ObjectMapper MAPPER;

Expand Down Expand Up @@ -86,9 +89,24 @@ public static OpenTelemetrySdk parseAndCreate(InputStream inputStream) {
* @throws ConfigurationException if unable to interpret
*/
public static OpenTelemetrySdk create(OpenTelemetryConfigurationModel configurationModel) {
return create(configurationModel, DEFAULT_COMPONENT_LOADER);
}

/**
* Interpret the {@code configurationModel} to create {@link OpenTelemetrySdk} instance
* corresponding to the configuration.
*
* @param configurationModel the configuration model
* @param componentLoader the component loader used to load {@link ComponentProvider}
* implementations
* @return the {@link OpenTelemetrySdk}
* @throws ConfigurationException if unable to interpret
*/
public static OpenTelemetrySdk create(
OpenTelemetryConfigurationModel configurationModel, ComponentLoader componentLoader) {
return createAndMaybeCleanup(
OpenTelemetryConfigurationFactory.getInstance(),
SpiHelper.create(FileConfiguration.class.getClassLoader()),
SpiHelper.create(componentLoader),
configurationModel);
}

Expand Down Expand Up @@ -130,7 +148,7 @@ static Object loadYaml(InputStream inputStream, Map<String, String> environmentV
*/
public static StructuredConfigProperties toConfigProperties(
OpenTelemetryConfigurationModel model) {
return toConfigProperties((Object) model);
return toConfigProperties(model, DEFAULT_COMPONENT_LOADER);
}

/**
Expand All @@ -141,13 +159,14 @@ public static StructuredConfigProperties toConfigProperties(
*/
public static StructuredConfigProperties toConfigProperties(InputStream configuration) {
Object yamlObj = loadYaml(configuration, System.getenv());
return toConfigProperties(yamlObj);
return toConfigProperties(yamlObj, DEFAULT_COMPONENT_LOADER);
}

static StructuredConfigProperties toConfigProperties(Object model) {
static StructuredConfigProperties toConfigProperties(
Object model, ComponentLoader componentLoader) {
Map<String, Object> configurationMap =
MAPPER.convertValue(model, new TypeReference<Map<String, Object>>() {});
return YamlStructuredConfigProperties.create(configurationMap);
return YamlStructuredConfigProperties.create(configurationMap, componentLoader);
}

/**
Expand All @@ -162,21 +181,27 @@ static StructuredConfigProperties toConfigProperties(Object model) {
// ComponentProvider
public static io.opentelemetry.sdk.trace.samplers.Sampler createSampler(
StructuredConfigProperties genericSamplerModel) {
SamplerModel samplerModel = convertToModel(genericSamplerModel, SamplerModel.class);
YamlStructuredConfigProperties yamlStructuredConfigProperties =
requireYamlStructuredConfigProperties(genericSamplerModel);
SamplerModel samplerModel = convertToModel(yamlStructuredConfigProperties, SamplerModel.class);
return createAndMaybeCleanup(
SamplerFactory.getInstance(),
SpiHelper.create(FileConfiguration.class.getClassLoader()),
SpiHelper.create(yamlStructuredConfigProperties.getComponentLoader()),
samplerModel);
}

static <T> T convertToModel(
StructuredConfigProperties structuredConfigProperties, Class<T> modelType) {
private static YamlStructuredConfigProperties requireYamlStructuredConfigProperties(
StructuredConfigProperties structuredConfigProperties) {
if (!(structuredConfigProperties instanceof YamlStructuredConfigProperties)) {
throw new ConfigurationException(
"Only YamlStructuredConfigProperties can be converted to model");
}
return MAPPER.convertValue(
((YamlStructuredConfigProperties) structuredConfigProperties).toMap(), modelType);
return (YamlStructuredConfigProperties) structuredConfigProperties;
}

static <T> T convertToModel(
YamlStructuredConfigProperties structuredConfigProperties, Class<T> modelType) {
return MAPPER.convertValue(structuredConfigProperties.toMap(), modelType);
}

static <M, R> R createAndMaybeCleanup(Factory<M, R> factory, SpiHelper spiHelper, M model) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ final class ResourceFactory
Resource> {

private static final StructuredConfigProperties EMPTY_CONFIG =
FileConfiguration.toConfigProperties(Collections.emptyMap());
FileConfiguration.toConfigProperties(
Collections.emptyMap(),
SpiHelper.serviceComponentLoader(ResourceFactory.class.getClassLoader()));
private static final ResourceFactory INSTANCE = new ResourceFactory();

private ResourceFactory() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
Expand Down Expand Up @@ -37,14 +38,17 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties

private final Map<String, List<YamlStructuredConfigProperties>> listEntries;
private final Map<String, YamlStructuredConfigProperties> mapEntries;
private final ComponentLoader componentLoader;

private YamlStructuredConfigProperties(
Map<String, Object> simpleEntries,
Map<String, List<YamlStructuredConfigProperties>> listEntries,
Map<String, YamlStructuredConfigProperties> mapEntries) {
Map<String, YamlStructuredConfigProperties> mapEntries,
ComponentLoader componentLoader) {
this.simpleEntries = simpleEntries;
this.listEntries = listEntries;
this.mapEntries = mapEntries;
this.componentLoader = componentLoader;
}

/**
Expand All @@ -57,7 +61,8 @@ private YamlStructuredConfigProperties(
* @see FileConfiguration#toConfigProperties(OpenTelemetryConfigurationModel)
*/
@SuppressWarnings("unchecked")
static YamlStructuredConfigProperties create(Map<String, Object> properties) {
static YamlStructuredConfigProperties create(
Map<String, Object> properties, ComponentLoader componentLoader) {
Map<String, Object> simpleEntries = new HashMap<>();
Map<String, List<YamlStructuredConfigProperties>> listEntries = new HashMap<>();
Map<String, YamlStructuredConfigProperties> mapEntries = new HashMap<>();
Expand All @@ -75,13 +80,15 @@ static YamlStructuredConfigProperties create(Map<String, Object> properties) {
if (isListOfMaps(value)) {
List<YamlStructuredConfigProperties> list =
((List<Map<String, Object>>) value)
.stream().map(YamlStructuredConfigProperties::create).collect(toList());
.stream()
.map(map -> YamlStructuredConfigProperties.create(map, componentLoader))
.collect(toList());
listEntries.put(key, list);
continue;
}
if (isMap(value)) {
YamlStructuredConfigProperties configProperties =
YamlStructuredConfigProperties.create((Map<String, Object>) value);
YamlStructuredConfigProperties.create((Map<String, Object>) value, componentLoader);
mapEntries.put(key, configProperties);
continue;
}
Expand All @@ -91,7 +98,8 @@ static YamlStructuredConfigProperties create(Map<String, Object> properties) {
+ "\" has unrecognized object type "
+ value.getClass().getName());
}
return new YamlStructuredConfigProperties(simpleEntries, listEntries, mapEntries);
return new YamlStructuredConfigProperties(
simpleEntries, listEntries, mapEntries, componentLoader);
}

private static boolean isPrimitiveList(Object object) {
Expand Down Expand Up @@ -292,4 +300,9 @@ public Map<String, Object> toMap() {
mapEntries.forEach((key, value) -> result.put(key, value.toMap()));
return Collections.unmodifiableMap(result);
}

/** Return the {@link ComponentLoader}. */
public ComponentLoader getComponentLoader() {
return componentLoader;
}
}

0 comments on commit 325822c

Please sign in to comment.