mirror of
https://github.com/jlengrand/helidon.git
synced 2026-03-10 08:21:17 +00:00
SE Config can be created from MP Config (#2060)
* MP to SE config now works including environment variables. * Empty config now does not create an executor service. * Using the same classloader when null is sent in MP config. * Added env var test. * Ensure the reference config uses its own configuration (intermitent failure fix) Signed-off-by: Tomas Langer <tomas.langer@oracle.com> Co-authored-by: Romain Grecourt <romain.grecourt@oracle.com>
This commit is contained in:
@@ -61,4 +61,18 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<environmentVariables>
|
||||
<FOO_BAR>mapped-env-value</FOO_BAR>
|
||||
</environmentVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -16,11 +16,8 @@
|
||||
|
||||
package io.helidon.config.mp;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.helidon.config.ConfigSources;
|
||||
import io.helidon.config.OverrideSources;
|
||||
|
||||
import org.eclipse.microprofile.config.Config;
|
||||
|
||||
@@ -49,30 +46,18 @@ public final class MpConfig {
|
||||
return (io.helidon.config.Config) mpConfig;
|
||||
}
|
||||
|
||||
io.helidon.config.Config.Builder builder = io.helidon.config.Config.builder()
|
||||
io.helidon.config.Config mapper = io.helidon.config.Config.builder()
|
||||
.sources(ConfigSources.empty())
|
||||
.overrides(OverrideSources.empty())
|
||||
.disableEnvironmentVariablesSource()
|
||||
.disableSystemPropertiesSource()
|
||||
.disableParserServices()
|
||||
.disableFilterServices()
|
||||
.disableCaching()
|
||||
.disableParserServices();
|
||||
|
||||
if (mpConfig instanceof MpConfigImpl) {
|
||||
((MpConfigImpl) mpConfig).converters()
|
||||
.forEach((clazz, converter) -> {
|
||||
Class<Object> cl = (Class<Object>) clazz;
|
||||
builder.addStringMapper(cl, converter::convert);
|
||||
});
|
||||
}
|
||||
|
||||
Map<String, String> allConfig = new HashMap<>();
|
||||
mpConfig.getPropertyNames()
|
||||
.forEach(it -> {
|
||||
// covering the condition where a config key disappears between getting the property names and requesting
|
||||
// the value
|
||||
Optional<String> optionalValue = mpConfig.getOptionalValue(it, String.class);
|
||||
optionalValue.ifPresent(value -> allConfig.put(it, value));
|
||||
});
|
||||
|
||||
return builder.addSource(ConfigSources.create(allConfig))
|
||||
.disableValueResolving()
|
||||
.changesExecutor(command -> {})
|
||||
.build();
|
||||
|
||||
return new SeConfig(mapper, mpConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,9 +54,12 @@ public class MpConfigProviderResolver extends ConfigProviderResolver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config getConfig(ClassLoader loader) {
|
||||
if (null == loader) {
|
||||
loader = ClassLoader.getSystemClassLoader();
|
||||
public Config getConfig(ClassLoader classLoader) {
|
||||
ClassLoader loader;
|
||||
if (classLoader == null) {
|
||||
loader = Thread.currentThread().getContextClassLoader();
|
||||
} else {
|
||||
loader = classLoader;
|
||||
}
|
||||
Lock lock = RW_LOCK.readLock();
|
||||
try {
|
||||
@@ -102,10 +105,17 @@ public class MpConfigProviderResolver extends ConfigProviderResolver {
|
||||
|
||||
@Override
|
||||
public void registerConfig(Config config, ClassLoader classLoader) {
|
||||
ClassLoader usedClassloader;
|
||||
if (null == classLoader) {
|
||||
usedClassloader = Thread.currentThread().getContextClassLoader();
|
||||
} else {
|
||||
usedClassloader = classLoader;
|
||||
}
|
||||
|
||||
Lock lock = RW_LOCK.writeLock();
|
||||
try {
|
||||
lock.lock();
|
||||
doRegisterConfig(config, classLoader);
|
||||
doRegisterConfig(config, usedClassloader);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Oracle and/or its affiliates.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.helidon.config.mp;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.helidon.common.GenericType;
|
||||
import io.helidon.config.Config;
|
||||
import io.helidon.config.ConfigMappingException;
|
||||
import io.helidon.config.ConfigValue;
|
||||
import io.helidon.config.ConfigValues;
|
||||
import io.helidon.config.MissingValueException;
|
||||
|
||||
/**
|
||||
* Implementation of SE config backed by MP config.
|
||||
*/
|
||||
class SeConfig implements Config {
|
||||
private static final Pattern SPLIT_PATTERN = Pattern.compile("(?<!\\\\),");
|
||||
private static final Pattern ESCAPED_COMMA_PATTERN = Pattern.compile("\\,", Pattern.LITERAL);
|
||||
|
||||
private final Map<Key, SeConfig> children = new ConcurrentHashMap<>();
|
||||
private final io.helidon.config.Config mapper;
|
||||
private final Key prefix;
|
||||
private final Key key;
|
||||
private final Key fullKey;
|
||||
private final org.eclipse.microprofile.config.Config delegate;
|
||||
private final MpConfigImpl delegateImpl;
|
||||
private final String stringKey;
|
||||
private final String stringPrefix;
|
||||
|
||||
SeConfig(io.helidon.config.Config mapper,
|
||||
Key prefix,
|
||||
Key key,
|
||||
Key fullKey,
|
||||
org.eclipse.microprofile.config.Config delegate,
|
||||
MpConfigImpl delegateImpl) {
|
||||
this.mapper = mapper;
|
||||
this.prefix = prefix;
|
||||
this.key = key;
|
||||
this.fullKey = fullKey;
|
||||
this.delegate = delegate;
|
||||
this.stringKey = fullKey.toString();
|
||||
this.stringPrefix = prefix.toString();
|
||||
this.delegateImpl = delegateImpl;
|
||||
}
|
||||
|
||||
SeConfig(io.helidon.config.Config mapper, org.eclipse.microprofile.config.Config delegate) {
|
||||
this.mapper = mapper;
|
||||
this.prefix = Key.create("");
|
||||
this.key = prefix;
|
||||
this.fullKey = prefix;
|
||||
this.delegate = delegate;
|
||||
this.stringKey = prefix.child(key).toString();
|
||||
this.stringPrefix = prefix.toString();
|
||||
|
||||
if (delegate instanceof MpConfigImpl) {
|
||||
this.delegateImpl = (MpConfigImpl) delegate;
|
||||
} else {
|
||||
this.delegateImpl = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant timestamp() {
|
||||
return Instant.now();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key key() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config get(Key key) {
|
||||
return children.computeIfAbsent(key, it -> new SeConfig(mapper, prefix, key, fullKey.child(key), delegate, delegateImpl));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config detach() {
|
||||
return new SeConfig(mapper, fullKey, Key.create(""), fullKey, delegate, delegateImpl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type type() {
|
||||
// check if there are any sub-nodes that have prefix with our key
|
||||
boolean isObject = false;
|
||||
|
||||
Iterator<String> it = delegate.getPropertyNames().iterator();
|
||||
if (stringKey.isEmpty()) {
|
||||
if (!it.hasNext()) {
|
||||
return hasValue() ? Type.VALUE : Type.MISSING;
|
||||
}
|
||||
return Type.OBJECT;
|
||||
}
|
||||
|
||||
while (it.hasNext()) {
|
||||
String name = it.next();
|
||||
if (name.equals(stringKey)) {
|
||||
continue;
|
||||
}
|
||||
if (name.startsWith(stringKey + ".")) {
|
||||
isObject = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isObject) {
|
||||
return Type.OBJECT;
|
||||
}
|
||||
if (hasValue()) {
|
||||
return Type.VALUE;
|
||||
}
|
||||
|
||||
return Type.MISSING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasValue() {
|
||||
return currentValue().isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Config> traverse(Predicate<Config> predicate) {
|
||||
return asNodeList()
|
||||
.map(list -> list.stream()
|
||||
.filter(predicate)
|
||||
.map(node -> traverseSubNodes(node, predicate))
|
||||
.reduce(Stream.empty(), Stream::concat))
|
||||
.orElseThrow(MissingValueException.createSupplier(key()));
|
||||
|
||||
}
|
||||
|
||||
private Stream<Config> traverseSubNodes(Config config, Predicate<Config> predicate) {
|
||||
if (config.type().isLeaf()) {
|
||||
return Stream.of(config);
|
||||
} else {
|
||||
return config.asNodeList()
|
||||
.map(list -> list.stream()
|
||||
.filter(predicate)
|
||||
.map(node -> traverseSubNodes(node, predicate))
|
||||
.reduce(Stream.of(config), Stream::concat))
|
||||
.orElseThrow(MissingValueException.createSupplier(key()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T convert(Class<T> type, String value) throws ConfigMappingException {
|
||||
try {
|
||||
return impl().findConverter(type)
|
||||
.convert(value);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
return mapper.convert(type, value);
|
||||
} catch (ConfigMappingException ignored) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> ConfigValue<T> as(GenericType<T> genericType) {
|
||||
if (genericType.isClass()) {
|
||||
return (ConfigValue<T>) as(genericType.rawType());
|
||||
}
|
||||
throw new UnsupportedOperationException("MP Configuration does not support generic types.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ConfigValue<T> as(Class<T> type) {
|
||||
return delegate.getOptionalValue(stringKey, type)
|
||||
.map(ConfigValues::simpleValue)
|
||||
.orElseGet(ConfigValues::empty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ConfigValue<T> as(Function<Config, T> mapper) {
|
||||
if (type() == Type.MISSING) {
|
||||
return ConfigValues.empty();
|
||||
}
|
||||
|
||||
return ConfigValues.simpleValue(mapper.apply(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ConfigValue<List<T>> asList(Class<T> type) throws ConfigMappingException {
|
||||
if (Config.class.equals(type)) {
|
||||
return toNodeList();
|
||||
}
|
||||
return asList(stringKey, type)
|
||||
.map(ConfigValues::simpleValue)
|
||||
.orElseGet(ConfigValues::empty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ConfigValue<List<T>> asList(Function<Config, T> mapper) throws ConfigMappingException {
|
||||
return asNodeList()
|
||||
.as(it -> it.stream()
|
||||
.map(mapper)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigValue<List<Config>> asNodeList() throws ConfigMappingException {
|
||||
return asList(Config.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigValue<Map<String, String>> asMap() throws MissingValueException {
|
||||
Type nodeType = type();
|
||||
if (nodeType == Type.MISSING || nodeType == Type.VALUE) {
|
||||
return ConfigValues.empty();
|
||||
}
|
||||
|
||||
Map<String, String> children = new HashMap<>();
|
||||
|
||||
for (String propertyName : delegate.getPropertyNames()) {
|
||||
if (stringKey.isEmpty()) {
|
||||
children.put(propertyName, delegate.getValue(propertyName, String.class));
|
||||
} else {
|
||||
if (propertyName.equals(stringKey)) {
|
||||
continue;
|
||||
}
|
||||
if (propertyName.startsWith(stringKey + ".")) {
|
||||
String noPrefix = propertyName.substring(stringPrefix.length() + 1);
|
||||
|
||||
children.put(noPrefix, delegate.getValue(propertyName, String.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ConfigValues.simpleValue(children);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type() + " " + stringKey + " = " + currentValue().orElse(null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> ConfigValue<List<T>> toNodeList() {
|
||||
Type nodeType = type();
|
||||
if (nodeType == Type.MISSING || nodeType == Type.VALUE) {
|
||||
return ConfigValues.empty();
|
||||
}
|
||||
|
||||
// this is an object or a list
|
||||
List<T> result = new LinkedList<>();
|
||||
Set<String> children = new HashSet<>();
|
||||
|
||||
for (String propertyName : delegate.getPropertyNames()) {
|
||||
if (stringKey.isEmpty()) {
|
||||
String noSuffix = propertyName;
|
||||
int dot = noSuffix.indexOf('.');
|
||||
if (dot > 0) {
|
||||
noSuffix = noSuffix.substring(0, dot);
|
||||
}
|
||||
children.add(noSuffix);
|
||||
} else {
|
||||
if (propertyName.equals(stringKey)) {
|
||||
continue;
|
||||
}
|
||||
if (propertyName.startsWith(stringKey + ".")) {
|
||||
String noSuffix = propertyName.substring(stringKey.length() + 1);
|
||||
int dot = noSuffix.indexOf('.');
|
||||
if (dot > 0) {
|
||||
noSuffix = noSuffix.substring(0, dot);
|
||||
}
|
||||
children.add(noSuffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (String child : children) {
|
||||
result.add((T) get(child));
|
||||
}
|
||||
|
||||
return ConfigValues.simpleValue(result);
|
||||
}
|
||||
|
||||
private Optional<String> currentValue() {
|
||||
return delegate.getOptionalValue(stringKey, String.class);
|
||||
}
|
||||
|
||||
private MpConfigImpl impl() {
|
||||
if (null == delegateImpl) {
|
||||
throw new IllegalStateException("Cannot convert to arbitrary types when the MP Config is not a Helidon "
|
||||
+ "implementation");
|
||||
}
|
||||
|
||||
return delegateImpl;
|
||||
}
|
||||
|
||||
private <T> Optional<List<T>> asList(String configKey,
|
||||
Class<T> typeArg) {
|
||||
// first try to see if we have a direct value
|
||||
Optional<String> optionalValue = delegate.getOptionalValue(configKey, String.class);
|
||||
if (optionalValue.isPresent()) {
|
||||
return Optional.of(toList(configKey, optionalValue.get(), typeArg));
|
||||
}
|
||||
|
||||
/*
|
||||
we also support indexed value
|
||||
e.g. for key "my.list" you can have both:
|
||||
my.list=12,13,14
|
||||
or (not and):
|
||||
my.list.0=12
|
||||
my.list.1=13
|
||||
*/
|
||||
|
||||
String indexedConfigKey = configKey + ".0";
|
||||
optionalValue = delegate.getOptionalValue(indexedConfigKey, String.class);
|
||||
if (optionalValue.isPresent()) {
|
||||
List<T> result = new LinkedList<>();
|
||||
|
||||
// first element is already in
|
||||
result.add(convert(indexedConfigKey, optionalValue.get(), typeArg));
|
||||
|
||||
// hardcoded limit to lists of 1000 elements
|
||||
for (int i = 1; i < 1000; i++) {
|
||||
indexedConfigKey = configKey + "." + i;
|
||||
optionalValue = delegate.getOptionalValue(indexedConfigKey, String.class);
|
||||
if (optionalValue.isPresent()) {
|
||||
result.add(convert(indexedConfigKey, optionalValue.get(), typeArg));
|
||||
} else {
|
||||
// finish the iteration on first missing index
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Optional.of(result);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private <T> List<T> toList(String configKey,
|
||||
String stringValue,
|
||||
Class<T> typeArg) {
|
||||
if (stringValue.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
// we have a comma separated list
|
||||
List<T> result = new LinkedList<>();
|
||||
for (String value : toArray(stringValue)) {
|
||||
result.add(convert(configKey, value, typeArg));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static String[] toArray(String stringValue) {
|
||||
String[] values = SPLIT_PATTERN.split(stringValue, -1);
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
String value = values[i];
|
||||
values[i] = ESCAPED_COMMA_PATTERN.matcher(value).replaceAll(Matcher.quoteReplacement(","));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T convert(String key, String value, Class<T> type) {
|
||||
if (null == value) {
|
||||
return null;
|
||||
}
|
||||
if (String.class.equals(type)) {
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
try {
|
||||
return impl().getConverter(type)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Did not find converter for type "
|
||||
+ type.getName()
|
||||
+ ", for key "
|
||||
+ key))
|
||||
.convert(value);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
return mapper.convert(type, value);
|
||||
} catch (ConfigMappingException ignored) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
package io.helidon.config.mp;
|
||||
|
||||
import org.eclipse.microprofile.config.Config;
|
||||
import org.eclipse.microprofile.config.ConfigProvider;
|
||||
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -35,7 +35,10 @@ public class MpConfigReferenceTest {
|
||||
static void initClass() {
|
||||
System.setProperty("value2", VALUE_2);
|
||||
|
||||
config = ConfigProvider.getConfig();
|
||||
config = ConfigProviderResolver.instance()
|
||||
.getBuilder()
|
||||
.addDefaultSources()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -30,6 +30,7 @@ import io.helidon.config.spi.ConfigMapper;
|
||||
import io.helidon.config.spi.ConfigMapperProvider;
|
||||
|
||||
import org.eclipse.microprofile.config.Config;
|
||||
import org.eclipse.microprofile.config.ConfigProvider;
|
||||
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
|
||||
import org.eclipse.microprofile.config.spi.ConfigSource;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@@ -274,5 +275,31 @@ public class MpConfigTest {
|
||||
counter.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEnvVar() {
|
||||
ConfigProviderResolver instance = ConfigProviderResolver.instance();
|
||||
ClassLoader myCl = Thread.currentThread().getContextClassLoader();
|
||||
Config current = ConfigProvider.getConfig(myCl);
|
||||
|
||||
try {
|
||||
instance.registerConfig(instance.getBuilder()
|
||||
.withSources(MpConfigSources.environmentVariables())
|
||||
.build(),
|
||||
myCl);
|
||||
Config myConfig = instance.getConfig(myCl);
|
||||
// this must not throw an exception - path should be on any environment
|
||||
// and the MP env var processing should make it available
|
||||
String fooBar = myConfig.getValue("foo.bar", String.class);
|
||||
assertThat(fooBar, is("mapped-env-value"));
|
||||
|
||||
io.helidon.config.Config helidonConfig = (io.helidon.config.Config) myConfig;
|
||||
// should work if we use it as SE as well
|
||||
fooBar = helidonConfig.get("foo.bar").asString().get();
|
||||
assertThat(fooBar, is("mapped-env-value"));
|
||||
} finally {
|
||||
instance.registerConfig(current, myCl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ import java.util.concurrent.Executors;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.helidon.common.GenericType;
|
||||
import io.helidon.common.Prioritized;
|
||||
@@ -53,8 +52,6 @@ class BuilderImpl implements Config.Builder {
|
||||
/*
|
||||
* Config sources
|
||||
*/
|
||||
// sources to be sorted by priority
|
||||
private final List<HelidonSourceWithPriority> prioritizedSources = new ArrayList<>();
|
||||
// sources "pre-sorted" - all user defined sources without priority will be ordered
|
||||
// as added, as well as config sources from meta configuration
|
||||
private final List<ConfigSource> sources = new LinkedList<>();
|
||||
@@ -118,7 +115,6 @@ class BuilderImpl implements Config.Builder {
|
||||
public Config.Builder sources(List<Supplier<? extends ConfigSource>> sourceSuppliers) {
|
||||
// replace current config sources with the ones provided
|
||||
sources.clear();
|
||||
prioritizedSources.clear();
|
||||
|
||||
sourceSuppliers.stream()
|
||||
.map(Supplier::get)
|
||||
@@ -414,7 +410,7 @@ class BuilderImpl implements Config.Builder {
|
||||
envVarAliasGeneratorEnabled = true;
|
||||
}
|
||||
|
||||
boolean nothingConfigured = sources.isEmpty() && prioritizedSources.isEmpty();
|
||||
boolean nothingConfigured = sources.isEmpty();
|
||||
|
||||
if (nothingConfigured) {
|
||||
// use meta configuration to load all sources
|
||||
@@ -429,29 +425,12 @@ class BuilderImpl implements Config.Builder {
|
||||
sources.stream()
|
||||
.map(context::sourceRuntimeBase)
|
||||
.forEach(targetSources::add);
|
||||
|
||||
// prioritized sources are next
|
||||
targetSources.addAll(mergePrioritized(context));
|
||||
}
|
||||
|
||||
// targetSources now contain runtimes correctly ordered for each config source
|
||||
return new ConfigSourcesRuntime(targetSources, mergingStrategy);
|
||||
}
|
||||
|
||||
private List<ConfigSourceRuntimeImpl> mergePrioritized(ConfigContextImpl context) {
|
||||
List<PrioritizedConfigSource> allPrioritized = new ArrayList<>();
|
||||
prioritizedSources.stream()
|
||||
.map(it -> new PrioritizedConfigSource(it, context))
|
||||
.forEach(allPrioritized::add);
|
||||
|
||||
Priorities.sort(allPrioritized);
|
||||
|
||||
return allPrioritized
|
||||
.stream()
|
||||
.map(it -> it.runtime(context))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@SuppressWarnings("ParameterNumber")
|
||||
ProviderImpl createProvider(ConfigMapperManager configMapperManager,
|
||||
ConfigSourcesRuntime targetConfigSource,
|
||||
@@ -605,6 +584,7 @@ class BuilderImpl implements Config.Builder {
|
||||
.disableSystemPropertiesSource()
|
||||
.disableParserServices()
|
||||
.disableFilterServices()
|
||||
.changesExecutor(command -> {})
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Oracle and/or its affiliates.
|
||||
* Copyright (c) 2020 Oracle and/or its affiliates.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -65,7 +65,7 @@ final class ConnectorConfigHelper {
|
||||
Config incomingChannelConfig = rootConfig.get("mp.messaging.incoming");
|
||||
Config outgoingChannelConfig = rootConfig.get("mp.messaging.outgoing");
|
||||
|
||||
Config channelsConfig = (Config) ConnectorConfigBuilder
|
||||
Config channelsConfig = ConnectorConfigBuilder
|
||||
.create(incomingChannelConfig)
|
||||
.config(outgoingChannelConfig)
|
||||
.build();
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
package io.helidon.messaging;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.helidon.config.mp.MpConfig;
|
||||
|
||||
@@ -56,17 +56,25 @@ public class TestConfigurableConnector implements IncomingConnectorFactory, Outg
|
||||
|
||||
@Override
|
||||
public SubscriberBuilder<? extends Message<?>, Void> getSubscriberBuilder(final Config config) {
|
||||
io.helidon.config.Config helidonConfig = MpConfig.toHelidonConfig(config);
|
||||
printConfig(helidonConfig);
|
||||
printConfig(config);
|
||||
return ReactiveStreams.<Message<CompletableFuture<Map<String, String>>>>builder()
|
||||
.map(Message::getPayload)
|
||||
.forEach(f -> f.complete(helidonConfig
|
||||
.traverse()
|
||||
.map(c -> Map.entry(c.key().name(), c.asString().get()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))
|
||||
);
|
||||
.forEach(f -> f.complete(toMap(config)));
|
||||
}
|
||||
|
||||
private static Map<String, String> toMap(Config config) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
|
||||
config.getPropertyNames()
|
||||
.forEach(it -> {
|
||||
config.getOptionalValue(it, String.class).ifPresent(value -> result.put(it, value));
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
private static void printConfig(Config c) {
|
||||
toMap(c).forEach((key, value) -> System.out.println(key + ": " + value));
|
||||
}
|
||||
private static void printConfig(io.helidon.config.Config c) {
|
||||
c.asMap().orElse(Map.of()).forEach((key, value) -> System.out.println(key + ": " + value));
|
||||
}
|
||||
|
||||
@@ -21,12 +21,15 @@ import java.util.Map;
|
||||
|
||||
import io.helidon.config.Config;
|
||||
import io.helidon.config.ConfigSources;
|
||||
import io.helidon.config.mp.MpConfigSources;
|
||||
|
||||
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class AdHocConfigBuilderTest {
|
||||
|
||||
private static final String TEST_TOPIC_CONFIG = "TEST_TOPIC_CONFIG";
|
||||
@@ -38,6 +41,38 @@ class AdHocConfigBuilderTest {
|
||||
private static final String ADDITION_ATTR_2_VALUE = "addition-attr2-value";
|
||||
private static final String TEST_CONNECTOR = "test-connector";
|
||||
|
||||
private static ConfigProviderResolver resolver;
|
||||
private static ClassLoader cl;
|
||||
|
||||
@BeforeAll
|
||||
static void initClass() {
|
||||
resolver = ConfigProviderResolver.instance();
|
||||
cl = Thread.currentThread().getContextClassLoader();
|
||||
}
|
||||
|
||||
@Test
|
||||
void currentContextMp() {
|
||||
// MP tests ensure that the MP Config -> SE Config casting works as expected
|
||||
// when obtained from config resolver
|
||||
Map<String, String> propMap = Map.of(
|
||||
"mp.messaging.outcoming.test-channel.key.serializer", AdHocConfigBuilderTest.class.getName()
|
||||
);
|
||||
|
||||
resolver.registerConfig(resolver.getBuilder()
|
||||
.withSources(MpConfigSources.create(propMap))
|
||||
.build(), cl);
|
||||
|
||||
Config config = (Config) resolver.getConfig(cl);
|
||||
|
||||
org.eclipse.microprofile.config.Config c = AdHocConfigBuilder
|
||||
.from(config.get("mp.messaging.outcoming.test-channel"))
|
||||
.put(TEST_KEY, TEST_TOPIC_CUSTOM)
|
||||
.build();
|
||||
|
||||
assertThat(c.getValue(TEST_KEY, String.class), is(TEST_TOPIC_CUSTOM));
|
||||
assertThat(c.getValue("key.serializer", String.class), is(AdHocConfigBuilderTest.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void currentContext() {
|
||||
Map<String, String> propMap = Map.of(
|
||||
@@ -57,6 +92,27 @@ class AdHocConfigBuilderTest {
|
||||
assertThat(c.getValue("key.serializer", String.class), is(AdHocConfigBuilderTest.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customValueOverrideMp() {
|
||||
Map<String, String> propMap = Map.of(
|
||||
"mp.messaging.outcoming.test-channel." + TEST_KEY, TEST_TOPIC_CONFIG,
|
||||
"mp.messaging.outcoming.test-channel.key.serializer", AdHocConfigBuilderTest.class.getName()
|
||||
);
|
||||
|
||||
resolver.registerConfig(resolver.getBuilder()
|
||||
.withSources(MpConfigSources.create(propMap))
|
||||
.build(), cl);
|
||||
|
||||
Config config = (Config) resolver.getConfig(cl);
|
||||
|
||||
org.eclipse.microprofile.config.Config c = AdHocConfigBuilder
|
||||
.from(config.get("mp.messaging.outcoming.test-channel"))
|
||||
.put(TEST_KEY, TEST_TOPIC_CUSTOM)
|
||||
.build();
|
||||
|
||||
assertThat(c.getValue(TEST_KEY, String.class), is(TEST_TOPIC_CUSTOM));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customValueOverride() {
|
||||
Map<String, String> propMap = Map.of(
|
||||
@@ -76,6 +132,38 @@ class AdHocConfigBuilderTest {
|
||||
assertThat(c.getValue(TEST_KEY, String.class), is(TEST_TOPIC_CUSTOM));
|
||||
}
|
||||
|
||||
@Test
|
||||
void putAllTestMp() {
|
||||
Map<String, String> propMap = Map.of(
|
||||
"mp.messaging.outcoming.test-channel." + TEST_KEY, TEST_TOPIC_CONFIG
|
||||
);
|
||||
|
||||
Map<String, String> propMap2 = Map.of(
|
||||
"mp.messaging.connector." + TEST_CONNECTOR + "." + ADDITION_ATTR_1, ADDITION_ATTR_1_VALUE,
|
||||
"mp.messaging.connector." + TEST_CONNECTOR + "." + ADDITION_ATTR_2, ADDITION_ATTR_2_VALUE
|
||||
);
|
||||
|
||||
resolver.registerConfig(resolver.getBuilder()
|
||||
.withSources(MpConfigSources.create(propMap))
|
||||
.build(), cl);
|
||||
|
||||
Config config = (Config) resolver.getConfig(cl);
|
||||
|
||||
resolver.registerConfig(resolver.getBuilder()
|
||||
.withSources(MpConfigSources.create(propMap2))
|
||||
.build(), cl);
|
||||
|
||||
Config config2 = (Config) resolver.getConfig(cl);
|
||||
|
||||
org.eclipse.microprofile.config.Config c = AdHocConfigBuilder
|
||||
.from(config.get("mp.messaging.outcoming.test-channel"))
|
||||
.putAll(config2.get("mp.messaging.connector." + TEST_CONNECTOR))
|
||||
.build();
|
||||
|
||||
assertThat(c.getValue(ADDITION_ATTR_1, String.class), is(ADDITION_ATTR_1_VALUE));
|
||||
assertThat(c.getValue(ADDITION_ATTR_2, String.class), is(ADDITION_ATTR_2_VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void putAllTest() {
|
||||
Map<String, String> propMap = Map.of(
|
||||
|
||||
Reference in New Issue
Block a user