properties = new HashSet<>();
for (ConfigMappingInterface superType : type.getSuperTypes()) {
@@ -1009,4 +1019,252 @@ static Class> rawTypeOf(final Type type) {
throw ConfigMessages.msg.noRawType(type);
}
}
+
+ /**
+ * Constructs a representation of all {@link Property} names contained in the {@link ConfigMappingInterface}.
+ *
+ *
+ *
+ * - The first level
Map
key is each Class
name that is part of the mapping
+ * - The second level
Map
key is each String
path
+ * - The
Set
contains all {@link Property} String
names under the mapping path,
+ * including sub-elements and nested elements
+ *
+ *
+ * The path names do not include the {@link ConfigMapping#prefix()} because the same {@link ConfigMappingInterface}
+ * can be registered for multiple prefixes.
+ *
+ * The mapping class root {@link Property} use the empty String
for the path key.
+ *
+ * @param configMapping a {@link ConfigMappingInterface} representation of a {@link ConfigMapping} annotated class
+ * @return a Map
with the mapping properties names
+ */
+ public static Map>> getNames(final ConfigMappingInterface configMapping) {
+ Map>> names = new HashMap<>();
+ Map, Map>> properties = getProperties(configMapping);
+ for (Map.Entry, Map>> entry : properties.entrySet()) {
+ Map> groups = new HashMap<>();
+ for (Map.Entry> group : entry.getValue().entrySet()) {
+ groups.put(group.getKey(), group.getValue().keySet());
+ }
+ names.put(entry.getKey().getName(), groups);
+ }
+ return names;
+ }
+
+ /**
+ * Constructs a representation of all {@link Property} contained in the {@link ConfigMappingInterface}.
+ *
+ *
+ *
+ * - The first level
Map
key is each Class
that is part of the mapping
+ * - The second level
Map
key is each String
path in the mapping to a {@link Property}
+ * - The third level
Map
key is each String
{@link Property} name under the mapping
+ * path, including sub-elements and nested elements
+ *
+ *
+ * The path names do not include the {@link ConfigMapping#prefix()} because the same {@link ConfigMappingInterface}
+ * can be registered for multiple prefixes.
+ *
+ * The mapping class root {@link Property} use the empty String
for the path key.
+ *
+ * @param configMapping a {@link ConfigMappingInterface} representation of a {@link ConfigMapping} annotated class
+ * @return a Map
with {@link Property} values
+ */
+ public static Map, Map>> getProperties(final ConfigMappingInterface configMapping) {
+ Map, Map>> properties = new HashMap<>();
+ getProperties(new GroupProperty(null, null, configMapping), configMapping.getNamingStrategy(), new Path(), properties);
+ return properties;
+ }
+
+ private static void getProperties(
+ final GroupProperty groupProperty,
+ final NamingStrategy namingStrategy,
+ final Path path,
+ final Map, Map>> properties) {
+
+ ConfigMappingInterface groupType = groupProperty.getGroupType();
+ Map groupProperties = properties
+ .computeIfAbsent(groupType.getInterfaceType(), group -> new HashMap<>())
+ .computeIfAbsent(path.get(), s -> new HashMap<>());
+
+ getProperties(groupProperty, namingStrategy, path, properties, groupProperties);
+ }
+
+ private static void getProperties(
+ final GroupProperty groupProperty,
+ final NamingStrategy namingStrategy,
+ final Path path,
+ final Map, Map>> properties,
+ final Map groupProperties) {
+
+ for (Property property : groupProperty.getGroupType().getProperties()) {
+ getProperty(property, namingStrategy, path, properties, groupProperties);
+ }
+ }
+
+ private static void getProperty(
+ final Property property,
+ final NamingStrategy namingStrategy,
+ final Path path,
+ final Map, Map>> properties,
+ final Map groupProperties) {
+
+ if (property.isLeaf() || property.isPrimitive()) {
+ groupProperties.put(path.get(property, namingStrategy), property);
+ } else if (property.isGroup()) {
+ GroupProperty groupProperty = property.asGroup();
+ NamingStrategy groupNamingStrategy = groupProperty.hasNamingStrategy() ? groupProperty.getNamingStrategy()
+ : namingStrategy;
+ Path groupPath = path.group(groupProperty, namingStrategy);
+ getProperties(groupProperty, groupNamingStrategy, groupPath, properties);
+ getProperties(groupProperty, groupNamingStrategy, groupPath, properties, groupProperties);
+ } else if (property.isMap()) {
+ ConfigMappingInterface.MapProperty mapProperty = property.asMap();
+ if (mapProperty.getValueProperty().isLeaf()) {
+ groupProperties.put(path.map(property, namingStrategy).get(), property);
+ if (mapProperty.hasKeyUnnamed()) {
+ groupProperties.put(path.unnamedMap(property, namingStrategy).get(), property);
+ }
+ } else if (mapProperty.getValueProperty().isGroup()) {
+ GroupProperty groupProperty = mapProperty.getValueProperty().asGroup();
+ NamingStrategy groupNamingStrategy = groupProperty.hasNamingStrategy() ? groupProperty.getNamingStrategy()
+ : namingStrategy;
+ Path mapPath = path.map(mapProperty, namingStrategy);
+ getProperties(groupProperty, groupNamingStrategy, mapPath, properties);
+ getProperties(groupProperty, groupNamingStrategy, mapPath, properties, groupProperties);
+ if (mapProperty.hasKeyUnnamed()) {
+ Path unnamedMapPath = path.unnamedMap(mapProperty, namingStrategy);
+ getProperties(groupProperty, groupNamingStrategy, unnamedMapPath, properties);
+ getProperties(groupProperty, groupNamingStrategy, unnamedMapPath, properties, groupProperties);
+ }
+ } else if (mapProperty.getValueProperty().isCollection()) {
+ CollectionProperty collectionProperty = mapProperty.getValueProperty().asCollection();
+ Property element = collectionProperty.getElement();
+ if (element.isLeaf()) {
+ groupProperties.put(path.map().collection().get(property, namingStrategy), property);
+ // We use a different leaf to remove the default, to not duplicate defaults properties, because
+ // leaf collections can use the single name or use the name with square brackets
+ groupProperties.put(path.map().get(property, namingStrategy),
+ new LeafProperty(element.getMethod(), element.getPropertyName(), element.asLeaf().getValueType(),
+ element.asLeaf().getConvertWith(), null));
+ if (mapProperty.hasKeyUnnamed()) {
+ groupProperties.put(path.collection().get(property, namingStrategy), property);
+ }
+ } else {
+ getProperty(element, namingStrategy, path.map().collection(), properties, groupProperties);
+ if (mapProperty.hasKeyUnnamed()) {
+ getProperty(element, namingStrategy, path.collection(), properties, groupProperties);
+ }
+ }
+ } else if (mapProperty.getValueProperty().isMap()) {
+ getProperty(mapProperty.getValueProperty(), namingStrategy, path.map(), properties, groupProperties);
+ if (mapProperty.hasKeyUnnamed()) {
+ getProperty(mapProperty.getValueProperty(), namingStrategy, path.unnamedMap(), properties, groupProperties);
+ }
+ }
+ } else if (property.isCollection()) {
+ CollectionProperty collectionProperty = property.asCollection();
+ if (collectionProperty.getElement().isLeaf()) {
+ getProperty(collectionProperty.getElement(), namingStrategy, path, properties, groupProperties);
+ }
+ getProperty(collectionProperty.getElement(), namingStrategy, path.collection(), properties, groupProperties);
+ } else if (property.isOptional()) {
+ getProperty(property.asOptional().getNestedProperty(), namingStrategy, path, properties, groupProperties);
+ }
+ }
+
+ static class Path {
+ private final String path;
+ private final List elements;
+
+ Path() {
+ this("");
+ }
+
+ Path(final String path) {
+ this(path, new ArrayList<>());
+ }
+
+ Path(final String path, final List elements) {
+ this.path = path;
+ this.elements = elements;
+ }
+
+ Path group(final String path) {
+ return new Path(get(path));
+ }
+
+ Path group(final Property property, final NamingStrategy namingStrategy) {
+ if (property.isParentPropertyName()) {
+ return group("");
+ } else {
+ return group(property.getPropertyName(namingStrategy));
+ }
+ }
+
+ Path map() {
+ List elements = new ArrayList<>(this.elements);
+ elements.add(path.isEmpty() && elements.isEmpty() ? "*" : ".*");
+ return new Path(get(), elements);
+ }
+
+ Path map(final String path) {
+ return new Path(get(path));
+ }
+
+ Path map(final Property property, final NamingStrategy namingStrategy) {
+ if (property.isParentPropertyName()) {
+ this.elements.add(path.isEmpty() && this.elements.isEmpty() ? "*" : ".*");
+ return map("");
+ } else {
+ this.elements.add(".*");
+ return map(property.getPropertyName(namingStrategy));
+ }
+ }
+
+ Path collection() {
+ List elements = new ArrayList<>(this.elements);
+ elements.add("[*]");
+ return new Path(get(), elements);
+ }
+
+ Path unnamedMap() {
+ return this;
+ }
+
+ Path unnamedMap(final String path) {
+ return new Path(get(path));
+ }
+
+ Path unnamedMap(final Property property, final NamingStrategy namingStrategy) {
+ return unnamedMap(property.isParentPropertyName() ? "" : property.getPropertyName(namingStrategy));
+ }
+
+ String get(final String path) {
+ String elements = String.join("", this.elements);
+ this.elements.clear();
+ if (this.path.isEmpty()) {
+ if (path.isEmpty()) {
+ return elements;
+ } else {
+ if (!elements.isEmpty() && elements.charAt(0) == '*') {
+ return path + "." + elements;
+ } else {
+ return path + elements;
+ }
+ }
+ } else {
+ return path.isEmpty() ? this.path + elements : this.path + "." + path + elements;
+ }
+ }
+
+ String get(final Property property, final NamingStrategy namingStrategy) {
+ return get(property.isParentPropertyName() ? "" : property.getPropertyName(namingStrategy));
+ }
+
+ String get() {
+ return path;
+ }
+ }
}
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingLoader.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingLoader.java
index 0ecae4d3e..bb9c85023 100644
--- a/implementation/src/main/java/io/smallrye/config/ConfigMappingLoader.java
+++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingLoader.java
@@ -3,9 +3,12 @@
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.microprofile.config.inject.ConfigProperties;
@@ -50,7 +53,7 @@ public static ConfigMappingInterface getConfigMapping(final Class> type) {
static Class> getConfigMappingClass(final Class> type) {
validateAnnotations(type);
- final ConfigMappingClass configMappingClass = ConfigMappingClass.getConfigurationClass(type);
+ ConfigMappingClass configMappingClass = ConfigMappingClass.getConfigurationClass(type);
if (configMappingClass == null) {
return type;
} else {
@@ -58,6 +61,45 @@ static Class> getConfigMappingClass(final Class> type) {
}
}
+ @SuppressWarnings("unchecked")
+ static Map>> configMappingNames(final Class interfaceType) {
+ try {
+ Method getNames = CACHE.get(interfaceType).getImplementationClass().getDeclaredMethod("getNames");
+ return (Map>>) getNames.invoke(null);
+ } catch (NoSuchMethodException e) {
+ throw new NoSuchMethodError(e.getMessage());
+ } catch (IllegalAccessException e) {
+ throw new IllegalAccessError(e.getMessage());
+ } catch (InvocationTargetException e) {
+ try {
+ throw e.getCause();
+ } catch (RuntimeException | Error e2) {
+ throw e2;
+ } catch (Throwable t) {
+ throw new UndeclaredThrowableException(t);
+ }
+ }
+ }
+
+ static Map configMappingDefaults(final Class interfaceType) {
+ try {
+ Method getDefaults = CACHE.get(interfaceType).getImplementationClass().getDeclaredMethod("getDefaults");
+ return (Map) getDefaults.invoke(null);
+ } catch (NoSuchMethodException e) {
+ throw new NoSuchMethodError(e.getMessage());
+ } catch (IllegalAccessException e) {
+ throw new IllegalAccessError(e.getMessage());
+ } catch (InvocationTargetException e) {
+ try {
+ throw e.getCause();
+ } catch (RuntimeException | Error e2) {
+ throw e2;
+ } catch (Throwable t) {
+ throw new UndeclaredThrowableException(t);
+ }
+ }
+ }
+
static T configMappingObject(final Class interfaceType, final ConfigMappingContext configMappingContext) {
ConfigMappingObject instance;
try {
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingNames.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingNames.java
index 01e3e1e65..172bfe971 100644
--- a/implementation/src/main/java/io/smallrye/config/ConfigMappingNames.java
+++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingNames.java
@@ -1,51 +1,117 @@
package io.smallrye.config;
+import static io.smallrye.config.PropertyName.name;
+
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+/**
+ * Represents the full structure of mapping classes per class name, relative path and property name.
+ */
class ConfigMappingNames {
- private final Map names;
+ private final Map>> names;
- public ConfigMappingNames(final Map>> names) {
+ ConfigMappingNames(final Map>> names) {
this.names = new HashMap<>(names.size());
- for (Map.Entry>> entry : names.entrySet()) {
- this.names.put(entry.getKey(), new Names(entry.getValue()));
+
+ for (Map.Entry>> mappings : names.entrySet()) {
+ Map> mappingPropertyNames = new HashMap<>();
+ for (Map.Entry> mappingNames : mappings.getValue().entrySet()) {
+ PropertyName key = name(mappingNames.getKey());
+ mappingPropertyNames.putIfAbsent(key, new HashSet<>());
+ // Give priority to the star key
+ if (key.getName().contains("*")) {
+ mappingPropertyNames.put(key, mappingPropertyNames.remove(key));
+ }
+
+ Set values = mappingPropertyNames.get(key);
+ for (String value : mappingNames.getValue()) {
+ // Give priority to the star key
+ if (value.contains("*")) {
+ values.remove(new PropertyName(value));
+ }
+ values.add(new PropertyName(value));
+ }
+ mappingPropertyNames.get(key).addAll(values);
+ }
+ this.names.put(mappings.getKey(), mappingPropertyNames);
}
}
- Set get(String mapping, String name) {
- return names.get(mapping).get(name);
+ public Map>> getNames() {
+ return names;
}
- private static class Names {
- private final Map> names;
- private final Map> anys;
-
- Names(Map> names) {
- this.names = new HashMap<>();
- this.anys = new HashMap<>();
- for (Map.Entry> entry : names.entrySet()) {
- if (entry.getKey().indexOf('*') == -1) {
- this.names.put(new PropertyName(entry.getKey()), toMappingNameSet(entry.getValue()));
- } else {
- this.anys.put(new PropertyName(entry.getKey()), toMappingNameSet(entry.getValue()));
+ /**
+ * Matches that at least one runtime configuration name is in the root path and relative path of a mapping class.
+ * This is required to trigger the construction of lazy mapping objects like Optional
or
+ * Map
.
+ *
+ * @param mapping the class name of the mapping
+ * @param rootPath the root path of the mapping
+ * @param path the relative path to the mapping
+ * @param names the runtime config names
+ * @return true
if a runtime config name exits in the mapping names or false
otherwise
+ */
+ boolean hasAnyName(final String mapping, final String rootPath, final String path, final Iterable names) {
+ Map> mappings = this.names.get(mapping);
+ if (mappings == null) {
+ return false;
+ }
+
+ // Simple case, no need to remove the rootPath from searched path and names
+ if (rootPath == null || rootPath.isEmpty()) {
+ return hasAnyName(mappings, path, names);
+ }
+
+ // The path does not match the rootPath or the next char is not a separator dot we can skip
+ if (!path.startsWith(rootPath) || (path.length() > rootPath.length() && path.charAt(rootPath.length()) != '.'
+ && path.charAt(rootPath.length()) != '[')) {
+ return false;
+ }
+
+ // Same length replace with empty string since we know they already match, or start after the next separator
+ PropertyName mappingName;
+ if (path.length() == rootPath.length()) {
+ mappingName = new PropertyName("");
+ } else if (path.charAt(rootPath.length()) == '.') {
+ mappingName = new PropertyName(path.substring(rootPath.length() + 1));
+ } else {
+ mappingName = new PropertyName(path.substring(rootPath.length()));
+ }
+ Set mappingNames = mappings.get(mappingName);
+ if (mappingNames == null) {
+ return false;
+ }
+
+ for (String name : names) {
+ // We can't check for next char separator dot because we may find collection names with square brackets
+ if (name.startsWith(path)) {
+ if (mappingNames
+ .contains(new PropertyName(name.charAt(rootPath.length()) == '.' ? name.substring(rootPath.length() + 1)
+ : name.substring(rootPath.length())))) {
+ return true;
}
}
}
- Set get(String name) {
- PropertyName mappingName = new PropertyName(name);
- return names.getOrDefault(mappingName, anys.get(mappingName));
+ return false;
+ }
+
+ boolean hasAnyName(final Map> mappings, final String path,
+ final Iterable names) {
+ Set mappingNames = mappings.get(name(path));
+ if (mappingNames == null || mappingNames.isEmpty()) {
+ return false;
}
- private static Set toMappingNameSet(Set names) {
- Set mappingNames = new HashSet<>(names.size());
- for (String name : names) {
- mappingNames.add(new PropertyName(name));
+ for (String name : names) {
+ if (name.startsWith(path) && mappingNames.contains(name(name))) {
+ return true;
}
- return mappingNames;
}
+ return false;
}
}
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingProvider.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingProvider.java
deleted file mode 100644
index ecaa7fb5e..000000000
--- a/implementation/src/main/java/io/smallrye/config/ConfigMappingProvider.java
+++ /dev/null
@@ -1,381 +0,0 @@
-package io.smallrye.config;
-
-import static io.smallrye.config.ConfigMappingLoader.getConfigMappingClass;
-import static io.smallrye.config.ConfigMappings.getDefaults;
-import static io.smallrye.config.ConfigMappings.getNames;
-import static io.smallrye.config.ConfigMappings.getProperties;
-import static io.smallrye.config.ConfigMappings.ConfigClassWithPrefix.configClassWithPrefix;
-import static io.smallrye.config.ProfileConfigSourceInterceptor.*;
-import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN;
-import static io.smallrye.config.common.utils.StringUtil.replaceNonAlphanumericByUnderscores;
-import static io.smallrye.config.common.utils.StringUtil.toLowerCaseAndDotted;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import org.eclipse.microprofile.config.spi.ConfigSource;
-
-import io.smallrye.common.constraint.Assert;
-import io.smallrye.config.ConfigMappings.ConfigClassWithPrefix;
-
-/**
- *
- */
-final class ConfigMappingProvider implements Serializable {
- private static final long serialVersionUID = 3977667610888849912L;
-
- private final Map>> roots;
- private final Map>> names;
- private final List ignoredPaths;
- private final boolean validateUnknown;
-
- ConfigMappingProvider(final Builder builder) {
- this.roots = new HashMap<>(builder.roots);
- this.names = builder.names;
- this.ignoredPaths = builder.ignoredPaths;
- this.validateUnknown = builder.validateUnknown;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- Map, Map> mapConfiguration(final SmallRyeConfig config)
- throws ConfigValidationException {
- // Register additional dissabiguation property names comparing mapped keys and env names
- matchPropertiesWithEnv(config);
-
- if (roots.isEmpty()) {
- return Collections.emptyMap();
- }
-
- // Perform the config mapping
- ConfigMappingContext context = SecretKeys.doUnlocked(new Supplier() {
- @Override
- public ConfigMappingContext get() {
- return new ConfigMappingContext(config, roots, names);
- }
- });
-
- if (config.getOptionalValue(SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN, boolean.class).orElse(this.validateUnknown)) {
- context.reportUnknown(ignoredPaths);
- }
-
- List problems = context.getProblems();
- if (!problems.isEmpty()) {
- throw new ConfigValidationException(problems.toArray(ConfigValidationException.Problem.NO_PROBLEMS));
- }
-
- return context.getRootsMap();
- }
-
- private void matchPropertiesWithEnv(final SmallRyeConfig config) {
- // TODO - We shouldn't be mutating the EnvSource.
- // We should do the calculation when creating the EnvSource, but right now mappings and sources are not well integrated.
-
- List profiles = config.getProfiles();
- boolean all = roots.containsKey("");
- StringBuilder sb = new StringBuilder();
-
- for (ConfigSource configSource : config.getConfigSources(EnvConfigSource.class)) {
- if (roots.isEmpty()) {
- break;
- }
-
- EnvConfigSource envConfigSource = (EnvConfigSource) configSource;
- Set mutableEnvProperties = envConfigSource.getPropertyNames();
- List envProperties = new ArrayList<>(mutableEnvProperties);
- for (String envProperty : envProperties) {
- String activeEnvProperty;
- if (envProperty.charAt(0) == '%') {
- activeEnvProperty = activeName(envProperty, profiles);
- } else {
- activeEnvProperty = envProperty;
- }
-
- String matchedRoot = null;
- if (!all) {
- for (String root : roots.keySet()) {
- if (isEnvPropertyInRoot(activeEnvProperty, root)) {
- matchedRoot = root;
- break;
- }
- }
- if (matchedRoot == null) {
- continue;
- }
- } else {
- matchedRoot = "";
- }
-
- for (Map> rootNames : names.values()) {
- Set mappedProperties = rootNames.get(matchedRoot);
- if (mappedProperties != null) {
- for (String mappedProperty : mappedProperties) {
- // Try to match Env with Root mapped property and generate the expected format
- List indexOfDashes = indexOfDashes(mappedProperty, activeEnvProperty);
- if (indexOfDashes != null) {
- sb.append(activeEnvProperty);
- for (Integer dash : indexOfDashes) {
- sb.setCharAt(dash, '-');
- }
- String expectedEnvProperty = sb.toString();
- if (!activeEnvProperty.equals(expectedEnvProperty)) {
- envConfigSource.getPropertyNames().add(sb.toString());
- envConfigSource.getPropertyNames().remove(envProperty);
- ignoredPaths.add(activeEnvProperty);
- }
- sb.setLength(0);
- break;
- }
- }
- break;
- }
- }
- }
- }
-
- // Match dotted properties from other sources with Env with the same semantic meaning
- // This needs to happen after matching dashed names from mappings
- List dottedProperties = new ArrayList<>();
- for (ConfigSource configSource : config.getConfigSources()) {
- if (!(configSource instanceof EnvConfigSource)) {
- Set propertyNames = configSource.getPropertyNames();
- if (propertyNames != null) {
- dottedProperties.addAll(propertyNames.stream().map(new Function() {
- @Override
- public String apply(final String name) {
- return activeName(name, profiles);
- }
- }).collect(Collectors.toList()));
- }
- }
- }
-
- for (ConfigSource configSource : config.getConfigSources(EnvConfigSource.class)) {
- EnvConfigSource envConfigSource = (EnvConfigSource) configSource;
- for (String dottedProperty : dottedProperties) {
- Set envNames = envConfigSource.getPropertyNames();
- if (envConfigSource.hasPropertyName(dottedProperty)) {
- if (!envNames.contains(dottedProperty)) {
- // this may be expensive, but it shouldn't happend that often
- envNames.remove(toLowerCaseAndDotted(replaceNonAlphanumericByUnderscores(dottedProperty)));
- envNames.add(dottedProperty);
- }
- }
- }
- }
- }
-
- /**
- * Matches if a dotted Environment property name is part of a registered root.
- *
- * @param envProperty a generated dotted property from the {@link EnvConfigSource}.
- * @param root the root name
- * @return true
if the env property ir part of the root, or false
otherwise.
- */
- private static boolean isEnvPropertyInRoot(final String envProperty, final String root) {
- if (envProperty.equals(root)) {
- return true;
- }
-
- // if property is less than the root no way to match
- if (envProperty.length() <= root.length()) {
- return false;
- }
-
- // foo.bar
- // foo.bar."baz"
- // foo.bar[0]
- char e = envProperty.charAt(root.length());
- if ((e == '.') || e == '[') {
- for (int i = 0; i < root.length(); i++) {
- char r = root.charAt(i);
- e = envProperty.charAt(i);
- if (r == '-') {
- if (e != '.' && e != '-') {
- return false;
- }
- } else if (r != e) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
-
- /**
- * Finds and returns all indexes from a dotted Environment property name, related to its matched mapped
- * property name that must be replaced with a dash. This allows to set single environment variables as
- * FOO_BAR_BAZ
and match them to mappeds properties like foo.*.baz
,
- * foo-bar.baz
or any other combinations find in mappings, without the need of additional metadata.
- *
- * @param mappedProperty the mapping property name.
- * @param envProperty a generated dotted property from the {@link EnvConfigSource}.
- * @return a List of indexes from the env property name to replace with a dash.
- */
- private static List indexOfDashes(final String mappedProperty, final String envProperty) {
- if (mappedProperty.length() > envProperty.length()) {
- return null;
- }
-
- List dashesPosition = null;
- int matchPosition = envProperty.length() - 1;
- for (int i = mappedProperty.length() - 1; i >= 0; i--) {
- if (matchPosition == -1) {
- return null;
- }
-
- char c = mappedProperty.charAt(i);
- if (c == '.' || c == '-') {
- char p = envProperty.charAt(matchPosition);
- if (p != '.' && p != '-') { // a property coming from env can either be . or -
- return null;
- }
- if (c == '-') {
- if (dashesPosition == null) {
- dashesPosition = new ArrayList<>();
- }
- dashesPosition.add(matchPosition);
- }
- matchPosition--;
- } else if (c == '*') { // it's a map - skip to next separator
- char p = envProperty.charAt(matchPosition);
- if (p == '"') {
- matchPosition = envProperty.lastIndexOf('"', matchPosition - 1);
- if (matchPosition != -1) {
- matchPosition = envProperty.lastIndexOf('.', matchPosition);
- }
- }
- matchPosition = envProperty.lastIndexOf('.', matchPosition);
- } else if (c == ']') { // it's a collection - skip to next separator
- i = i - 2;
- matchPosition = envProperty.lastIndexOf('[', matchPosition);
- if (matchPosition != -1) {
- matchPosition--;
- }
- } else if (c != envProperty.charAt(matchPosition)) {
- return null;
- } else {
- matchPosition--;
- }
- }
- return dashesPosition;
- }
-
- static final class Builder {
- Set> types = new HashSet<>();
- Map>> roots = new HashMap<>();
- Set keys = new HashSet<>();
- Map>> names = new HashMap<>();
- List ignoredPaths = new ArrayList<>();
- boolean validateUnknown = true;
- SmallRyeConfigBuilder configBuilder = null;
-
- Builder() {
- }
-
- Builder addRoot(String prefix, Class> type) {
- Assert.checkNotNullParam("path", prefix);
- Assert.checkNotNullParam("type", type);
- types.add(type);
- roots.computeIfAbsent(prefix, k -> new ArrayList<>(4)).add(getConfigMappingClass(type));
- return this;
- }
-
- Builder keys(Set keys) {
- Assert.checkNotNullParam("keys", keys);
- this.keys.addAll(keys);
- return this;
- }
-
- Builder names(Map>> names) {
- Assert.checkNotNullParam("names", names);
- for (Map.Entry>> entry : names.entrySet()) {
- Map> groupNames = this.names.computeIfAbsent(entry.getKey(),
- new Function>>() {
- @Override
- public Map> apply(
- final String s) {
- return new HashMap<>();
- }
- });
- groupNames.putAll(entry.getValue());
- }
- return this;
- }
-
- Builder ignoredPath(String ignoredPath) {
- Assert.checkNotNullParam("ignoredPath", ignoredPath);
- ignoredPaths.add(ignoredPath);
- return this;
- }
-
- Builder validateUnknown(boolean validateUnknown) {
- this.validateUnknown = validateUnknown;
- return this;
- }
-
- Builder registerDefaults(SmallRyeConfigBuilder configBuilder) {
- this.configBuilder = configBuilder;
- return this;
- }
-
- ConfigMappingProvider build() {
- // We don't validate for MP ConfigProperties, so if all classes are MP ConfigProperties disable validation.
- boolean allConfigurationProperties = true;
- for (Class> type : types) {
- if (ConfigMappingClass.getConfigurationClass(type) == null) {
- allConfigurationProperties = false;
- break;
- }
- }
-
- if (allConfigurationProperties) {
- validateUnknown = false;
- }
-
- if (keys.isEmpty()) {
- for (Map.Entry>> entry : roots.entrySet()) {
- for (Class> root : entry.getValue()) {
- ConfigClassWithPrefix configClass = configClassWithPrefix(root, entry.getKey());
- keys(getProperties(configClass).get(configClass.getKlass()).get(configClass.getPrefix()).keySet());
- }
- }
- }
-
- if (names.isEmpty()) {
- for (Map.Entry>> entry : roots.entrySet()) {
- for (Class> root : entry.getValue()) {
- ConfigClassWithPrefix configClass = configClassWithPrefix(root, entry.getKey());
- names(getNames(configClass));
- }
- }
- }
-
- if (configBuilder != null) {
- Map defaultValues = configBuilder.getDefaultValues();
- for (Map.Entry>> entry : roots.entrySet()) {
- for (Class> root : entry.getValue()) {
- for (Map.Entry defaultEntry : getDefaults(configClassWithPrefix(root, entry.getKey()))
- .entrySet()) {
- defaultValues.putIfAbsent(defaultEntry.getKey(), defaultEntry.getValue());
- }
- }
- }
- }
-
- return new ConfigMappingProvider(this);
- }
- }
-}
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappings.java b/implementation/src/main/java/io/smallrye/config/ConfigMappings.java
index d7b77039d..a08d4ac68 100644
--- a/implementation/src/main/java/io/smallrye/config/ConfigMappings.java
+++ b/implementation/src/main/java/io/smallrye/config/ConfigMappings.java
@@ -1,257 +1,113 @@
package io.smallrye.config;
-import static io.smallrye.config.ConfigMappingLoader.getConfigMappingClass;
import static io.smallrye.config.ConfigMappings.ConfigClassWithPrefix.configClassWithPrefix;
-import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN;
-import static java.lang.Boolean.TRUE;
-import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.function.Function;
-import io.smallrye.config.ConfigMapping.NamingStrategy;
-import io.smallrye.config.ConfigMappingInterface.CollectionProperty;
-import io.smallrye.config.ConfigMappingInterface.GroupProperty;
-import io.smallrye.config.ConfigMappingInterface.LeafProperty;
import io.smallrye.config.ConfigMappingInterface.Property;
-public final class ConfigMappings implements Serializable {
- private static final long serialVersionUID = -7790784345796818526L;
-
+/**
+ * Utility class for {@link ConfigMapping} annotated classes.
+ */
+public final class ConfigMappings {
+
+ /**
+ * Registers additional {@link ConfigMapping} annotated classes with a {@link SmallRyeConfig} instance.
+ *
+ * The recommended method of registering {@link ConfigMapping} is with a
+ * {@link SmallRyeConfigBuilder#withMapping(Class, String)}. In certain cases, this is not possible (ex. a CDI
+ * runtime), where mapping classes can only be discovered after the Config
instance creation.
+ *
+ * @param config the {@link SmallRyeConfig} instance
+ * @param configClasses a Set
of {@link ConfigMapping} annotated classes with prefixes
+ * @throws ConfigValidationException if a {@link ConfigMapping} cannot be registed with the
+ * {@link SmallRyeConfig} instance
+ */
public static void registerConfigMappings(final SmallRyeConfig config, final Set configClasses)
throws ConfigValidationException {
if (!configClasses.isEmpty()) {
- Boolean validateUnknown = config.getOptionalValue(SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN, Boolean.class)
- .orElse(TRUE);
- mapConfiguration(config, ConfigMappingProvider.builder().validateUnknown(validateUnknown), configClasses);
+ mapConfiguration(config, new SmallRyeConfigBuilder(), configClasses);
}
}
+ /**
+ * Registers additional ConfigProperties
> annotated classes with a {@link SmallRyeConfig} instance.
+ *
+ * The recommended method of registering ConfigProperties
> is with a
+ * {@link SmallRyeConfigBuilder#withMapping(Class, String)}. In certain cases, this is not possible (ex. a CDI
+ * runtime), where mapping classes can only be discovered after the Config
instance creation.
+ *
+ * @param config the {@link SmallRyeConfig} instance
+ * @param configClasses a Set
of ConfigProperties
> annotated classes with prefixes
+ * @throws ConfigValidationException if a ConfigProperties
> cannot be registed with the
+ * {@link SmallRyeConfig} instance
+ */
public static void registerConfigProperties(final SmallRyeConfig config, final Set configClasses)
throws ConfigValidationException {
if (!configClasses.isEmpty()) {
- mapConfiguration(config, ConfigMappingProvider.builder().validateUnknown(false), configClasses);
+ mapConfiguration(config, new SmallRyeConfigBuilder().withValidateUnknown(false), configClasses);
}
}
- public static Map, Map>> getProperties(final ConfigClassWithPrefix configClass) {
- Map, Map>> properties = new HashMap<>();
- Function path = new Function<>() {
- @Override
- public String apply(final String path) {
- return configClass.getPrefix().isEmpty() && !path.isEmpty() ? path.substring(1)
- : configClass.getPrefix() + path;
- }
- };
- ConfigMappingInterface configMapping = ConfigMappingLoader.getConfigMapping(configClass.getKlass());
- getProperties(new GroupProperty(null, null, configMapping), configMapping.getNamingStrategy(), path, properties);
- return properties;
- }
-
- public static Map>> getNames(final ConfigClassWithPrefix configClass) {
- Map>> names = new HashMap<>();
- Map, Map>> properties = getProperties(configClass);
- for (Map.Entry, Map>> entry : properties.entrySet()) {
- Map> groups = new HashMap<>();
- for (Map.Entry> group : entry.getValue().entrySet()) {
- groups.put(group.getKey(), group.getValue().keySet());
- }
- names.put(entry.getKey().getName(), groups);
+ /**
+ * Constructs a representation of all {@link Property} contained in a mapping class. The Map
key is
+ * the full path to the {@link Property}, including the mapping class prefix.
+ *
+ * @param configClass the {@link ConfigMapping} annotated class and String
prefix
+ * @see ConfigMappingInterface#getProperties(ConfigMappingInterface)
+ * @return a Map
with all mapping class {@link Property}.
+ */
+ public static Map getProperties(final ConfigClassWithPrefix configClass) {
+ Map properties = new HashMap<>();
+ // Because the properties key names do not include the path prefix we need to add it
+ for (Map.Entry entry : ConfigMappingInterface
+ .getProperties(ConfigMappingLoader.getConfigMapping(configClass.getKlass()))
+ .get(configClass.getKlass())
+ .get("").entrySet()) {
+ properties.put(prefix(configClass.getPrefix(), entry.getKey()), entry.getValue());
}
- return names;
- }
-
- public static Set getKeys(final ConfigClassWithPrefix configClass) {
- return getProperties(configClass).get(configClass.getKlass()).get(configClass.getPrefix()).keySet();
- }
-
- public static Map getDefaults(final ConfigClassWithPrefix configClass) {
- Map defaults = new HashMap<>();
- Map, Map>> properties = getProperties(configClass);
- for (Map.Entry entry : properties.get(configClass.getKlass()).get(configClass.getPrefix())
- .entrySet()) {
- if (entry.getValue().hasDefaultValue()) {
- defaults.put(entry.getKey(), entry.getValue().getDefaultValue());
- }
- }
- return defaults;
+ return properties;
}
+ @Deprecated
public static Set mappedProperties(final ConfigClassWithPrefix configClass, final Set properties) {
- Set names = new ConfigMappingNames(getNames(configClass))
- .get(configClass.getKlass().getName(), configClass.getPrefix());
- Set mappedProperties = new HashSet<>();
+ ConfigMappingNames names = new ConfigMappingNames(
+ ConfigMappingLoader.getConfigMapping(configClass.getKlass()).getNames());
+ Set mappedNames = new HashSet<>();
for (String property : properties) {
- if (names.contains(new PropertyName(property))) {
- mappedProperties.add(property);
+ if (names.hasAnyName(configClass.getKlass().getName(), configClass.getPrefix(), configClass.getPrefix(),
+ Set.of(property))) {
+ mappedNames.add(property);
}
}
- return mappedProperties;
+ return mappedNames;
}
private static void mapConfiguration(
final SmallRyeConfig config,
- final ConfigMappingProvider.Builder builder,
+ final SmallRyeConfigBuilder configBuilder,
final Set configClasses)
throws ConfigValidationException {
- DefaultValuesConfigSource defaultValues = (DefaultValuesConfigSource) config.getDefaultValues();
for (ConfigClassWithPrefix configClass : configClasses) {
- builder.addRoot(configClass.getPrefix(), configClass.getKlass());
- defaultValues.addDefaults(
- getDefaults(configClassWithPrefix(getConfigMappingClass(configClass.getKlass()), configClass.getPrefix())));
+ configBuilder.withMapping(configClass.getKlass(), configClass.getPrefix());
}
- config.getMappings().putAll(builder.build().mapConfiguration(config));
+ config.getDefaultValues().addDefaults(configBuilder.getDefaultValues());
+ config.getMappings().putAll(config.buildMappings(configBuilder));
}
- private static void getProperties(
- final GroupProperty groupProperty,
- final NamingStrategy namingStrategy,
- final Function path,
- final Map, Map>> properties) {
-
- ConfigMappingInterface groupType = groupProperty.getGroupType();
- Map groupProperties = properties
- .computeIfAbsent(groupType.getInterfaceType(), group -> new HashMap<>())
- .computeIfAbsent(path.apply(""), s -> new HashMap<>());
-
- getProperties(groupProperty, namingStrategy, path, properties, groupProperties);
- }
-
- private static void getProperties(
- final GroupProperty groupProperty,
- final NamingStrategy namingStrategy,
- final Function path,
- final Map, Map>> properties,
- final Map groupProperties) {
-
- for (Property property : groupProperty.getGroupType().getProperties()) {
- getProperty(property, namingStrategy, path, properties, groupProperties);
- }
- }
-
- private static void getProperty(
- final Property property,
- final NamingStrategy namingStrategy,
- final Function path,
- final Map, Map>> properties,
- final Map groupProperties) {
-
- if (property.isLeaf()) {
- groupProperties.put(
- path.apply(property.isParentPropertyName() ? "" : "." + property.getPropertyName(namingStrategy)),
- property);
- } else if (property.isPrimitive()) {
- groupProperties.put(
- path.apply(property.isParentPropertyName() ? "" : "." + property.getPropertyName(namingStrategy)),
- property);
- } else if (property.isGroup()) {
- GroupProperty groupProperty = property.asGroup();
- NamingStrategy groupNamingStrategy = groupProperty.hasNamingStrategy() ? groupProperty.getNamingStrategy()
- : namingStrategy;
- Function groupPath = new Function<>() {
- @Override
- public String apply(final String name) {
- return property.isParentPropertyName() ? path.apply("") + name
- : path.apply("." + property.getPropertyName(namingStrategy)) + name;
- }
- };
- getProperties(groupProperty, groupNamingStrategy, groupPath, properties);
- getProperties(groupProperty, groupNamingStrategy, groupPath, properties, groupProperties);
- } else if (property.isMap()) {
- ConfigMappingInterface.MapProperty mapProperty = property.asMap();
- if (mapProperty.getValueProperty().isLeaf()) {
- groupProperties.put(property.isParentPropertyName() ? path.apply(".*")
- : path.apply("." + property.getPropertyName(namingStrategy) + ".*"), mapProperty);
- if (mapProperty.hasKeyUnnamed()) {
- groupProperties.put(property.isParentPropertyName() ? path.apply("")
- : path.apply("." + property.getPropertyName(namingStrategy)), mapProperty);
- }
- } else if (mapProperty.getValueProperty().isGroup()) {
- GroupProperty groupProperty = mapProperty.getValueProperty().asGroup();
- NamingStrategy groupNamingStrategy = groupProperty.hasNamingStrategy() ? groupProperty.getNamingStrategy()
- : namingStrategy;
- Function groupPath = new Function<>() {
- @Override
- public String apply(final String name) {
- return property.isParentPropertyName() ? path.apply(".*") + name
- : path.apply("." + mapProperty.getPropertyName(namingStrategy) + ".*") + name;
- }
- };
- getProperties(groupProperty, groupNamingStrategy, groupPath, properties);
- getProperties(groupProperty, groupNamingStrategy, groupPath, properties, groupProperties);
- if (mapProperty.hasKeyUnnamed()) {
- Function unnamedGroupPath = new Function<>() {
- @Override
- public String apply(final String name) {
- return property.isParentPropertyName() ? path.apply(name)
- : path.apply("." + mapProperty.getPropertyName(namingStrategy)) + name;
- }
- };
- getProperties(groupProperty, groupNamingStrategy, unnamedGroupPath, properties);
- getProperties(groupProperty, groupNamingStrategy, unnamedGroupPath, properties, groupProperties);
- }
- } else if (mapProperty.getValueProperty().isCollection()) {
- CollectionProperty collectionProperty = mapProperty.getValueProperty().asCollection();
- Property element = collectionProperty.getElement();
- if (element.isLeaf()) {
- LeafProperty leafProperty = new LeafProperty(element.getMethod(), element.getPropertyName(),
- element.asLeaf().getValueType(), element.asLeaf().getConvertWith(), null);
- getProperty(leafProperty, namingStrategy, new Function() {
- @Override
- public String apply(final String name) {
- return path.apply(name + ".*");
- }
- }, properties, groupProperties);
- }
- getProperty(element, namingStrategy, new Function() {
- @Override
- public String apply(final String name) {
- return path.apply(name + ".*[*]");
- }
- }, properties, groupProperties);
- if (mapProperty.hasKeyUnnamed()) {
- getProperty(element, namingStrategy, new Function() {
- @Override
- public String apply(final String name) {
- return path.apply(name + "[*]");
- }
- }, properties, groupProperties);
- }
- } else if (mapProperty.getValueProperty().isMap()) {
- getProperty(mapProperty.getValueProperty(), namingStrategy,
- new Function() {
- @Override
- public String apply(final String name) {
- return path.apply(name + ".*");
- }
- }, properties, groupProperties);
- if (mapProperty.hasKeyUnnamed()) {
- getProperty(mapProperty.getValueProperty(), namingStrategy,
- new Function() {
- @Override
- public String apply(final String name) {
- return path.apply(name);
- }
- }, properties, groupProperties);
- }
- }
- } else if (property.isCollection()) {
- CollectionProperty collectionProperty = property.asCollection();
- if (collectionProperty.getElement().isLeaf()) {
- getProperty(collectionProperty.getElement(), namingStrategy, path, properties, groupProperties);
- }
- getProperty(collectionProperty.getElement(), namingStrategy, new Function() {
- @Override
- public String apply(final String name) {
- return path.apply(name.endsWith(".*") ? name.substring(0, name.length() - 2) + "[*].*" : name + "[*]");
- }
- }, properties, groupProperties);
- } else if (property.isOptional()) {
- getProperty(property.asOptional().getNestedProperty(), namingStrategy, path, properties, groupProperties);
+ static String prefix(final String prefix, final String path) {
+ if (prefix.isEmpty()) {
+ return path;
+ } else if (path.isEmpty()) {
+ return prefix;
+ } else if (path.charAt(0) == '[') {
+ return prefix + path;
+ } else {
+ return prefix + "." + path;
}
}
@@ -260,6 +116,10 @@ static String getPrefix(Class> type) {
return configMapping != null ? configMapping.prefix() : "";
}
+ /**
+ * A representation of a {@link ConfigMapping} or @ConfigProperties
with a Class
and the
+ * prefix.
+ */
public static final class ConfigClassWithPrefix {
private final Class> klass;
private final String prefix;
diff --git a/implementation/src/main/java/io/smallrye/config/DefaultValuesConfigSource.java b/implementation/src/main/java/io/smallrye/config/DefaultValuesConfigSource.java
index b9c14cdf0..c946105c7 100644
--- a/implementation/src/main/java/io/smallrye/config/DefaultValuesConfigSource.java
+++ b/implementation/src/main/java/io/smallrye/config/DefaultValuesConfigSource.java
@@ -43,7 +43,7 @@ void addDefault(final String name, final String value) {
if (name.indexOf('*') == -1) {
this.properties.putIfAbsent(name, value);
} else {
- this.wildcards.put(new PropertyName(name), value);
+ this.wildcards.putIfAbsent(new PropertyName(name), value);
}
}
}
diff --git a/implementation/src/main/java/io/smallrye/config/PropertyName.java b/implementation/src/main/java/io/smallrye/config/PropertyName.java
index 9067ffce6..f51a3d5df 100644
--- a/implementation/src/main/java/io/smallrye/config/PropertyName.java
+++ b/implementation/src/main/java/io/smallrye/config/PropertyName.java
@@ -54,11 +54,7 @@ static boolean equals(final String name, final String other) {
} else if (o == '"') {
int beginQuote = other.lastIndexOf('"', i - 1);
if (beginQuote != -1) {
- if (beginQuote != 0 && other.charAt(beginQuote - 1) == '.') {
- i = beginQuote;
- } else {
- i = beginQuote;
- }
+ i = beginQuote;
}
} else {
int previousDot = other.lastIndexOf('.', i);
diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
index a71e01d31..52b6774cc 100644
--- a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
+++ b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
@@ -20,6 +20,9 @@
import static io.smallrye.config.Converters.newCollectionConverter;
import static io.smallrye.config.Converters.newMapConverter;
import static io.smallrye.config.Converters.newOptionalConverter;
+import static io.smallrye.config.ProfileConfigSourceInterceptor.activeName;
+import static io.smallrye.config.common.utils.StringUtil.replaceNonAlphanumericByUnderscores;
+import static io.smallrye.config.common.utils.StringUtil.toLowerCaseAndDotted;
import static io.smallrye.config.common.utils.StringUtil.unindexed;
import static io.smallrye.config.common.utils.StringUtil.unquoted;
import static java.util.stream.Collectors.toList;
@@ -40,7 +43,10 @@
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
import java.util.function.IntFunction;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
@@ -76,12 +82,14 @@ public class SmallRyeConfig implements Config, Serializable {
private final Map, Map> mappings;
SmallRyeConfig(SmallRyeConfigBuilder builder) {
- // This needs to be executed before everything else to make sure that defaults from mappings are available to all sources
- ConfigMappingProvider mappingProvider = builder.getMappingsBuilder().build();
this.configSources = new ConfigSources(builder, this);
this.converters = buildConverters(builder);
this.configValidator = builder.getValidator();
- this.mappings = new ConcurrentHashMap<>(mappingProvider.mapConfiguration(this));
+ this.mappings = new ConcurrentHashMap<>(buildMappings(builder));
+
+ // Match dotted properties from other sources with Env with the same semantic meaning
+ // This needs to happen after matching dashed names from mappings
+ matchPropertiesWithEnv();
}
private Map> buildConverters(final SmallRyeConfigBuilder builder) {
@@ -106,6 +114,64 @@ private Map> buildConverters(final SmallRyeConfigBuilder buil
return converters;
}
+ Map, Map> buildMappings(final SmallRyeConfigBuilder builder)
+ throws ConfigValidationException {
+ SmallRyeConfigBuilder.MappingBuilder mappingsBuilder = builder.getMappingsBuilder();
+ if (mappingsBuilder.getMappings().isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ // Perform the config mapping
+ ConfigMappingContext context = SecretKeys.doUnlocked(new Supplier() {
+ @Override
+ public ConfigMappingContext get() {
+ return new ConfigMappingContext(SmallRyeConfig.this, mappingsBuilder.getMappings());
+ }
+ });
+
+ if (getOptionalValue(SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN, boolean.class).orElse(true)) {
+ context.reportUnknown(mappingsBuilder.getIgnoredPaths());
+ }
+
+ List problems = context.getProblems();
+ if (!problems.isEmpty()) {
+ throw new ConfigValidationException(problems.toArray(ConfigValidationException.Problem.NO_PROBLEMS));
+ }
+
+ return context.getRootsMap();
+ }
+
+ private void matchPropertiesWithEnv() {
+ List dottedProperties = new ArrayList<>();
+ for (ConfigSource configSource : getConfigSources()) {
+ if (!(configSource instanceof EnvConfigSource)) {
+ Set propertyNames = configSource.getPropertyNames();
+ if (propertyNames != null) {
+ dottedProperties.addAll(propertyNames.stream().map(new Function() {
+ @Override
+ public String apply(final String name) {
+ return activeName(name, getProfiles());
+ }
+ }).collect(Collectors.toList()));
+ }
+ }
+ }
+
+ for (ConfigSource configSource : getConfigSources(EnvConfigSource.class)) {
+ EnvConfigSource envConfigSource = (EnvConfigSource) configSource;
+ for (String dottedProperty : dottedProperties) {
+ Set envNames = envConfigSource.getPropertyNames();
+ if (envConfigSource.hasPropertyName(dottedProperty)) {
+ if (!envNames.contains(dottedProperty)) {
+ // this may be expensive, but it shouldn't happen that often
+ envNames.remove(toLowerCaseAndDotted(replaceNonAlphanumericByUnderscores(dottedProperty)));
+ envNames.add(dottedProperty);
+ }
+ }
+ }
+ }
+ }
+
@Override
public List getValues(final String name, final Class propertyType) {
return getValues(name, propertyType, ArrayList::new);
@@ -609,6 +675,10 @@ public Optional getConfigSource(final String name) {
return Optional.empty();
}
+ DefaultValuesConfigSource getDefaultValues() {
+ return configSources.defaultValues;
+ }
+
public T convert(String value, Class asType) {
return value != null ? requireConverter(asType).convert(value) : null;
}
@@ -677,10 +747,6 @@ public List getProfiles() {
return configSources.getProfiles();
}
- public ConfigSource getDefaultValues() {
- return configSources.defaultValues;
- }
-
ConfigSourceInterceptorContext interceptorChain() {
return configSources.interceptorChain;
}
@@ -690,7 +756,7 @@ private static class ConfigSources implements Serializable {
private final List profiles;
private final List sources;
- private final ConfigSource defaultValues;
+ private final DefaultValuesConfigSource defaultValues;
private final ConfigSourceInterceptorContext interceptorChain;
private final PropertyNames propertyNames;
diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java b/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java
index df5732f5e..d51b57294 100644
--- a/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java
+++ b/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java
@@ -16,6 +16,8 @@
package io.smallrye.config;
+import static io.smallrye.config.ConfigMappingLoader.getConfigMappingClass;
+import static io.smallrye.config.ConfigMappings.prefix;
import static io.smallrye.config.ConfigSourceInterceptorFactory.DEFAULT_PRIORITY;
import static io.smallrye.config.Converters.STRING_CONVERTER;
import static io.smallrye.config.Converters.newCollectionConverter;
@@ -52,6 +54,7 @@
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
import org.eclipse.microprofile.config.spi.Converter;
+import io.smallrye.common.constraint.Assert;
import io.smallrye.config._private.ConfigMessages;
/**
@@ -71,7 +74,7 @@ public class SmallRyeConfigBuilder implements ConfigBuilder {
private final List secretKeysHandlers = new ArrayList<>();
private ConfigValidator validator = ConfigValidator.EMPTY;
private final Map defaultValues = new HashMap<>();
- private final ConfigMappingProvider.Builder mappingsBuilder = ConfigMappingProvider.builder();
+ private final MappingBuilder mappingsBuilder = new MappingBuilder();
private ClassLoader classLoader = SecuritySupport.getContextClassLoader();
private boolean addDiscoveredCustomizers = false;
private boolean addDefaultSources = false;
@@ -82,10 +85,6 @@ public class SmallRyeConfigBuilder implements ConfigBuilder {
private boolean addDiscoveredSecretKeysHandlers = false;
private boolean addDiscoveredValidator = false;
- public SmallRyeConfigBuilder() {
- withMappingDefaults(true);
- }
-
public SmallRyeConfigBuilder addDiscoveredCustomizers() {
addDiscoveredCustomizers = true;
return this;
@@ -177,10 +176,6 @@ ConfigValidator discoverValidator() {
return ConfigValidator.EMPTY;
}
- ConfigMappingProvider.Builder getMappingsBuilder() {
- return mappingsBuilder;
- }
-
@Override
public SmallRyeConfigBuilder addDefaultSources() {
addDefaultSources = true;
@@ -515,7 +510,7 @@ public SmallRyeConfigBuilder withMapping(Class> klass) {
}
public SmallRyeConfigBuilder withMapping(Class> klass, String prefix) {
- mappingsBuilder.addRoot(prefix, klass);
+ mappingsBuilder.mapping(klass, prefix);
return this;
}
@@ -525,26 +520,10 @@ public SmallRyeConfigBuilder withMappingIgnore(String path) {
}
public SmallRyeConfigBuilder withValidateUnknown(boolean validateUnknown) {
- mappingsBuilder.validateUnknown(validateUnknown);
withDefaultValue(SmallRyeConfig.SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN, Boolean.toString(validateUnknown));
return this;
}
- public SmallRyeConfigBuilder withMappingDefaults(boolean mappingDefaults) {
- mappingsBuilder.registerDefaults(mappingDefaults ? this : null);
- return this;
- }
-
- public SmallRyeConfigBuilder withMappingNames(final Map>> names) {
- mappingsBuilder.names(names);
- return this;
- }
-
- public SmallRyeConfigBuilder withMappingKeys(final Set