Skip to content

Commit 922c780

Browse files
committed
Introduce ConfigSourceException, have ConfigProvider.Source get method throw this exception, and introduce tests
1 parent e8faa94 commit 922c780

File tree

3 files changed

+166
-37
lines changed

3 files changed

+166
-37
lines changed

internal-api/src/main/java/datadog/trace/bootstrap/config/provider/ConfigProvider.java

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,18 @@ public <T extends Enum<T>> T getEnum(String key, Class<T> enumType, T defaultVal
7676

7777
public String getString(String key, String defaultValue, String... aliases) {
7878
for (ConfigProvider.Source source : sources) {
79-
String value = source.get(key, aliases);
80-
if (value != null) {
79+
try {
80+
String value = source.get(key, aliases);
81+
if (value != null) {
82+
if (collectConfig) {
83+
ConfigCollector.get().put(key, value, source.origin());
84+
}
85+
return value;
86+
}
87+
} catch (ConfigSourceException e) {
8188
if (collectConfig) {
82-
ConfigCollector.get().put(key, value, source.origin());
89+
ConfigCollector.get().put(key, e.getRawValue(), source.origin());
8390
}
84-
return value;
8591
}
8692
}
8793
if (collectConfig) {
@@ -96,12 +102,18 @@ public String getString(String key, String defaultValue, String... aliases) {
96102
*/
97103
public String getStringNotEmpty(String key, String defaultValue, String... aliases) {
98104
for (ConfigProvider.Source source : sources) {
99-
String value = source.get(key, aliases);
100-
if (value != null && !value.trim().isEmpty()) {
105+
try {
106+
String value = source.get(key, aliases);
107+
if (value != null && !value.trim().isEmpty()) {
108+
if (collectConfig) {
109+
ConfigCollector.get().put(key, value, source.origin());
110+
}
111+
return value;
112+
}
113+
} catch (ConfigSourceException e) {
101114
if (collectConfig) {
102-
ConfigCollector.get().put(key, value, source.origin());
115+
ConfigCollector.get().put(key, e.getRawValue(), source.origin());
103116
}
104-
return value;
105117
}
106118
}
107119
if (collectConfig) {
@@ -119,13 +131,18 @@ public String getStringExcludingSource(
119131
if (excludedSource.isAssignableFrom(source.getClass())) {
120132
continue;
121133
}
122-
123-
String value = source.get(key, aliases);
124-
if (value != null) {
134+
try {
135+
String value = source.get(key, aliases);
136+
if (value != null) {
137+
if (collectConfig) {
138+
ConfigCollector.get().put(key, value, source.origin());
139+
}
140+
return value;
141+
}
142+
} catch (ConfigSourceException e) {
125143
if (collectConfig) {
126-
ConfigCollector.get().put(key, value, source.origin());
144+
ConfigCollector.get().put(key, e.getRawValue(), source.origin());
127145
}
128-
return value;
129146
}
130147
}
131148
if (collectConfig) {
@@ -202,6 +219,10 @@ private <T> T get(String key, T defaultValue, Class<T> type, String... aliases)
202219
}
203220
return value;
204221
}
222+
} catch (ConfigSourceException e) {
223+
if (collectConfig) {
224+
ConfigCollector.get().put(key, e.getRawValue(), source.origin());
225+
}
205226
} catch (NumberFormatException ex) {
206227
// continue
207228
}
@@ -252,12 +273,18 @@ public Map<String, String> getMergedMap(String key, String... aliases) {
252273
// https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/html/boot-features-external-config.html
253274
// We reverse iterate to allow overrides
254275
for (int i = sources.length - 1; 0 <= i; i--) {
255-
String value = sources[i].get(key, aliases);
256-
Map<String, String> parsedMap = ConfigConverter.parseMap(value, key);
257-
if (!parsedMap.isEmpty()) {
258-
origin = sources[i].origin();
276+
try {
277+
String value = sources[i].get(key, aliases);
278+
Map<String, String> parsedMap = ConfigConverter.parseMap(value, key);
279+
if (!parsedMap.isEmpty()) {
280+
origin = sources[i].origin();
281+
}
282+
merged.putAll(parsedMap);
283+
} catch (ConfigSourceException e) {
284+
if (collectConfig) {
285+
ConfigCollector.get().put(key, e.getRawValue(), sources[i].origin());
286+
}
259287
}
260-
merged.putAll(parsedMap);
261288
}
262289
if (collectConfig) {
263290
ConfigCollector.get().put(key, merged, origin);
@@ -273,13 +300,19 @@ public Map<String, String> getMergedTagsMap(String key, String... aliases) {
273300
// https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/html/boot-features-external-config.html
274301
// We reverse iterate to allow overrides
275302
for (int i = sources.length - 1; 0 <= i; i--) {
276-
String value = sources[i].get(key, aliases);
277-
Map<String, String> parsedMap =
278-
ConfigConverter.parseTraceTagsMap(value, ':', Arrays.asList(',', ' '));
279-
if (!parsedMap.isEmpty()) {
280-
origin = sources[i].origin();
303+
try {
304+
String value = sources[i].get(key, aliases);
305+
Map<String, String> parsedMap =
306+
ConfigConverter.parseTraceTagsMap(value, ':', Arrays.asList(',', ' '));
307+
if (!parsedMap.isEmpty()) {
308+
origin = sources[i].origin();
309+
}
310+
merged.putAll(parsedMap);
311+
} catch (ConfigSourceException e) {
312+
if (collectConfig) {
313+
ConfigCollector.get().put(key, e.getRawValue(), sources[i].origin());
314+
}
281315
}
282-
merged.putAll(parsedMap);
283316
}
284317
if (collectConfig) {
285318
ConfigCollector.get().put(key, merged, origin);
@@ -295,12 +328,18 @@ public Map<String, String> getOrderedMap(String key) {
295328
// https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/html/boot-features-external-config.html
296329
// We reverse iterate to allow overrides
297330
for (int i = sources.length - 1; 0 <= i; i--) {
298-
String value = sources[i].get(key);
299-
Map<String, String> parsedMap = ConfigConverter.parseOrderedMap(value, key);
300-
if (!parsedMap.isEmpty()) {
301-
origin = sources[i].origin();
331+
try {
332+
String value = sources[i].get(key);
333+
Map<String, String> parsedMap = ConfigConverter.parseOrderedMap(value, key);
334+
if (!parsedMap.isEmpty()) {
335+
origin = sources[i].origin();
336+
}
337+
merged.putAll(parsedMap);
338+
} catch (ConfigSourceException e) {
339+
if (collectConfig) {
340+
ConfigCollector.get().put(key, e.getRawValue(), sources[i].origin());
341+
}
302342
}
303-
merged.putAll(parsedMap);
304343
}
305344
if (collectConfig) {
306345
ConfigCollector.get().put(key, merged, origin);
@@ -318,13 +357,20 @@ public Map<String, String> getMergedMapWithOptionalMappings(
318357
// We reverse iterate to allow overrides
319358
for (String key : keys) {
320359
for (int i = sources.length - 1; 0 <= i; i--) {
321-
String value = sources[i].get(key);
322-
Map<String, String> parsedMap =
323-
ConfigConverter.parseMapWithOptionalMappings(value, key, defaultPrefix, lowercaseKeys);
324-
if (!parsedMap.isEmpty()) {
325-
origin = sources[i].origin();
360+
try {
361+
String value = sources[i].get(key);
362+
Map<String, String> parsedMap =
363+
ConfigConverter.parseMapWithOptionalMappings(
364+
value, key, defaultPrefix, lowercaseKeys);
365+
if (!parsedMap.isEmpty()) {
366+
origin = sources[i].origin();
367+
}
368+
merged.putAll(parsedMap);
369+
} catch (ConfigSourceException e) {
370+
if (collectConfig) {
371+
ConfigCollector.get().put(key, e.getRawValue(), sources[i].origin());
372+
}
326373
}
327-
merged.putAll(parsedMap);
328374
}
329375
if (collectConfig) {
330376
ConfigCollector.get().put(key, merged, origin);
@@ -500,7 +546,7 @@ private static Properties loadConfigurationFile(ConfigProvider configProvider) {
500546
}
501547

502548
public abstract static class Source {
503-
public final String get(String key, String... aliases) {
549+
public final String get(String key, String... aliases) throws ConfigSourceException {
504550
String value = get(key);
505551
if (value != null) {
506552
return value;
@@ -514,7 +560,7 @@ public final String get(String key, String... aliases) {
514560
return null;
515561
}
516562

517-
protected abstract String get(String key);
563+
protected abstract String get(String key) throws ConfigSourceException;
518564

519565
public abstract ConfigOrigin origin();
520566
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package datadog.trace.bootstrap.config.provider;
2+
3+
/**
4+
* Exception thrown when a ConfigProvider.Source encounters an error (e.g., parsing, IO, or format
5+
* error) while retrieving a configuration value.
6+
*/
7+
public class ConfigSourceException extends Exception {
8+
private final Object rawValue;
9+
10+
public ConfigSourceException(String message, Object rawValue, Throwable cause) {
11+
super(message, cause);
12+
this.rawValue = rawValue;
13+
}
14+
15+
public ConfigSourceException(Object rawValue) {
16+
this.rawValue = rawValue;
17+
}
18+
19+
/** Returns the raw value that caused the exception, if available. */
20+
public Object getRawValue() {
21+
return rawValue;
22+
}
23+
}

internal-api/src/test/groovy/datadog/trace/bootstrap/config/provider/ConfigProviderTest.groovy

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package datadog.trace.bootstrap.config.provider
22

3+
import datadog.trace.api.ConfigOrigin
34
import datadog.trace.test.util.DDSpecification
45
import spock.lang.Shared
56

@@ -45,4 +46,63 @@ class ConfigProviderTest extends DDSpecification {
4546
"default" | null | "alias2" | "default"
4647
null | "alias1" | "alias2" | "alias1"
4748
}
49+
50+
def "ConfigProvider handles ConfigSourceException gracefully"() {
51+
given:
52+
def throwingSource = new ConfigProvider.Source() {
53+
@Override
54+
protected String get(String key) throws ConfigSourceException {
55+
throw new ConfigSourceException("raw")
56+
}
57+
@Override
58+
ConfigOrigin origin() {
59+
ConfigOrigin.ENV
60+
}
61+
}
62+
// Create a provider with a collector
63+
def provider = new ConfigProvider(true, throwingSource)
64+
65+
expect:
66+
//Any "get" method should return the default value, if provided
67+
provider.getString("any.key", "default") == "default"
68+
provider.getBoolean("any.key", true) == true
69+
provider.getInteger("any.key", 42) == 42
70+
provider.getLong("any.key", 123L) == 123L
71+
provider.getFloat("any.key", 1.23f) == 1.23f
72+
provider.getDouble("any.key", 2.34d) == 2.34d
73+
provider.getList("any.key", ["a", "b"]) == ["a", "b"]
74+
provider.getSet("any.key", ["x", "y"] as Set) == ["x", "y"] as Set
75+
}
76+
77+
def "ConfigProvider skips sources that throw ConfigSourceException and uses next available value"() {
78+
given:
79+
def throwingSource = new ConfigProvider.Source() {
80+
@Override
81+
protected String get(String key) throws ConfigSourceException {
82+
throw new ConfigSourceException("raw")
83+
}
84+
@Override
85+
ConfigOrigin origin() {
86+
ConfigOrigin.ENV
87+
}
88+
}
89+
90+
def workingSource = new ConfigProvider.Source() {
91+
@Override
92+
protected String get(String key) throws ConfigSourceException {
93+
if (key == "any.key") {
94+
return "fromSecondSource"
95+
}
96+
return null
97+
}
98+
@Override
99+
ConfigOrigin origin() {
100+
ConfigOrigin.JVM_PROP
101+
}
102+
}
103+
def provider = new ConfigProvider(true, throwingSource, workingSource)
104+
105+
expect:
106+
provider.getString("any.key", "default") == "fromSecondSource"
107+
}
48108
}

0 commit comments

Comments
 (0)