Skip to content

Commit 915c64a

Browse files
authored
Add new convenience methods for converting DeclarativeConfigProperties to config model (#7453)
1 parent de49179 commit 915c64a

File tree

8 files changed

+474
-28
lines changed

8 files changed

+474
-28
lines changed

api/incubator/build.gradle.kts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ otelJava.moduleName.set("io.opentelemetry.api.incubator")
1212
dependencies {
1313
api(project(":api:all"))
1414

15+
// Supports optional InstrumentationConfigUtil#convertToModel
16+
compileOnly("com.fasterxml.jackson.core:jackson-databind")
17+
1518
annotationProcessor("com.google.auto.value:auto-value")
1619

1720
// To use parsed config file as input for InstrumentationConfigUtilTest
@@ -24,3 +27,16 @@ dependencies {
2427

2528
testImplementation("com.google.guava:guava")
2629
}
30+
31+
testing {
32+
suites {
33+
register<JvmTestSuite>("testConvertToModel") {
34+
dependencies {
35+
implementation("com.fasterxml.jackson.core:jackson-databind")
36+
implementation(project(":sdk-extensions:incubator"))
37+
implementation(project(":sdk-extensions:autoconfigure"))
38+
implementation("com.google.guava:guava")
39+
}
40+
}
41+
}
42+
}

api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static io.opentelemetry.api.internal.ConfigUtil.defaultIfNull;
99

1010
import java.util.List;
11+
import java.util.Map;
1112
import java.util.Set;
1213
import javax.annotation.Nullable;
1314

@@ -36,6 +37,11 @@ static DeclarativeConfigProperties empty() {
3637
return EmptyDeclarativeConfigProperties.getInstance();
3738
}
3839

40+
/** Return a map representation of the {@code declarativeConfigProperties}. */
41+
static Map<String, Object> toMap(DeclarativeConfigProperties declarativeConfigProperties) {
42+
return DeclarativeConfigPropertyUtil.toMap(declarativeConfigProperties);
43+
}
44+
3945
/**
4046
* Returns a {@link String} configuration property.
4147
*
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.api.incubator.config;
7+
8+
import static java.util.stream.Collectors.toList;
9+
10+
import java.util.Arrays;
11+
import java.util.HashMap;
12+
import java.util.List;
13+
import java.util.Map;
14+
import java.util.Optional;
15+
import java.util.Set;
16+
import java.util.function.BiFunction;
17+
import javax.annotation.Nullable;
18+
19+
final class DeclarativeConfigPropertyUtil {
20+
21+
private DeclarativeConfigPropertyUtil() {}
22+
23+
private static final List<BiFunction<String, DeclarativeConfigProperties, Object>>
24+
valueResolvers =
25+
Arrays.asList(
26+
DeclarativeConfigPropertyUtil::getString,
27+
DeclarativeConfigPropertyUtil::getBoolean,
28+
DeclarativeConfigPropertyUtil::getLong,
29+
DeclarativeConfigPropertyUtil::getDouble,
30+
DeclarativeConfigPropertyUtil::getStringList,
31+
DeclarativeConfigPropertyUtil::getBooleanList,
32+
DeclarativeConfigPropertyUtil::getLongList,
33+
DeclarativeConfigPropertyUtil::getDoubleList,
34+
DeclarativeConfigPropertyUtil::getStringList,
35+
DeclarativeConfigPropertyUtil::getStructuredList,
36+
DeclarativeConfigPropertyUtil::getStructured);
37+
38+
static Map<String, Object> toMap(DeclarativeConfigProperties declarativeConfigProperties) {
39+
Set<String> propertyKeys = declarativeConfigProperties.getPropertyKeys();
40+
Map<String, Object> result = new HashMap<>(propertyKeys.size());
41+
for (String key : declarativeConfigProperties.getPropertyKeys()) {
42+
result.put(key, resolveValue(key, declarativeConfigProperties));
43+
}
44+
return result;
45+
}
46+
47+
@Nullable
48+
private static Object resolveValue(
49+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
50+
for (int i = 0; i < valueResolvers.size(); i++) {
51+
try {
52+
Object value = valueResolvers.get(i).apply(key, declarativeConfigProperties);
53+
if (value != null) {
54+
return value;
55+
}
56+
} catch (DeclarativeConfigException e) {
57+
// Ignore and continue
58+
}
59+
}
60+
return null;
61+
}
62+
63+
@Nullable
64+
private static Object getString(
65+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
66+
return declarativeConfigProperties.getString(key);
67+
}
68+
69+
@Nullable
70+
private static Object getBoolean(
71+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
72+
return declarativeConfigProperties.getBoolean(key);
73+
}
74+
75+
@Nullable
76+
private static Object getLong(
77+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
78+
return declarativeConfigProperties.getLong(key);
79+
}
80+
81+
@Nullable
82+
private static Object getDouble(
83+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
84+
return declarativeConfigProperties.getDouble(key);
85+
}
86+
87+
@Nullable
88+
private static Object getStringList(
89+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
90+
return declarativeConfigProperties.getScalarList(key, String.class);
91+
}
92+
93+
@Nullable
94+
private static Object getBooleanList(
95+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
96+
return declarativeConfigProperties.getScalarList(key, Boolean.class);
97+
}
98+
99+
@Nullable
100+
private static Object getLongList(
101+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
102+
return declarativeConfigProperties.getScalarList(key, Long.class);
103+
}
104+
105+
@Nullable
106+
private static Object getDoubleList(
107+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
108+
return declarativeConfigProperties.getScalarList(key, Double.class);
109+
}
110+
111+
@Nullable
112+
private static Object getStructuredList(
113+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
114+
return Optional.ofNullable(declarativeConfigProperties.getStructuredList(key))
115+
.map(list -> list.stream().map(DeclarativeConfigPropertyUtil::toMap).collect(toList()))
116+
.orElse(null);
117+
}
118+
119+
@Nullable
120+
private static Object getStructured(
121+
String key, DeclarativeConfigProperties declarativeConfigProperties) {
122+
return Optional.ofNullable(declarativeConfigProperties.getStructured(key))
123+
.map(DeclarativeConfigPropertyUtil::toMap)
124+
.orElse(null);
125+
}
126+
}

api/incubator/src/main/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtil.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package io.opentelemetry.api.incubator.config;
77

8+
import com.fasterxml.jackson.databind.ObjectMapper;
89
import java.util.LinkedHashMap;
910
import java.util.List;
1011
import java.util.Map;
@@ -148,4 +149,49 @@ public static <T> T getOrNull(
148149
}
149150

150151
private InstrumentationConfigUtil() {}
152+
153+
/**
154+
* Return {@code .instrumentation.java.<instrumentationName>}, after converting it to the {@code
155+
* modelType} using the {@code objectMapper}. If no configuration exists for the {@code
156+
* instrumentationName}, returns {@code null}.
157+
*
158+
* <p>This method is a convenience method for a common instrumentation library workflow:
159+
*
160+
* <ul>
161+
* <li>During initialization, an instrumentation library is given an {@link ConfigProvider} and
162+
* must initialize according to the relevant config
163+
* <li>It checks if the user has provided configuration for it, and if so...
164+
* <li>It converts the configuration to an in-memory model representing all of its relevant
165+
* properties
166+
* <li>It initializes using the strongly typed in-memory model
167+
* </ul>
168+
*
169+
* <p>Conversion is done using {@link ObjectMapper#convertValue(Object, Class)} from {@code
170+
* com.fasterxml.jackson.databind}, and assumes the {@code modelType} is a POJO written /
171+
* annotated to support jackson databinding.
172+
*
173+
* <p>NOTE: callers MUST add their own dependency on {@code
174+
* com.fasterxml.jackson.core:jackson-databind}. This module's dependency is {@code compileOnly}
175+
* since jackson is a large dependency that many users will not require. It's very possible to
176+
* convert between {@link DeclarativeConfigProperties} (or a map representation from {@link
177+
* DeclarativeConfigProperties#toMap(DeclarativeConfigProperties)}) and a target model type
178+
* without jackson. This method is provided as an optional convenience method.
179+
*
180+
* @throws IllegalArgumentException if conversion fails. See {@link
181+
* ObjectMapper#convertValue(Object, Class)} for details.
182+
*/
183+
@Nullable
184+
public static <T> T getInstrumentationConfigModel(
185+
ConfigProvider configProvider,
186+
String instrumentationName,
187+
ObjectMapper objectMapper,
188+
Class<T> modelType) {
189+
DeclarativeConfigProperties properties =
190+
javaInstrumentationConfig(configProvider, instrumentationName);
191+
if (properties == null) {
192+
return null;
193+
}
194+
Map<String, Object> configPropertiesMap = DeclarativeConfigProperties.toMap(properties);
195+
return objectMapper.convertValue(configPropertiesMap, modelType);
196+
}
151197
}

api/incubator/src/test/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtilTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ void javaInstrumentationConfig() {
150150
.isInstanceOfSatisfying(
151151
YamlDeclarativeConfigProperties.class,
152152
exampleConfig ->
153-
assertThat(exampleConfig.toMap()).isEqualTo(ImmutableMap.of("property", "value")));
153+
assertThat(DeclarativeConfigProperties.toMap(exampleConfig))
154+
.isEqualTo(ImmutableMap.of("property", "value")));
154155
assertThat(
155156
InstrumentationConfigUtil.javaInstrumentationConfig(kitchenSinkConfigProvider, "foo"))
156157
.isNull();

0 commit comments

Comments
 (0)