diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java index 79ec7cd9e..d4174df69 100644 --- a/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java +++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java @@ -8,6 +8,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -36,7 +37,7 @@ public final class ConfigMappingContext { private final SmallRyeConfig config; private final ConfigMappingNames names; - private final Map, Map> roots = new IdentityHashMap<>(); + private final Map, Map> mappings = new IdentityHashMap<>(); private final Map, Converter> converterInstances = new IdentityHashMap<>(); private NamingStrategy namingStrategy = NamingStrategy.KEBAB_CASE; @@ -46,13 +47,13 @@ public final class ConfigMappingContext { private final Set usedProperties = new HashSet<>(); private final List problems = new ArrayList<>(); - public ConfigMappingContext(final SmallRyeConfig config, final Map, Set> roots) { + public ConfigMappingContext(final SmallRyeConfig config, final Map, Set> mappings) { this(config, new Supplier>>>() { @Override public Map>> get() { // All mapping names must be loaded first because of split mappings Map>> names = new HashMap<>(); - for (Map.Entry, Set> mapping : roots.entrySet()) { + for (Map.Entry, Set> mapping : mappings.entrySet()) { for (Map.Entry>> entry : configMappingNames(mapping.getKey()).entrySet()) { names.putIfAbsent(entry.getKey(), new HashMap<>()); names.get(entry.getKey()).putAll(entry.getValue()); @@ -60,29 +61,46 @@ public Map>> get() { } return names; } - }.get(), roots); + }.get(), mappings); } ConfigMappingContext( final SmallRyeConfig config, final Map>> names, - final Map, Set> roots) { + final Map, Set> mappings) { this.config = config; this.names = new ConfigMappingNames(names); - matchPropertiesWithEnv(roots); - for (Map.Entry, Set> mapping : roots.entrySet()) { - Map mappingObjects = new HashMap<>(); + matchPropertiesWithEnv(mappings); + for (Map.Entry, Set> mapping : mappings.entrySet()) { + Map mappingObjects = new HashMap<>(); + Class mappingType = mapping.getKey(); for (String rootPath : mapping.getValue()) { applyRootPath(rootPath); - mappingObjects.put(rootPath, (ConfigMappingObject) constructRoot(mapping.getKey())); + mappingObjects.put(rootPath, constructRoot(mappingType)); } - this.roots.put(mapping.getKey(), mappingObjects); + this.mappings.put(mappingType, mappingObjects); } } + @SuppressWarnings("unchecked") T constructRoot(Class interfaceType) { - return constructGroup(interfaceType); + int problemsCount = problems.size(); + Object mappingObject = constructGroup(interfaceType); + if (problemsCount != problems.size()) { + return (T) mappingObject; + } + try { + if (mappingObject instanceof ConfigMappingClassMapper) { + mappingObject = ((ConfigMappingClassMapper) mappingObject).map(); + config.getConfigValidator().validateMapping(mappingObject.getClass(), rootPath, mappingObject); + } else { + config.getConfigValidator().validateMapping(interfaceType, rootPath, mappingObject); + } + } catch (ConfigValidationException e) { + problems.addAll(Arrays.asList(e.getProblems())); + } + return (T) mappingObject; } public T constructGroup(Class interfaceType) { @@ -168,8 +186,8 @@ List getProblems() { return problems; } - Map, Map> getRootsMap() { - return roots; + Map, Map> getMappings() { + return mappings; } private void matchPropertiesWithEnv(final Map, Set> roots) { @@ -316,7 +334,7 @@ void reportUnknown(final Set ignoredPaths) { } Set prefixes = new HashSet<>(); - for (Map value : this.roots.values()) { + for (Map value : this.mappings.values()) { prefixes.addAll(value.keySet()); } if (prefixes.contains("")) { diff --git a/implementation/src/main/java/io/smallrye/config/ConfigValidationException.java b/implementation/src/main/java/io/smallrye/config/ConfigValidationException.java index 07c0a94b7..7215c8b45 100644 --- a/implementation/src/main/java/io/smallrye/config/ConfigValidationException.java +++ b/implementation/src/main/java/io/smallrye/config/ConfigValidationException.java @@ -36,6 +36,10 @@ private static String list(Problem[] problems) { return b.toString(); } + Problem[] getProblems() { + return problems; + } + public int getProblemCount() { return problems.length; } diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java index 1a4b2a56a..e936e62c7 100644 --- a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java +++ b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java @@ -80,7 +80,7 @@ public class SmallRyeConfig implements Config, Serializable { private final Map>> optionalConverters = new ConcurrentHashMap<>(); private final ConfigValidator configValidator; - private final Map, Map> mappings; + private final Map, Map> mappings; SmallRyeConfig(SmallRyeConfigBuilder builder) { this.configSources = new ConfigSources(builder, this); @@ -115,7 +115,7 @@ private Map> buildConverters(final SmallRyeConfigBuilder buil return converters; } - Map, Map> buildMappings(final SmallRyeConfigBuilder builder) + Map, Map> buildMappings(final SmallRyeConfigBuilder builder) throws ConfigValidationException { SmallRyeConfigBuilder.MappingBuilder mappingsBuilder = builder.getMappingsBuilder(); if (mappingsBuilder.getMappings().isEmpty()) { @@ -139,7 +139,7 @@ public ConfigMappingContext get() { throw new ConfigValidationException(problems.toArray(ConfigValidationException.Problem.NO_PROBLEMS)); } - return context.getRootsMap(); + return context.getMappings(); } private void matchPropertiesWithEnv() { @@ -605,7 +605,11 @@ public > Optional> getOptionalValues( return Optional.of(getMapIndexedValues(keys, keyConverter, valueConverter, mapFactory, collectionFactory)); } - Map, Map> getMappings() { + ConfigValidator getConfigValidator() { + return configValidator; + } + + Map, Map> getMappings() { return mappings; } @@ -626,24 +630,17 @@ public T getConfigMapping(Class type, String prefix) { return getConfigMapping(type); } - Map mappingsForType = mappings.get(getConfigMappingClass(type)); + Map mappingsForType = mappings.get(getConfigMappingClass(type)); if (mappingsForType == null) { throw ConfigMessages.msg.mappingNotFound(type.getName()); } - ConfigMappingObject configMappingObject = mappingsForType.get(prefix); + Object configMappingObject = mappingsForType.get(prefix); if (configMappingObject == null) { throw ConfigMessages.msg.mappingPrefixNotFound(type.getName(), prefix); } - Object value = configMappingObject; - if (configMappingObject instanceof ConfigMappingClassMapper) { - value = ((ConfigMappingClassMapper) configMappingObject).map(); - } - - configValidator.validateMapping(type, prefix, value); - - return type.cast(value); + return type.cast(configMappingObject); } /** diff --git a/validator/src/test/java/io/smallrye/config/validator/ValidateConfigTest.java b/validator/src/test/java/io/smallrye/config/validator/ValidateConfigTest.java index a9955035b..627f5b196 100644 --- a/validator/src/test/java/io/smallrye/config/validator/ValidateConfigTest.java +++ b/validator/src/test/java/io/smallrye/config/validator/ValidateConfigTest.java @@ -67,7 +67,7 @@ static void restoreMessageLocale() { @Test void validateConfigMapping() { - SmallRyeConfig config = new SmallRyeConfigBuilder() + SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder() .withValidator(new BeanValidationConfigValidatorImpl()) .withSources(config( "server.host", "localhost", @@ -98,11 +98,9 @@ void validateConfigMapping() { "server.info.admins.root[1].username", "admin", "server.info.firewall.accepted[0]", "127.0.0.1", "server.info.firewall.accepted[1]", "8.8.8")) - .withMapping(Server.class) - .build(); + .withMapping(Server.class); - ConfigValidationException validationException = assertThrows(ConfigValidationException.class, - () -> config.getConfigMapping(Server.class, "server")); + ConfigValidationException validationException = assertThrows(ConfigValidationException.class, builder::build); List validations = new ArrayList<>(); for (int i = 0; i < validationException.getProblemCount(); i++) { validations.add(validationException.getProblem(i).getMessage()); @@ -134,16 +132,14 @@ void validateConfigMapping() { @Test void validateNamingStrategy() { - SmallRyeConfig config = new SmallRyeConfigBuilder() + SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder() .withValidator(new BeanValidationConfigValidatorImpl()) .withSources(config( "server.the_host", "localhost", "server.the_port", "8080")) - .withMapping(ServerNamingStrategy.class) - .build(); + .withMapping(ServerNamingStrategy.class); - ConfigValidationException validationException = assertThrows(ConfigValidationException.class, - () -> config.getConfigMapping(ServerNamingStrategy.class, "server")); + ConfigValidationException validationException = assertThrows(ConfigValidationException.class, builder::build); List validations = new ArrayList<>(); for (int i = 0; i < validationException.getProblemCount(); i++) { validations.add(validationException.getProblem(i).getMessage()); @@ -154,13 +150,11 @@ void validateNamingStrategy() { @Test void validateConfigProperties() { - SmallRyeConfig config = new SmallRyeConfigBuilder() + SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder() .withValidator(new BeanValidationConfigValidatorImpl()) - .withMapping(Client.class) - .build(); + .withMapping(Client.class); - ConfigValidationException validationException = assertThrows(ConfigValidationException.class, - () -> config.getConfigMapping(Client.class)); + ConfigValidationException validationException = assertThrows(ConfigValidationException.class, builder::build); assertEquals(1, validationException.getProblemCount()); List validations = new ArrayList<>(); validations.add(validationException.getProblem(0).getMessage()); @@ -169,16 +163,14 @@ void validateConfigProperties() { @Test void validateParent() { - SmallRyeConfig config = new SmallRyeConfigBuilder() + SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder() .withValidator(new BeanValidationConfigValidatorImpl()) .withSources(config( "server.host", "localhost", "server.port", "80")) - .withMapping(ServerParent.class) - .build(); + .withMapping(ServerParent.class); - ConfigValidationException validationException = assertThrows(ConfigValidationException.class, - () -> config.getConfigMapping(ServerParent.class, "server")); + ConfigValidationException validationException = assertThrows(ConfigValidationException.class, builder::build); assertEquals("server.port must be greater than or equal to 8000", validationException.getProblem(0).getMessage()); } @@ -359,14 +351,12 @@ interface Nested { @Test void hierarchy() { - SmallRyeConfig config = new SmallRyeConfigBuilder() + SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder() .withValidator(new BeanValidationConfigValidatorImpl()) .withMapping(Child.class) - .withSources(config("validator.child.number", "1")) - .build(); + .withSources(config("validator.child.number", "1")); - ConfigValidationException validationException = assertThrows(ConfigValidationException.class, - () -> config.getConfigMapping(Child.class)); + ConfigValidationException validationException = assertThrows(ConfigValidationException.class, builder::build); List validations = new ArrayList<>(); for (int i = 0; i < validationException.getProblemCount(); i++) { validations.add(validationException.getProblem(i).getMessage()); @@ -387,13 +377,11 @@ public interface Child extends Parent { @Test void nestedMethodValidation() { - SmallRyeConfig config = new SmallRyeConfigBuilder() + SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder() .withValidator(new BeanValidationConfigValidatorImpl()) - .withMapping(MethodValidation.class) - .build(); + .withMapping(MethodValidation.class); - ConfigValidationException validationException = assertThrows(ConfigValidationException.class, - () -> config.getConfigMapping(MethodValidation.class)); + ConfigValidationException validationException = assertThrows(ConfigValidationException.class, builder::build); List validations = new ArrayList<>(); for (int i = 0; i < validationException.getProblemCount(); i++) { validations.add(validationException.getProblem(i).getMessage());