Skip to content

Commit d0d7c64

Browse files
authored
4.x: Upgrade to MP Config 3.1 and fix an issue with profile specific properties (helidon-io#8757)
* Correctly handle profile-specific properties * Add test for profile specific properties * Upgrade MP Config to 3.1 * Return a ConfigValue even when expression on rhs does not resolve * Update docs for microprofile config 3.1 * Add ConfigValue test for missing expression
1 parent cf82198 commit d0d7c64

6 files changed

Lines changed: 82 additions & 21 deletions

File tree

config/config-mp/src/main/java/io/helidon/config/mp/MpConfigImpl.java

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2020, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -107,13 +107,18 @@ class MpConfigImpl implements Config {
107107

108108
@Override
109109
public ConfigValue getConfigValue(String key) {
110+
111+
ConfigValue value = findConfigValue(key)
112+
.orElse(new ConfigValueImpl(key, null, null, null, 0));
113+
110114
if (configProfile == null) {
111-
return findConfigValue(key)
112-
.orElseGet(() -> new ConfigValueImpl(key, null, null, null, 0));
115+
return value;
113116
}
114-
return findConfigValue("%" + configProfile + "." + key)
115-
.or(() -> findConfigValue(key))
116-
.orElseGet(() -> new ConfigValueImpl(key, null, null, null, 0));
117+
118+
ConfigValue profileValue = findConfigValue("%" + configProfile + "." + key)
119+
.orElse(value);
120+
121+
return value.getSourceOrdinal() > profileValue.getSourceOrdinal() ? value : profileValue;
117122
}
118123

119124
@Override
@@ -126,12 +131,7 @@ public <T> T getValue(String propertyName, Class<T> propertyType) {
126131
@SuppressWarnings("unchecked")
127132
@Override
128133
public <T> Optional<T> getOptionalValue(String propertyName, Class<T> propertyType) {
129-
if (configProfile == null) {
130-
return optionalValue(propertyName, propertyType);
131-
}
132-
133-
return optionalValue("%" + configProfile + "." + propertyName, propertyType)
134-
.or(() -> optionalValue(propertyName, propertyType));
134+
return optionalValue(propertyName, propertyType);
135135
}
136136

137137
@SuppressWarnings("unchecked")
@@ -187,9 +187,9 @@ private <T> Optional<T> optionalValue(String propertyName, Class<T> propertyType
187187
return Optional.empty();
188188
}
189189
} else {
190-
return findConfigValue(propertyName)
191-
.map(ConfigValue::getValue)
192-
.map(it -> convert(propertyName, propertyType, it));
190+
Optional<ConfigValue> value = Optional.of(getConfigValue(propertyName));
191+
return value.map(ConfigValue::getValue)
192+
.map(it -> convert(propertyName, propertyType, it));
193193
}
194194
}
195195

@@ -314,6 +314,7 @@ private <T> T convert(String propertyName, Class<T> type, String value) {
314314
}
315315

316316
private Optional<ConfigValue> findConfigValue(String propertyName) {
317+
317318
for (ConfigSource source : sources) {
318319
String value = source.getValue(propertyName);
319320

@@ -341,7 +342,7 @@ private Optional<ConfigValue> findConfigValue(String propertyName) {
341342
.map(it -> new ConfigValueImpl(propertyName, it, rawValue, source.getName(), source.getOrdinal()));
342343
} catch (NoSuchElementException e) {
343344
// Property expression does not resolve
344-
return Optional.empty();
345+
return Optional.of(new ConfigValueImpl(propertyName, null, rawValue, source.getName(), source.getOrdinal()));
345346
}
346347
}
347348

config/config-mp/src/test/java/io/helidon/config/mp/MpConfigReferenceTest.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2020, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,13 +20,16 @@
2020
import java.util.Optional;
2121

2222
import org.eclipse.microprofile.config.Config;
23+
import org.eclipse.microprofile.config.ConfigValue;
2324
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
2425
import org.junit.jupiter.api.BeforeAll;
2526
import org.junit.jupiter.api.Test;
2627

28+
import static org.hamcrest.CoreMatchers.endsWith;
2729
import static org.hamcrest.CoreMatchers.is;
2830
import static org.hamcrest.CoreMatchers.notNullValue;
2931
import static org.hamcrest.MatcherAssert.assertThat;
32+
import static org.hamcrest.Matchers.nullValue;
3033
import static org.junit.jupiter.api.Assertions.assertThrows;
3134

3235
public class MpConfigReferenceTest {
@@ -65,6 +68,15 @@ void testMissingRefs() {
6568
// since Config 2.0, missing references must throw an exception
6669
assertThrows(NoSuchElementException.class, () -> config.getValue("referencing4-1", String.class));
6770
assertThrows(NoSuchElementException.class, () -> config.getValue( "referencing4-2", String.class));
71+
72+
// MP Config 3.1 TCK requires well-formed ConfigValue when missing reference
73+
ConfigValue configValue = config.getConfigValue("referencing4-1");
74+
assertThat(configValue, notNullValue());
75+
assertThat(configValue.getName(), is("referencing4-1"));
76+
assertThat(configValue.getValue(), nullValue());
77+
assertThat(configValue.getRawValue(), is("${missing}"));
78+
assertThat(configValue.getSourceName(), endsWith("microprofile-config.properties"));
79+
assertThat(configValue.getSourceOrdinal(), is(100));
6880
}
6981

7082
@Test

config/config-mp/src/test/java/io/helidon/config/mp/MpConfigSourcesTest.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import java.io.ByteArrayInputStream;
2020
import java.io.StringReader;
2121
import java.nio.charset.StandardCharsets;
22+
import java.util.Collections;
23+
import java.util.HashMap;
24+
import java.util.List;
2225
import java.util.Map;
2326
import java.util.Optional;
2427
import java.util.concurrent.ConcurrentHashMap;
@@ -60,6 +63,52 @@ void testHelidonMap() {
6063
assertThat(mpSource.getValue("key.third"), is("<>{}().,:;/|\\~`?!@#$%^&*-=+*"));
6164
}
6265

66+
@Test
67+
void testProfileSpecificProperty() {
68+
Map<String, String> values = Map.of(
69+
"%dev.vehicle.name", "car",
70+
"vehicle.name", "bike",
71+
"%dev.vehicle.color", "blue",
72+
"vehicle.color", "red",
73+
"%dev.vehicle.size", "large"
74+
);
75+
org.eclipse.microprofile.config.spi.ConfigSource mapSource = MpConfigSources.create(ConfigSources.create(values).build());
76+
assertThat(mapSource.getOrdinal(), is(100));
77+
assertThat(mapSource.getValue("vehicle.name"), is("bike"));
78+
79+
// One data source. The profile specific property should take precedence
80+
MpConfigImpl config = new MpConfigImpl(List.of(mapSource), new HashMap<>(), Collections.emptyList(), "dev");
81+
assertThat(config.getConfigValue("vehicle.name").getValue(), is("car"));
82+
assertThat(config.getOptionalValue("vehicle.name", String.class).orElse("error"), is("car"));
83+
84+
System.setProperty("vehicle.name", "jet");
85+
System.setProperty("%dev.vehicle.make", "tucker");
86+
org.eclipse.microprofile.config.spi.ConfigSource propertySource = MpConfigSources.systemProperties();
87+
assertThat(propertySource.getOrdinal(), is(400));
88+
assertThat(propertySource.getValue("vehicle.name"), is("jet"));
89+
90+
// Create Config from both data sources with the "dev" profile
91+
config = new MpConfigImpl(List.of(propertySource, mapSource), new HashMap<>(), Collections.emptyList(), "dev");
92+
93+
// The vanilla property in the higher ordinal data source should trump the profile specific property in the
94+
// lower ordinal data source
95+
assertThat(config.getConfigValue("vehicle.name").getValue(), is("jet"));
96+
assertThat(config.getOptionalValue("vehicle.name", String.class).orElse("error"), is("jet"));
97+
98+
// Within one DataSource the profile specific property takes precedence
99+
assertThat(config.getConfigValue("vehicle.color").getValue(), is("blue"));
100+
assertThat(config.getOptionalValue("vehicle.color", String.class).orElse("error"), is("blue"));
101+
102+
// Make sure missing vanilla values do not mess things up
103+
assertThat(config.getConfigValue("vehicle.size").getValue(), is("large"));
104+
assertThat(config.getOptionalValue("vehicle.size", String.class).orElse("error"), is("large"));
105+
assertThat(config.getConfigValue("vehicle.make").getValue(), is("tucker"));
106+
assertThat(config.getOptionalValue("vehicle.make", String.class).orElse("error"), is("tucker"));
107+
108+
System.clearProperty("vehicle.name");
109+
System.clearProperty("%dev.vehicle.name");
110+
}
111+
63112
@Test
64113
void testHelidonParsable() {
65114
ParsableImpl helidonSource = new ParsableImpl();

dependencies/pom.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,7 @@
104104
<version.lib.micronaut>3.8.7</version.lib.micronaut>
105105
<version.lib.micronaut.data>3.4.3</version.lib.micronaut.data>
106106
<version.lib.micronaut.sql>4.8.0</version.lib.micronaut.sql>
107-
<!-- FIXME upgrade to 3.1 when it is released in Maven -->
108-
<version.lib.microprofile-config>3.0.3</version.lib.microprofile-config>
107+
<version.lib.microprofile-config>3.1</version.lib.microprofile-config>
109108
<!-- FIXME upgrade to 4.1 when it is released in Maven -->
110109
<version.lib.microprofile-fault-tolerance-api>4.0.2</version.lib.microprofile-fault-tolerance-api>
111110
<version.lib.microprofile-graphql>2.0</version.lib.microprofile-graphql>

docs/src/main/asciidoc/includes/attributes.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ endif::[]
4343
4444
// microprofile specifications
4545
:version-lib-microprofile-lra-api: 2.0
46-
:version-lib-microprofile-config: 3.0.3
46+
:version-lib-microprofile-config: 3.1
4747
:version-lib-microprofile-fault-tolerance-api: 4.0.2
4848
:version-lib-microprofile-graphql: 2.0
4949
:version-lib-microprofile-health: 4.0

docs/src/main/asciidoc/mp/config/introduction.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,4 @@ Step-by-step guide about using {spec-name} in your Helidon MP application.
243243
== Reference
244244
245245
* link:{microprofile-config-spec-url}[{spec-name} Specifications]
246-
* link:{microprofile-fault-tolerance-javadoc-url}[{spec-name} Javadocs]
246+
* link:{microprofile-config-javadoc-url}[{spec-name} Javadocs]

0 commit comments

Comments
 (0)